C# Socket BeginReceive方法中参数byte[] buffer 的理解

TcpClient tcpClient;  

byte[] byteBuffer= new byte[1024*4];

tcpClient.Client.BeginReceive(byteBuffer, 0, byteBuffer.Length, SocketFlags.None, ReceiveCallBack, null);

    // 参数:
        //   buffer:
        //     类型的数组 System.Byte ,它是接收到的数据的存储位置。

如上代码片段所示:当发送端以10ms的间隔频率密集发送数据时,接收方利用Thread.Sleep(10000) 刻意等待10s 后观察测试结果:

发送方发了382次,此时接收方数据先写入socket系统缓冲区累加了,但我们程序读取延时,所以只用了6次就全部读取完了。

如果把上面伪代码的 byteBuffer 设置为发送数据长度(测试发送的数据长度为48:)的一半:即 byte[] byteBuffer=new byte[24];

再次测试发现接收次数为发送次数的两倍,这个好理解:接收容量小了一半,次数就要增加一倍,此时和是否有Sleep无关。Sleep等待只是使得接收数据变慢了。

由上可知:这个buffer参数 太小,则接收太慢,如果参数过大,则可以最大化利用网络I/O性能,但相应速度可能下降,通常要保持一个合理的值,如1024*2 或1024*4 等;

附加一个Socket异步接收模型:参考了这个Blog 的封装,但实际测试有些bug,做了些改进:

比如连接时,不小心触发了多次连接就可能报异常:

System.InvalidOperationException:“同一个套接字上正在进行另一个异步操作时,不能调用 BeginConnect

另外测试发现一个比较严重的bug,如果客户端Socket异步发送频繁:比如:

  for(int i=0;i<(int)this.numericUpDown1.Value;i++)
            {
                PipeReader l100 = new PipeReader { PipeCode ="#"+ (i + 1).ToString("0000"), ProductType= ProductType.S45, IsNormalCode=true };
                client.SendAsync(l100.ToBytes());
            }        

此Socket异步模型会导致数据漏收,比如发了50个,实际只收到了2,3个,问题发生的原因在线程同步锁ManualResetEvent的使用导致,去掉这个锁的逻辑,可以接受完整;或者在客户端发送消息时加上延时,则也可接收完整的回调;

//友情提示:下面的封装异步Tcp客户端,可以拿来做测试用,如果实际项目利用需要谨慎使用
public class AsynSocketClient
    {
        #region private
        /// <summary>
        /// 用于控制异步接收消息
        /// </summary>
        // private ManualResetEvent doReceive = new ManualResetEvent(false);
        private TcpClient tcpClient;

        //标识客户端是否接收数据
        private bool isStopWork = false;
        #endregion

        /// <summary>
        /// 是否释放对象了
        /// </summary>
        public bool IsClosed { get => isStopWork; set => isStopWork = value; }

        #region 事件
        /// <summary>
        /// 客户端连接完成、发送完成、连接异常或者服务端关闭触发的事件
        /// </summary>
        public event Action<TcpClient, SocketAction> Completed;
        
        /// <summary>
        /// 客户端接收消息触发的事件
        /// </summary>
        public event EventHandler<DataEventArgs> Received;

        #endregion
        
        public AsynSocketClient()
        {
            tcpClient = new TcpClient();    
          
        }

        #region 连接
        /// <summary>
        /// 异步连接
        /// </summary>
        /// <param name="ip">要连接的服务器的ip地址</param>
        /// <param name="port">要连接的服务器的端口</param>
        public void ConnectAsync(string ip, int port)
        {
            IPAddress ipAddress = null;
            try
            {
                ipAddress = IPAddress.Parse(ip);
            }
            catch (Exception)
            {
                throw new Exception("ip地址格式不正确,请使用正确的ip地址!");
            }
            try
            {
                if (!tcpClient.Connected)
                {
                    tcpClient.BeginConnect(ipAddress, port, ConnectCallBack, tcpClient);
                }
                else if (isStopWork)
                {
                    isStopWork = false;
                    OnComplete(tcpClient, SocketAction.Connect);
                }
            }
            catch
            {
            }            
        }

        /// <summary>
        /// 异步连接的回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectCallBack(IAsyncResult ar)
        {
            try
            {
                TcpClient client = ar.AsyncState as TcpClient;
                client.EndConnect(ar);
                OnComplete(client, SocketAction.Connect);
            }
            catch
            { }           
        }
        #endregion

        #region 收发数据
        /// <summary>
        /// 异步接收消息
        /// </summary>
        private void ReceiveAsync()
        {
           // doReceive.Reset();
            StateObject obj = new StateObject();
            obj.TcpClient = tcpClient;

            tcpClient.Client.BeginReceive(obj.ListData, 0, obj.ListData.Length, SocketFlags.None, ReceiveCallBack, obj);
           // doReceive.WaitOne();
        }
        /// <summary>
        /// 异步发送消息
        /// </summary>
        /// <param name="msg"></param>
        public void SendAsync(string msg)
        {
            byte[] listData = Encoding.UTF8.GetBytes(msg);
            SendAsync(listData);
        }

        public void SendAsync(byte[] bytes)
        {
            try
            {
                if (tcpClient.Client == null)
                {
                    throw new Exception("连接已经断开");
                }
                if (isStopWork)
                {
                    return;
                }
                tcpClient.Client.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallBack, tcpClient);
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode == (int)SocketError.ConnectionAborted)
                {
                    throw new Exception("连接已经断开");
                }
            }
          
        }


        private void ReceiveCallBack(IAsyncResult ar)
        {
            StateObject state = ar.AsyncState as StateObject;
            int count = -1;
            try
            {
                if (isStopWork)
                {
                    return;
                }
                count = state.TcpClient.Client.EndReceive(ar);
                //doReceive.Set();
            }
            catch (Exception ex)
            {
                //如果发生异常,说明客户端失去连接,触发关闭事件
                Stop();
                OnComplete(state.TcpClient, SocketAction.Close);
            }
            if (count > 0)
            {
                Received?.Invoke(this, new DataEventArgs() { Data = state.ListData.Take<byte>(count).ToArray() });
            }
        }

        private void SendCallBack(IAsyncResult ar)
        {
            TcpClient client = ar.AsyncState as TcpClient;
            try
            {
                client.Client.EndSend(ar);
                OnComplete(client, SocketAction.SendMsg);
            }
            catch (Exception)
            {
                //如果发生异常,说明客户端失去连接,触发关闭事件
                Stop();
                OnComplete(client, SocketAction.Close);
            }
        }
        #endregion

        public virtual void OnComplete(TcpClient client, SocketAction action)
        {
            if (Completed != null)
                Completed(client, action);
            if (action == SocketAction.Connect)
            {
                // 接收数据
                ThreadPool.QueueUserWorkItem(x =>
                {
                    while (!isStopWork)
                    {
                        try
                        {
                            ReceiveAsync();
                            Thread.Sleep(20);
                        }
                        catch (Exception)
                        {
                            Stop();
                            OnComplete(client, SocketAction.Close);
                        }
                    }
                });
            }
            else if (action == SocketAction.Close)
            {
                try
                {
                    Thread.Sleep(50);
                    this.Received = null;
                    tcpClient.Close();
                }
                catch
                {
                }
            }
        }

        void Stop()
        {
            isStopWork = true;
        }

        public void Close()
        {
            if (tcpClient != null)
            {
                try
                {
                    Stop();
                    IsClosed = true;
                    Completed = null;
                    Received = null;
                    tcpClient.GetStream().Close();
                 
                }
                catch(Exception ex)
                {

                }
            }
        }

    }

    /// <summary>
    /// 接收socket的行为
    /// </summary>
    public enum SocketAction
    {
        /// <summary>
        /// socket发生连接
        /// </summary>
        Connect = 1,
        /// <summary>
        /// socket发送数据
        /// </summary>
        SendMsg = 2,
        /// <summary>
        /// socket关闭
        /// </summary>
        Close = 4
    }
    
    public class StateObject
    {
        public TcpClient TcpClient { get; set; }
        private byte[] listData = new byte[1024*4];
        /// <summary>
        /// 接收的数据
        /// </summary>
        public byte[] ListData
        {
            get
            {
                return listData;
            }
            set
            {
                listData = value;
            }
        }
    }

    public class DataEventArgs : EventArgs
    {
        public byte[] Data
        {
            get;
            set;
        }

        public int Offset
        {
            get;
            set;
        }

        public int Length
        {
            get;
            set;
        }
    }

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
C# Socket 类提供了底层的网络编程接口,但使用起来相对较为繁琐,因此可以进行封装,以便更方便地使用。以下是一个简单的 C# Socket 封装示例: ```csharp using System; using System.Net; using System.Net.Sockets; using System.Text; public class SocketClient { private Socket _socket; private byte[] _buffer = new byte[1024]; public SocketClient(string ipAddress, int port) { IPAddress ip = IPAddress.Parse(ipAddress); IPEndPoint remoteEP = new IPEndPoint(ip, port); _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), _socket); } private void ConnectCallback(IAsyncResult ar) { Socket socket = (Socket)ar.AsyncState; socket.EndConnect(ar); } public void Send(string data) { byte[] byteData = Encoding.ASCII.GetBytes(data); _socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), _socket); } private void SendCallback(IAsyncResult ar) { Socket socket = (Socket)ar.AsyncState; int bytesSent = socket.EndSend(ar); Console.WriteLine("Sent {0} bytes to server.", bytesSent); } public void Receive() { _socket.BeginReceive(_buffer, 0, _buffer.Length, 0, new AsyncCallback(ReceiveCallback), _socket); } private void ReceiveCallback(IAsyncResult ar) { Socket socket = (Socket)ar.AsyncState; int bytesRead = socket.EndReceive(ar); if (bytesRead > 0) { string data = Encoding.ASCII.GetString(_buffer, 0, bytesRead); Console.WriteLine("Received: {0}", data); } } public void Close() { _socket.Shutdown(SocketShutdown.Both); _socket.Close(); } } ``` 这个示例,我们创建了一个名为 `SocketClient` 的类,该类实现了一个基本的 TCP 客户端。在构造函数,我们使用指定的 IP 地址和端口号初始化了一个 `IPEndPoint` 对象,并创建了一个 `Socket` 对象。在连接服务器时,我们使用 `BeginConnect` 方法异步地连接服务器,并在连接成功后调用 `ConnectCallback` 方法。在发送数据时,我们使用 `BeginSend` 方法异步地发送数据,并在发送完成后调用 `SendCallback` 方法。在接收数据时,我们使用 `BeginReceive` 方法异步接收数据,并在接收完成后调用 `ReceiveCallback` 方法。在关闭客户端时,我们使用 `Shutdown` 方法关闭连接并调用 `Close` 方法释放资源。 以上是一个简单的 C# Socket 封装示例,实际应用,可能需要根据具体需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值