C# Socket通信 定义收发信息协议处理粘包

客户端发送数据到服务端时可能有以下三种情况:

1.服务端完整接收客户端发送的一条消息;
2.客户端发送的一条消息被当成两条消息处理;
3.客户端发送的两条消息(甚至更多)被合并成一条消息接收;
原因是NetWrokStream写入数据时,数据并没有立即发往远程主机,而是保存在了TCP缓存(TCP Buffer)中,经过一段时间之后再进行发送,对于传输二进制文件并没什么影响,但对于文本来说,就需要明确发送文本的边界。

// 协议:[length=XX]..... 其中XX是字符串的长度(注意不是字节数组buffer的长度)

如:"[length=12]Hello World!"

  • 处理逻辑

服务端接收字符串后可能有下面两种情况:

1."[""]"中括号是完整的,可以读取到字符串的length长度,然后根据这个数值与后面的字符串长度相比,如果相等则说明接收到的数据是完整的;如果多了,则说明接收的字节数多了,取出合适的长度,并将剩余的数据进行缓存,等待下一次接收数据的时候进行合并;如果少了,说明接收数据不完整,将数据进行缓存,等待下一次接收的户数的时候进行合并。

2."[""]"中括号本身不完整,此时读取不到字符串的length长度,将接收的数据进行缓存,等待读取下次接收的数据,然后将两次合并字后的数据按上面的方式进行处理。

  • 编码实现

定义RequestHandler类用于服务端解析客户端发送的字符串数据:

    public class RequestHandler
    {
        private string temp = string.Empty;

        public string[] GetActualString(string input) {
            return GetActualString(input, null);
        }

        public string[] GetActualString(string input, List<string> outputList) {
            if (outputList == null) {
                outputList = new List<string>();
            }
            if (!string.IsNullOrEmpty(temp)) {
                input = temp + input;
            }

            string output;
            // (?<=patten) 反向肯定预查询   (?=patten) 正向肯定预查询
            string patten = @"(?<=^\[length=)(\d+)(?=\])";
            int length;

            if (Regex.IsMatch(input, patten)) {
                Match m = Regex.Match(input, patten);
                // 获取消息字符串实际长度
                length = Convert.ToInt32(m.Groups[0].Value);
                // 获取需要进行截取的位置
                int startIndex = input.IndexOf(']') + 1;
                // 获取从截取位置开始后所有字符的长度
                output = input.Substring(startIndex);

                if (output.Length == length) {
                    // output长度与消息字符串长度相等,说明刚好是完整的一条消息
                    outputList.Add(output);
                    temp = "";
                }
                else if (output.Length < length) {
                    // output长度小于消息字符串长度,说明消息没有发完整
                    // 应将整条消息,包括元数据全部缓存,与下一条数据合并起来再进行处理
                    temp = input;                    
                }
                if (output.Length > length) {
                    // output长度大于消息字符串长度,说明消息发完整了,但是有多余的数据
                    // 多余的数据可能是截断消息,也可能是多条完整的消息
                    // 截取字符串
                    output = output.Substring(0, length);
                    outputList.Add(output);
                    temp = "";
                    // 缩短input的长度
                    input = input.Substring(startIndex + length);                    
                    // 递归调用
                    GetActualString(input, outputList);                    
                }
            }
            else {
                // "[","]"不完整
                temp = input;
            }
            return outputList.ToArray();
        }
    }

 

posted @ 2020-09-30 15:14  Z大山  阅读(891)  评论(1编辑  收藏  举报