博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Socket编程 (异步通讯,解决Tcp粘包) - Part3
阅读量:4959 次
发布时间:2019-06-12

本文共 6569 字,大约阅读时间需要 21 分钟。

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字符串:

   ///     /// 文本信息    ///     public class MessageInfo    {        public string Content { get; set; }//信息内容        public MessageInfo(string content)        {            this.Content = content;        }        public override string ToString()        {            return String.Format("
", this.Content); } } /// /// 文件信息 /// 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("
"); return sbString.ToString(); } } /// /// 订立信息协议 /// 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("
", MessageType)); if (MessageType == MessageType.text) { sbString.Append(MessageInfo.ToString()); } else { sbString.Append(RequestFile.ToString()); } sbString.Append("
"); return sbString.ToString(); } public byte[] ToBytes() { return Encoding.UTF8.GetBytes(this.ToString()); } }

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

     //临时缓存        public string temp = string.Empty;        //匹配协议获取信息        public List
HandlerString(string msg) { List
msgProList = new List
(); if (!String.IsNullOrEmpty(temp)) { msg = temp + msg; } string pattern = "(^
.*?
)"; 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丢包的个人解决方案!

附上源码:

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

转载于:https://www.cnblogs.com/zengqinglei/archive/2013/05/14/3078842.html

你可能感兴趣的文章
Strict Standards: Only variables should be passed by reference
查看>>
hiho_offer收割18_题解报告_差第四题
查看>>
AngularJs表单验证
查看>>
静态方法是否属于线程安全
查看>>
02号团队-团队任务3:每日立会(2018-12-05)
查看>>
SQLite移植手记1
查看>>
C# windows程序应用与JavaScript 程序交互实现例子
查看>>
HashMap详解
查看>>
js05-DOM对象二
查看>>
mariadb BINLOG_FORMAT = STATEMENT 异常
查看>>
C3P0 WARN: Establishing SSL connection without server's identity verification is not recommended
查看>>
iPhone在日本最牛,在中国输得最慘
查看>>
动态方法决议 和 消息转发
查看>>
WPF自定义搜索框代码分享
查看>>
js 基础拓展
查看>>
C#生成随机数
查看>>
iOS CoreData介绍和使用(以及一些注意事项)
查看>>
Android应用程序与SurfaceFlinger服务的连接过程分析
查看>>
Java回顾之多线程
查看>>
sqlite
查看>>