使用NetworkStream.WriteAsync检测错误 [英] Detect errors with NetworkStream.WriteAsync

查看:81
本文介绍了使用NetworkStream.WriteAsync检测错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果在完成对 Login 的调用后杀死服务器,则对 stream.WriteAsync(data,0,data.Count()); <的调用不会引发异常./code>已完成,返回的Task中没有错误提示.

If I kill my server after the call to Login is complete, no exception is thrown when the call to stream.WriteAsync(data, 0, data.Count()); is made and there is no indication of error in the returned Task.

那我应该如何检测错误?当然,应该有一些迹象表明我试图通过已挂断的连接发送数据.

How then, am I supposed to detect errors? Surely, there should be some indication that I tried to send data over a connection that had been hung up on.

这是我最新的代码尝试:

Here is my latest code attempt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

using log4net;
using System.IO;
using System.Threading;

namespace IntegrationTests
{
    public class Client
    {
        private static readonly ILog log = LogManager.GetLogger("root");

        static private ulong m_lastId = 1;

        private ulong m_id;
        private string m_host;
        private uint m_port;
        private uint m_timeoutMilliseconds;
        private string m_clientId;
        private TcpClient m_tcpClient;
        private CancellationTokenSource m_cancelationSource;

        public Client(string host, uint port, string clientId, uint timeoutMilliseconds)
        {
            m_id = m_lastId++;
            m_host = host;
            m_port = port;
            m_clientId = clientId;
            m_timeoutMilliseconds = timeoutMilliseconds;
            m_tcpClient = null;
            m_cancelationSource = null;
        }

        ~Client()
        {
            Disconnect();
        }

        /// <summary>
        /// Attempts to connect to the hostname and port specified in the constructor
        /// </summary>
        /// <throws cref="System.ApplicationException" on failure
        public void Connect()
        {
            Disconnect();

            m_tcpClient = new TcpClient();
            m_cancelationSource = new CancellationTokenSource();

            try
            {
                m_tcpClient.Connect(m_host, (int)m_port);
            }
            catch (SocketException e)
            {
                string msg = string.Format("Client #{0} failed to connect to {1} on port {2}"
                                          , m_id, m_host, m_port);
                throw new System.ApplicationException(msg, e);
            }

            if (m_tcpClient.Connected)
            {
                log.Debug(string.Format("Client #{0} connnected to the Component on {1}"
                                      , m_id, m_tcpClient.Client.RemoteEndPoint.ToString()));
            }
        }

        public void Disconnect()
        {
            if (m_cancelationSource != null)
            {
                m_cancelationSource.Cancel();

                // TODO - There needs to be some kind of wait here until the async methods all return!
                //        How to do that?
                //        Are we even supposed to be manually canceling? One would think TcpClient.Close takes care of that,
                //        however when deleting all cancelation stuff, instead we get exceptions from the async methods about
                //        using TcpClient's members after it was disposed.

                m_cancelationSource.Dispose();
                m_cancelationSource = null;
            }

            if (m_tcpClient != null)
            {
                m_tcpClient.Close();
                m_tcpClient = null;
            }
        }

        public void Login()
        {
            string loginRequest = string.Format("loginstuff{0}", m_clientId);
            var data = Encoding.ASCII.GetBytes(loginRequest);

            NetworkStream stream = m_tcpClient.GetStream();
            Task writeTask = stream.WriteAsync(data, 0, data.Count());

            // This will block until the login is sent
            // We want block until the login is sent, so we can be sure we logged in before making requests
            if( !writeTask.Wait((int)m_timeoutMilliseconds) )
            {
                // Error - Send timed out
                log.Error(string.Format("Client #{0} Timed out while sending login request to the Component"
                                      , m_id));
            }
            else
            {
                log.Debug(string.Format("Client #{0} sent login request to the Component"
                                       , m_id));
            }
        }

        public async void Read()
        {
            byte[] buffer = new byte[1024];
            MemoryStream memoryStream = new MemoryStream();

            NetworkStream networkStream = m_tcpClient.GetStream();
            Task<int> readTask = null;

            bool disconnected = false;

            try
            {
                while (!disconnected)
                {
                    readTask = networkStream.ReadAsync(buffer, 0, buffer.Length, m_cancelationSource.Token);
                    int bytesReceived = await readTask;

                    if (readTask.Status == TaskStatus.RanToCompletion)
                    {
                        if( bytesReceived <= 0)
                        {
                            disconnected = true;
                            continue;
                        }

                        memoryStream.Write(buffer, 0, bytesReceived);

                        // TODO - Handle parsing of messages in the memory stream

                        memoryStream.Seek(0, SeekOrigin.Begin);
                    }
                    else if (readTask.Status == TaskStatus.Canceled)
                    {
                        // Error - Read was cancelled
                        log.Error(string.Format("Client #{0} Read operation was canceled."
                                              , m_id));
                        disconnected = true;
                        continue;
                    }
                    else
                    {
                        // Error - Unexpected status
                        log.Error(string.Format("Client #{0} Read operation has unexpected status after returning from await."
                                              , m_id));
                    }
                }
            }
            catch (System.Exception e)
            {
                log.Error(string.Format("Client #{0} Exception caught while reading from socket. Exception: {1}"
                                       , m_id, e.ToString()));
            }
        }

        public async void MakeRequest(string thingy)
        {
            string message = string.Format("requeststuff{0}", thingy);
            var data = Encoding.ASCII.GetBytes(message);

            NetworkStream networkStream = m_tcpClient.GetStream();
            Task writeTask = null;

            try
            {
                writeTask = networkStream.WriteAsync(data, 0, data.Count(), m_cancelationSource.Token);
                await writeTask;

                if (writeTask.Status == TaskStatus.RanToCompletion)
                {
                    log.Debug(string.Format("Client #{0} sent request for thingy {1} to the Component"
                                           , m_id, thingy));
                }
                else if (writeTask.Status == TaskStatus.Canceled)
                {
                    // Error - Write was cancelled
                    log.Error(string.Format("Client #{0} Write operation was canceled while requesting thingy {1} from the Component"
                                          , m_id, thingy));
                }
                else
                {
                    // Error - Unexpected status
                    log.Error(string.Format("Client #{0} Write operation has unexpected status after returning from await, while requesting thingy {1} from the Component"
                                          , m_id, thingy));
                }
            }
            catch (System.Exception e)
            {
                log.Error(string.Format("Client #{0} Exception caught while requesting thingy {1}. Exception: {2}" 
                                       , m_id, thingy, e.ToString()));
            }
        }
    }
}

主要:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using log4net;
using log4net.Config;

namespace IntegrationTests
{
    class Program
    {
        private static readonly ILog log = LogManager.GetLogger("root");

        static void Main(string[] args)
        {
            try
            {
                XmlConfigurator.Configure();
                log.Info("Starting Component Integration Tests...");

                Client client = new Client("127.0.0.1", 24001, "MyClientId", 60000);
                client.Connect();

                client.Read();
                client.Login();
                client.MakeRequest("Stuff");

                System.Threading.Thread.Sleep(60000);

                client.Disconnect();
            }
            catch (Exception e)
            {
                log.Error(string.Format("Caught an exception in main. Exception: {0}"
                                      , e.ToString()));
            }
        }
    }
}

推荐答案

此行为是TCP堆栈设计的.请参阅这篇文章以获得解释(xamarin但适用相同的原理这里).如果您同时控制客户端和服务器,则可以创建一些轮询机制.

This behavior is by design of the TCP stack. See this post for explanation (xamarin but the same principles apply here). If you have control over both client and server you could create some polling mechanism.

这篇关于使用NetworkStream.WriteAsync检测错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆