从上一章的通讯中,我们发现如果使用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的格式发送出去,列入content通过正则匹配信息是否完整,如果不完整,我们可以先将本次接受信息缓存接受下一次信息,再次匹配得到相应的结果。
将信息对象转换成一定格式的xml字符串:
扫描二维码关注公众号,回复:
17542953 查看本文章

/// <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丢包的个人解决方案!