【飞秋】TCP粘包
来源:互联网 发布:apache部署webservice 编辑:程序博客网 时间:2024/04/29 20:32
首先申明一下,写的这个东西注重的是一个思想~,代码只是参考,并不能直接运行.下面进入正题
这两天在弄Silverlight版本的SOCKET网络编程,参考了菩提树下的杨过的例子,写了一段程序
自己也遇到了一些问题,例如TCP协议的粘包,想了个解决方案,兴冲冲的,GOOGLE了一下发现类似的思想很多,不过决定还是把代码贴出来吧
/// <summary>
/// 发送消息
/// </summary>
/// <param name="msgOrSql">消息类容</param>
/// <returns></returns>
public bool MsgTo(MsgOrSql msgOrSql)
{
try
{
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
MemoryStream ms = new MemoryStream();
byte[] dataLen = new byte[sizeof(long)];
ms.Write(dataLen, 0, dataLen.Length);//先占住前八位字符
string jsonString = JsonConvert.SerializeObject(msgOrSql);//JSON的序列化方式,读者也可以用其它的序列化方式
byte[] msgByte = Encoding.UTF8.GetBytes(jsonString);//写入需要的流
ms.Write(msgByte,0,msgByte.Length);
ms.Position = 0;//从头写
dataLen = BitConverter.GetBytes(ms.Length - dataLen.Length);//有效长度
ms.Write(dataLen,0,dataLen.Length);// 将有效长度写入流中
byte[] data = ms.ToArray();//需要发送的字节流
ms.Close();
socketEventArg.SetBuffer(data,0,data.Length);
socketEventArg.RemoteEndPoint = clickSocket.RemoteEndPoint;
clickSocket.SendAsync(socketEventArg);
}
catch (Exception ee)
{
Console.Write(ee);
}
return false;
}
以上是发送代码,下面贴一下接收方的代码
private int msgLength;//消息总长
private int yishouLength;//已收消息长度
byte[] lstReceiveBytes = new byte[] { };//已经接受的数据
/// <summary>
/// 接受服务端发来的数据-回调处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnSocketReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.Buffer == null)
{
return;
}
MemoryStream ms = new MemoryStream();
if (lstReceiveBytes.Length == 0)//判断缓存是否存在数据
{
//缓存中没有数据
msgLength = int.Parse(BitConverter.ToInt64(e.Buffer, 0).ToString());//获得数据长度,也就是发送端写入的字符长度
ms.Write(e.Buffer, 8, e.BytesTransferred - 8);//将剩余的字符流写入缓存,除了长度字符之外,所以是从第八位开始读取数据流的
}
else
{
//缓存中有数据了
ms.Write(lstReceiveBytes,0,lstReceiveBytes.Length);//将原有的数据写入缓存
ms.Write(e.Buffer, 0, e.BytesTransferred);//将当前接收数据写入缓存
}
yishouLength += e.BytesTransferred;//已收数据长度+=当前收的数据长度
lstReceiveBytes = ms.ToArray();//缓存中的数据替换
ms.Close();
GetMsgByByte();//调用处理缓存数据的方法
try
{
//继续异步地从服务端 Socket 接收数据(类似长连接)
if (clickSocket != null && clickSocket.Connected)
{
clickSocket.ReceiveAsync(e);
}
else
{
Console.Write("无法连接到服务器...请刷新后再试...");
}
}
catch (Exception ex)
{
Console.Write(ex.Message.ToString());
}
}
private void GetMsgByByte()//处理缓存数据的方法
{
try
{
if (lstReceiveBytes.Length >= msgLength) //如果已接受的数据长度大于等于定义的数据长度 也就是说可以处理一条消息了
{
byte[] msgByte = new byte[] { };//当前需要处理的包信息
msgByte = lstReceiveBytes;//默认为缓存中的信息
if (lstReceiveBytes.Length > msgLength) //处于粘包状态
{
//将字符流分为两个部分,一部分为当前的一条消息,分离出来当前处理,另一部分为下一步操作的数据流,写入缓存进行下一步操作,(或许写的有点模糊)
MemoryStream m = new MemoryStream();
m.Write(lstReceiveBytes,0,msgLength);//取字符流中的一条传送完毕的消息
msgByte = m.ToArray();
m.Close();
m = new MemoryStream();
m.Write(lstReceiveBytes, msgLength, lstReceiveBytes.Length - msgLength);//根据当前的数据长度读取一条数据,所属的所有字符流
byte[] bb = m.ToArray();
msgLength = int.Parse(BitConverter.ToInt64(bb, 0).ToString());//获得下一条信息的字符流的长度
m.Close();
m = new MemoryStream();
m.Write(lstReceiveBytes, msgLength, lstReceiveBytes.Length - msgLength);//获得下一条信息的字符流,(不包含长度的字符流)
lstReceiveBytes = m.ToArray();//替换缓存中的数据
m.Close();
}
string jsonString = Encoding.UTF8.GetString(lstReceiveBytes.ToArray(), 0, msgLength);//JSON的序列化方式,大家可以不用理会
MsgOrSql msgOrSql = new MsgOrSql();
msgOrSql = JsonConvert.DeserializeObject<MsgOrSql>(jsonString);
if (msgOrSql.dbInfo != null)
{
if (msgOrSql.dbInfo.FangFaMin != null)
{
DBHelper.listDbInfo.Add(msgOrSql.dbInfo.FangFaMin, msgOrSql.dbInfo);
}
}
if (msgOrSql.msgInfo != null)
{
if (msgOrSql.msgInfo.Id != null)//发送者的ID不为空
{
UserHelper.listMsg.Add(msgOrSql.msgInfo);
}
}
if (lstReceiveBytes.Length > msgLength)
{
GetMsgByByte();//递归调用处理字符流的方法
}else
{
return;
}
//字符流处理完毕,初始化接受消息的一些信息
msgLength = 0;
yishouLength = 0;
lstReceiveBytes=new byte[0];
}
}
catch (Exception ex)
{
Console.Write(ex);
}
}
两个方法,一个是接受的回调函数,另外一个是处理字符流的方法
代码写的可能有些问题,COPY下去也运行不了,不过主要的思想应该是表现出来了,相信有些功底的人都能看得懂
发送消息之前,将消息打包,消息头之前添加该消息的字符长度,接收方接受消息之后根据字符长度,判断消息是否处理完毕,如果出现粘包,则继续根据下一条消息的长度,处理下一条消息
希望对读者有所启发吧
关注技术文章飞秋:http://www.freeeim.com/,24小时专业转载。
- 【飞秋】TCP粘包
- TCP粘包、拆包
- TCP粘包,拆包
- TCP粘包问题
- TCP粘包现象
- TCP粘包问题
- TCP粘包
- tcp粘包分析
- TCP粘包问题
- tcp粘包分析
- tcp粘包分析
- TCP粘包问题
- tcp 粘包问题
- tcp 粘包分析
- tcp粘包分析
- tcp粘包分析
- TCP Socket 粘包
- tcp 粘包问题
- 殿堂之路──读书笔记
- 过程即目的
- 【飞秋】Android开发——NDK开发入门
- php system命令在windows无效
- TweenLite 缓动类
- 【飞秋】TCP粘包
- Linux内核启动
- dsb
- poj3268
- PV3D手记
- 【飞秋】SQL Server性能调教系列(4)--Profiler(上)
- ubuntu 10.04 安装好后必须要做的事(嵌入式开发)
- pku1624 This Takes the Cake
- 经营之道