代码之家  ›  专栏  ›  技术社区  ›  Christopher Pisz

使用NetworkStream检测错误。WriteAsync

  •  3
  • Christopher Pisz  · 技术社区  · 7 年前

    如果我在调用 Login 已完成,当调用 stream.WriteAsync(data, 0, data.Count());

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

    以下是我最新的代码尝试:

    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()));
                }
            }
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   HeWillem    6 年前

    这种行为是通过TCP堆栈的设计实现的。看见 this post 解释(xamarin,但此处适用相同的原则)。 如果您可以同时控制客户端和服务器,则可以创建一些轮询机制。