c# socket通信,定义消息体长度同步接收数据

因为没有文件上传,没有大的字节传输,数据过来就放到队列,所以没有用异步,使用同步方式接收数据。

原理就是:

1.前面四个字节是消息头,定义消息体长度;

2.后面字节定义消息体;

3.服务端收到消息后,根据消息头标记的长度获取消息体,如果不够,继续接收;如果够了并且有多余,重新获取头部,依次往下走;

        private void Receive(Socket socket, string ip)
        {
            Task.Factory.StartNew(() =>
            {
                var pack = new BytePkg();

                while (true)
                {
                    try
                    {
                        //如果socket已经断开,结束循环
                        if (!socket.Connected)
                        {
                            _logger.Warn($"IP:{ip},socket已断开,停止接收数据;");
                            break;
                        }

                        byte[] prevBytes = new byte[1024];
                        int len = socket.Receive(prevBytes, prevBytes.Length, SocketFlags.None);
                        var bytes = prevBytes.Take(len).ToArray();

                        this.RcvHeadData(pack, bytes);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"IP:{ip},接收socket数据异常,message:{ex.Message},stackTrace:{ex.StackTrace};");
                    }
                }
            });
        }
        
        /// <summary>
        /// 消息头
        /// </summary>
        /// <param name="pack"></param>
        /// <param name="bytes"></param>
        private void RcvHeadData(BytePkg pack, byte[] bytes)
        {
            var len = bytes.Length;

            pack.headIndex += len;if (pack.headIndex < pack.headLen)
            {for (int x = 0; x < len; x++)
                {
                    pack.headBuff[pack.headIndex - len + x] = bytes[x];
                };
            }
            else
            {var actualHeadLen = pack.headIndex - len;//head的实际长度
                var skipHeadLen = pack.headLen - actualHeadLen;//需要补上的长度;head定义长度 - head的实际长度 = body需要跳过的长度
                for (int x = 0; x < skipHeadLen; x++)
                {
                    pack.headBuff[actualHeadLen + x] = bytes[x];
                }//★★★★★开始处理消息体部分★★★★★
                var bodyLen = len;//身体长度
                if (skipHeadLen > 0)
                {
                    bodyLen = len - skipHeadLen;//第一次,获取剩余部分的长度
                    pack.InitBodyBuff();//第一次,需要初始化body
                }this.RcvBodyData(pack, bytes.Skip(skipHeadLen).Take(bodyLen).ToArray());
            }
        }

        /// <summary>
        /// 消息体
        /// </summary>
        /// <param name="pack"></param>
        /// <param name="bytes"></param>
        private void RcvBodyData(BytePkg pack, byte[] bytes)
        {
            var len = bytes.Length;

            pack.bodyIndex += len;
if (pack.bodyIndex < pack.bodyLen)
            {for (int x = 0; x < len; x++)
                {
                    pack.bodyBuff[pack.bodyIndex - len + x] = bytes[x];
                };
            }
            else
            {var actualBodyLen = pack.bodyIndex - len;//body的实际长度
                var skipBodyLen = pack.bodyLen - actualBodyLen;//需要补上的长度;body定义长度 - body的实际长度 = 本次需要获取的body长度
                for (int x = 0; x < skipBodyLen; x++)
                {
                    pack.bodyBuff[actualBodyLen + x] = bytes[x];
                }
//处理接收到的数据
                NetMsg msg = ByteHelper.DeSerialize<NetMsg>(pack.bodyBuff);
                this.OnReceiveMsg(msg);
//重置信息包
                pack.ResetData();//★★★★★开始处理消息头部分★★★★★
                this.RcvHeadData(pack, bytes.Skip(skipBodyLen).ToArray());
            }
        }

如果不放心,可以在该加日志的地方加日志,观察代码的逻辑是否正确。说明:这是借鉴一个叫做PESocket的开源项目改的。

猜你喜欢

转载自www.cnblogs.com/subendong/p/12570289.html