本文介绍了如何“冲洗" TCP客户端缓冲区?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从几个示例中提取了设置连接到服务器的全双工C#TCP客户端的信息.基本概念是客户端和服务器都发送/接收消息(命令和事件).因此,我开发了FullDuplexSocket类,该类公开了一个Send方法将消息发送到服务器,以及一个事件处理程序以从服务器接收消息.一切正常,除了我似乎无法刷新从服务器接收到的消息缓冲区.每次服务器发送新消息时,我套接字中的缓冲区将包含所有旧消息(已读取)和新消息.我可以用已知的定界符(/r/n)分割消息并跟踪它们,但这可能是长时间运行通信时出现内存问题的根源. .

I've pulled from several examples to setup a Full Duplex C# TCP Client connected to a server. The basic concept is that the client and server both send/receive messages (commands and events). Therefore, I've developed a FullDuplexSocket class that exposes a Send method to send messages to the server and a event handler to receive messages from the server. Everything works except I can't seem to flush the buffer of messages received from the server. Each time the server sends a new message, the buffer in my socket contains all the old messages (already read) and the new message(s). I could split the messages by a known delimiter (/r/n) and keep track of them, but this will likely be the source of memory issues on long running communications. .

有人有任何建议吗?总重写???下面的代码希望对其他人有帮助.

Does anyone have any suggestions. Total rewrite??? Code is below in hope that it will help others.

这是FullDuplexSocket类:

Here's the FullDuplexSocket class:

using System;
using System.Text;
using System.Net.Sockets;

namespace Utilities
{
    public class StateObject{
        public Socket workSocket = null;
        public const int BUFFER_SIZE = 1024;
        public byte[] buffer = new byte[BUFFER_SIZE];
    }

    public class FullDuplexSocket : IDisposable
    {
        public event NewMessageHandler OnMessageReceived;
        public delegate void NewMessageHandler(string Message);
        public event DisconnectHandler OnDisconnect;
        public delegate void DisconnectHandler(string Reason);

        private Socket _socket;
        private bool _useASCII = true;
        private string _remoteServerIp = "";
        private int _port = 0;

        /// <summary>
        /// Constructer of a full duplex client socket.   The consumer should immedately define
        /// and event handler for the OnMessageReceived event after construction has completed.
        /// </summary>
        /// <param name="RemoteServerIp">The remote Ip address of the server.</param>
        /// <param name="Port">The port that will used to transfer/receive messages to/from the remote IP.</param>
        /// <param name="UseASCII">The character type to encode/decode messages.  Defaulted to use ASCII, but setting the value to false will encode/decode messages in UNICODE.</param>
        public FullDuplexSocket(string RemoteServerIp, int Port, bool UseASCII = true)
        {
            _useASCII = UseASCII;
            _remoteServerIp = RemoteServerIp;
            _port = Port;

            try //to create the socket and connect
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Connect(RemoteServerIp, _port);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to connect to the remote Ip.", e);
            }

            try //to listen to the socket
            {
                StateObject stateObject = new StateObject();
                stateObject.workSocket = _socket;

                _socket.BeginReceive
                    (
                        stateObject.buffer, //Buffer to load in our state object
                        0, //Start at the first position in the byte array
                        StateObject.BUFFER_SIZE, //only load up to the max per read
                        0, //Set socket flags here if necessary
                        new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                        stateObject //state object to use when data arrives
                    );
            }
            catch (Exception e)
            {
                throw new Exception("Unable to start listening to the socket.", e);
            }
        }


        /// <summary>
        /// This will read the bytes from the socket, convert the bytes to a string and fire the OnMessageReceived event.
        /// If the socket is forcibly closed, the OnDisconnect event will be fired.   This happens when the other side of
        /// the socket connection on the remote Ip is no longer available.
        /// </summary>
        /// <param name="asyncResult"></param>
        public void ReadFromSocket(IAsyncResult asyncResult)
        {
            StateObject stateObject = (StateObject)asyncResult.AsyncState; //pull out the state object
            int bytesReceived = 0;

            try //to receive the message.
            {
                bytesReceived = stateObject.workSocket.EndReceive(asyncResult);
            }
            catch (Exception e)  //Exception will occur if connection was forcibly closed.
            {
                RaiseOnDisconnect(e.Message);
                return;
            }

            if (bytesReceived > 0)
            {
                RaiseOnMessageReceived
                    (
                        _useASCII ?
                            Encoding.ASCII.GetString(stateObject.buffer, 0, bytesReceived) :
                            Encoding.Unicode.GetString(stateObject.buffer, 0, bytesReceived)
                    );

                stateObject.workSocket.BeginReceive
                    (
                        stateObject.buffer, //Buffer to load in our state object
                        0, //Start at the first position in the byte array
                        StateObject.BUFFER_SIZE, //only load up to the max per read
                        0, //Set socket flags here if necessary
                        new AsyncCallback(ReadFromSocket), //Who to call when data arrives
                        stateObject //state object to use when data arrives
                    );

            }
            else
            {
                stateObject.workSocket.Close();
                RaiseOnDisconnect("Socket closed normally.");
                return;
            }
        }
        /// <summary>
        /// Broadcast a message to the IP/Port.  Consumer should handle any exceptions thrown by the socket.
        /// </summary>
        /// <param name="Message">The message to be sent will be encoded using the character set defined during construction.</param>
        public void Send(string Message)
        {
            //all messages are terminated with /r/n
            Message += Environment.NewLine;

            byte[] bytesToSend = _useASCII ?
                Encoding.ASCII.GetBytes(Message) :
                Encoding.Unicode.GetBytes(Message);

            int bytesSent = _socket.Send(bytesToSend);

        }

        /// <summary>
        /// Clean up the socket.
        /// </summary>
        void IDisposable.Dispose()
        {
            try
            {
                _socket.Close();
                RaiseOnDisconnect("Socket closed via Dispose method.");
            }
            catch { }
            try
            {
                _socket.Dispose();
            }
            catch { }
        }


        /// <summary>
        /// This method will gracefully raise any delegated events if they exist.
        /// </summary>
        /// <param name="Message"></param>
        private void RaiseOnMessageReceived(string Message)
        {
            try //to raise delegates
            {
                OnMessageReceived(Message);
            }
            catch { } //when none exist ignore the Object Reference Error
        }

        /// <summary>
        /// This method will gracefully raise any delegated events if they exist.
        /// </summary>
        /// <param name="Message"></param>
        private void RaiseOnDisconnect(string Message)
        {
            try //to raise delegates
            {
                OnDisconnect(Message);
            }
            catch { } //when none exist ignore the Object Reference Error
        }

    }
}

该类的使用者只需执行以下操作即可:

The consumer of the class would simply do the following:

using System;

namespace Utilities
{
    public class SocketConsumer
    {
        private FullDuplexSocket _fds;

        public Consumer()
        {
            _fds = new FullDuplexSocket("192.168.1.103", 4555);

            _fds.OnMessageReceived += fds_OnMessageReceived;

            _fds.Send("Hello World!");
        }

        void fds_OnMessageReceived(string Message)
        {
            Console.WriteLine("Message: {0}", Message);
        }
    }
}

任何帮助都会很棒.谢谢!

Any help would be great. Thanks!

推荐答案

即使缓冲区未满(在bytesRead < count情况下),您也在调用OnMessageReceived.考虑为应用程序的异步部分切换到await.这摆脱了可怕的回调递归.

You're calling OnMessageReceived even if the buffer has not been filled (in case bytesRead < count). Consider switching to await for the async part of the application. That gets rid of the hideous callback recursion.

这篇关于如何“冲洗" TCP客户端缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 03:07