好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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 
本页版权归作者和博客园所有,欢迎转载,但未经作者同意必须保留此段声明, 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利 

 

 

 

 

 

 

标签:  Socket ,  Tcp异步通讯 ,  Tcp粘包

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Socket编程 (异步通讯,解决Tcp粘包) 3的详细内容...

  阅读:63次