• 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();
            }
        }
  • 相关阅读:
    设计模式命令模式(Command)
    设计模式责任链模式(COR)
    设计模式备忘录模式(Memento)
    设计模式中介者模式(Mediator)
    设计模式策略模式(Strategy)
    设计模式解释器模式(Interpreter)
    设计模式迭代器模式(Iterator)
    设计模式状态模式(State)
    Ext终于开始收费了
    设计模式观察者模式(Observer)
  • 原文地址:https://www.cnblogs.com/zhengzc/p/11208377.html
Copyright © 2020-2023  润新知