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的详细内容...