zoukankan      html  css  js  c++  java
  • Socket编程 (异步通讯,解决Tcp粘包) 3

    Socket编程 (异步通讯,解决Tcp粘包) - Part3

    Socket编程 (异步通讯,解决Tcp粘包)

      从上一章的通讯中,我们发现如果使用Tcp连续发送消息会出现消息一起发送过来的情况,这样给我们编程造成一定的问题,给我们的信息解析造成一定的问题。那么这篇文章就将针对以上问题给出解决方案......

    问题一般会出现的情况如下,假设我们连续发送两条两天记录("我是liger_zql"):

    模拟发送示例:

    复制代码
     #region 测试消息发送,并匹配协议
      TcpClient client = new TcpClient();
      client.AsynConnect();
      Console.WriteLine("下面将连续发送2条测试消息...");
      Console.ReadKey();
      MessageProtocol msgPro;
      for (int i = 0; i < 2; i++)
      {
        msgPro = new MessageProtocol("我是liger_zql");
        Console.WriteLine("第{0}条:{1}", i + 1, msgPro.MessageInfo.Content);
        client.AsynSend(msgPro);
      }
      #endregion
    复制代码

    接收端接受两条信息会出现如下三种情况:

      1.(1)我是liger_zql(2)我是liger_zql

      2.(1)我是liger_zql我是(2)liger_zql

      3.(1)我是liger_zql我是liger_zql

    通过以上三种情况,显然2、3都不是我们想要的结果。那么如何处理这中情况呢?

    解决方案:通过自定义协议...

    我们可以以将信息以xml的格式发送出去,列入<protocol>content</protocol>通过正则匹配信息是否完整,如果不完整,我们可以先将本次接受信息缓存接受下一次信息,再次匹配得到相应的结果。

    将信息对象转换成一定格式的xml字符串:

    复制代码
       /// <summary>
        /// 文本信息
        /// </summary>
        public class MessageInfo
        {
            public string Content { get; set; }//信息内容
            public MessageInfo(string content)
            {
                this.Content = content;
            }
            public override string ToString()
            {
                return String.Format("<message Content=\"{0}\" />", this.Content);
            }
        }
    
        /// <summary>
        /// 文件信息
        /// </summary>
        public class RequestFile
        {
            public string Address { get; set; }//发送端Ip
            public int Port { get; set; }//端口号
            public RequestMode Mode { get; set; }//请求类
            public FileObject FileObject { get; set; }//文件详细参数
            public RequestFile() { }
            public RequestFile(RequestMode mode, FileObject fileobject)
            {
                this.Mode = mode;
                this.FileObject = fileobject;
            }
            public RequestFile(string address, int port, RequestMode mode, FileObject fileobject)
            {
                this.Address = address;
                this.Port = port;
                this.Mode = mode;
                this.FileObject = fileobject;
            }
            public RequestFile(string address, int port, RequestMode mode, string filename, long filelength, int packetsize, long packetcount)
            {
                this.Address = address;
                this.Port = port;
                this.Mode = mode;
                this.FileObject = new FileObject(filename, filelength, packetsize, packetcount);
            }
            public override string ToString()
            {
                StringBuilder sbString = new StringBuilder();
                sbString.Append("<message ");
                sbString.Append(String.Format("Address=\"{0}\" ", Address));
                sbString.Append(String.Format("Port=\"{0}\" ", Port));
                sbString.Append(String.Format("Mode=\"{0}\" ", Mode));
                sbString.Append(String.Format("FileName=\"{0}\" ", FileObject.FileName));
                sbString.Append(String.Format("FileLength=\"{0}\" ", FileObject.FileLength));
                sbString.Append(String.Format("PacketSize=\"{0}\" ", FileObject.PacketSize));
                sbString.Append(String.Format("PacketCount=\"{0}\" ", FileObject.PacketCount));
                sbString.Append("/>");
                return sbString.ToString();
            }
        }
        /// <summary>
        /// 订立信息协议
        /// </summary>
        public class MessageProtocol
        {
            public MessageType MessageType { get; set; }
            public MessageInfo MessageInfo { get; set; }
            public RequestFile RequestFile { get; set; }
            public MessageProtocol() { }
            public MessageProtocol(string msg)
            {
                MessageType = MessageType.text;
                MessageInfo = new MessageInfo(msg);
            }
            public MessageProtocol(RequestMode mode, FileObject fileobject)
            {
                MessageType = MessageType.file;
                RequestFile = new RequestFile(mode, fileobject);
            }
            public MessageProtocol(string address, int port, RequestMode mode, FileObject fileobject)
            {
                MessageType = MessageType.file;
                RequestFile = new RequestFile(address, port, mode, fileobject);
            }
            public override string ToString()
            {
                StringBuilder sbString = new StringBuilder();
                sbString.Append(String.Format("<protocol Type=\"{0}\">", MessageType));
                if (MessageType == MessageType.text)
                {
                    sbString.Append(MessageInfo.ToString());
                }
                else
                {
                    sbString.Append(RequestFile.ToString());
                }
                sbString.Append("</protocol>");
                return sbString.ToString();
            }
            public byte[] ToBytes()
            {
                return Encoding.UTF8.GetBytes(this.ToString());
            }
        }
    复制代码

    对接收的信息通过正则进行匹配处理:

    复制代码
         //临时缓存
            public string temp = string.Empty;
            //匹配协议获取信息
            public List<MessageProtocol> HandlerString(string msg)
            {
                List<MessageProtocol> msgProList = new List<MessageProtocol>();
                if (!String.IsNullOrEmpty(temp))
                {
                    msg = temp + msg;
                }
                string pattern = "(^<protocol Type=.*?>.*?</protocol>)";
                if (Regex.IsMatch(msg, pattern))
                {
                    //匹配协议内容
                    string match = Regex.Match(msg, pattern).Groups[0].Value;
                    //将匹配的内容添加到集合
                    msgProList.Add(HandlerObject(match));
                    temp = string.Empty;
                    //截取未匹配字符串,进行下一次匹配
                    msg = msg.Substring(match.Length);
                    if (!String.IsNullOrEmpty(msg))
                    {
                        msgProList.AddRange(HandlerString(msg));
                    }
                }
                else
                {
                    temp = msg;
                }
                return msgProList;
            }
    复制代码

    然后将该定义的协议换换成信息对象,通过对象获取自己想要的信息。

    复制代码
            //将已转成协议信息转成对象信息
            public MessageProtocol HandlerObject(string protocol)
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.LoadXml(protocol);
                XmlNode root = xmldoc.DocumentElement;
                XmlNode msgnode = root.SelectSingleNode("message");
                MessageProtocol msgPro = new MessageProtocol();
                if (root.Attributes["Type"].Value == MessageType.text.ToString())
                {
                    msgPro.MessageType = MessageType.text;
                    msgPro.MessageInfo = new MessageInfo(msgnode.Attributes["Content"].Value);
                }
                else
                {
                    msgPro.MessageType = MessageType.file;
                    RequestMode mode = (RequestMode)Enum.Parse(typeof(RequestMode), msgnode.Attributes["Mode"].Value);
                    FileObject fileobject = new FileObject();
                    fileobject.FileName = msgnode.Attributes["FileName"].Value;
                    fileobject.FileLength = Convert.ToInt64(msgnode.Attributes["FileLength"].Value);
                    fileobject.PacketSize = Convert.ToInt32(msgnode.Attributes["PacketSize"].Value);
                    fileobject.PacketCount = Convert.ToInt64(msgnode.Attributes["PacketCount"].Value);
                    msgPro.RequestFile = new RequestFile(
                        msgnode.Attributes["Address"].Value,
                        Convert.ToInt32(msgnode.Attributes["Port"].Value),
                        mode, fileobject);
                }
                return msgPro;
            }        
    复制代码

    最后运行结果如下:

    好了Tcp粘包的问题我们解决了。下一章我们将解决Udp丢包的个人解决方案!

    附上源码:SocketProQuests.zip

    作者:曾庆雷
    出处:http://www.cnblogs.com/zengqinglei
    本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利

     

     

     

     

     

     

  • 相关阅读:
    print格式化输出(format)
    Python list,tuple,dict,set高级变量常用方法
    K8s的kubectl常用命令
    C/C++中数组与指针的关系探究
    Java中的不可变类
    JAVA 类总结
    c++ string类find总结
    关于看板娘的事儿
    类型转换
    计算机2进制小数点表示法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3078892.html
Copyright © 2011-2022 走看看