.NET question about asynchronous socket operations and message framing

前端 未结 3 1119
长发绾君心
长发绾君心 2021-01-12 00:13

I\'ve been looking everywhere for examples on how to deal with TCP message framing. I see many examples where NetworkStreams are passed into a StreamReader or StreamWriter o

3条回答
  •  离开以前
    2021-01-12 00:45

    OK here's what I ended up doing. I created a reader thread that creates a NetworkStream and a StreamReader based on the network stream. Then I use StreamReader.ReadLine to read in the lines that way. It's a synchronous call but it is in its own thread. It seems to work a lot better. I had to implement this since that's our protocol for the application (newline-delimited messages). I know other people will be looking around like hell for the answer like I did here's the relevant read code in my Client class:

    public class Client
    {
        Socket              m_Socket;
    
        EventWaitHandle     m_WaitHandle;
        readonly object     m_Locker;
        Queue       m_Tasks;
        Thread              m_Thread;
    
        Thread              m_ReadThread;
    
        public Client()
        {
            m_WaitHandle = new AutoResetEvent(false);
            m_Locker = new object();
            m_Tasks = new Queue();
    
            m_Thread = new Thread(Run);
            m_Thread.IsBackground = true;
            m_Thread.Start();
        }
    
        public void EnqueueTask(IEvent task)
        {
            lock (m_Locker)
            {
                m_Tasks.Enqueue(task);
            }
    
            m_WaitHandle.Set();
        }
    
        private void Run()
        {
            while (true)
            {
                IEvent task = null;
    
                lock (m_Locker)
                {
                    if (m_Tasks.Count > 0)
                    {
                        task = m_Tasks.Dequeue();
    
                        if (task == null)
                        {
                            return;
                        }
                    }
                }
    
                if (task != null)
                {
                    task.DoTask(this);
                }
                else
                {
                    m_WaitHandle.WaitOne();
                }
            }
        }
    
        public void Connect(string hostname, int port)
        {
            try
            {
                m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                IPAddress[] IPs = Dns.GetHostAddresses(hostname);
    
                m_Socket.BeginConnect(IPs, port, new AsyncCallback(ConnectCallback), m_Socket);
            }
            catch (SocketException)
            {
                m_Socket.Close();
                OnConnect(false, "Unable to connect to server.");
            }
        }
    
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket socket = (Socket)ar.AsyncState;
    
                socket.EndConnect(ar);
    
                OnConnect(true, "Successfully connected to server.");
    
                m_ReadThread = new Thread(new ThreadStart(this.ReadThread));
                m_ReadThread.Name = "Read Thread";
                m_ReadThread.IsBackground = true;
                m_ReadThread.Start();
            }
            catch (SocketException)
            {
                m_Socket.Close();
                OnConnect(false, "Unable to connect to server.");
            }
        }
    
        void ReadThread()
        {
            NetworkStream networkStream = new NetworkStream(m_Socket);
            StreamReader reader = new StreamReader(networkStream);
    
            while (true)
            {
                try
                {
                    String message = reader.ReadLine();
    
                    // To keep the code thread-safe, enqueue a task in the CLient class thread to parse the message received.
                    EnqueueTask(new ServerMessageEvent(message));
                }
                catch (IOException)
                {
                    // The code will reach here if the server disconnects from the client. Make sure to cleanly shutdown...
                    Disconnect();
                    break;
                }
            }
        }
    
        ... Code for sending/parsing the message in the Client class thread.
    }
    

提交回复
热议问题