socket中处理数据包的问题

马少华 2010-07-11 02:05:15

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

bool isLoop = true;
Socket socketclient;
Queue<byte[]> m_packageArray = new Queue<byte[]>();
long FileLen = 0;
FileStream fs;

private void button1_Click(object sender, EventArgs e)
{
try
{
fs = new FileStream(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\Prevention.exe", FileMode.OpenOrCreate);
}
catch
{
isLoop = false;
return;
}

if (!System.Threading.ThreadPool.QueueUserWorkItem(this.DoPackage)) return;

byte[] buffReceive = new byte[1024*4];
byte[] buffer = new byte[1024*4];

int nIndex = 0;
while (isLoop)
{
//有数据到达
if (socketclient.Poll(20 * 1000, SelectMode.SelectRead))
{
int nlen = socketclient.Receive(buffReceive, nIndex, buffReceive.Length - nIndex, SocketFlags.None);
if (nlen == 0)
break;

//分析数据包
int nPackageHead = 0;
int nPackageTail = 0;
lock (buffReceive)
{
for (int i = 0; i < nlen; i++)
{
//报头
if (buffReceive[i] == (byte)60)
nPackageHead = i;

//报尾
if (buffReceive[i] == (byte)62)
{
nPackageTail = i;
//一个完整的数据包
if (nPackageTail != 0)
{
byte[] bufftemp = new byte[nPackageTail - nPackageHead + 1];
Buffer.BlockCopy(buffReceive, nPackageHead, bufftemp, 0, bufftemp.Length);
lock (m_packageArray)
{
m_packageArray.Enqueue(bufftemp);
}
nPackageHead = 0;
nPackageTail = 0;
nIndex = 0;
}
}
}
//有半包的数据
if (nPackageHead != 0 && nPackageTail == 0)
{
//将剩下的数据复制到备用缓存区
Array.Copy(buffReceive, nPackageHead, buffer, 0, buffReceive.Length - nPackageHead - 1);
Array.Clear(buffReceive, 0, buffReceive.Length);
Array.Copy(buffer, 0, buffReceive, 0, buffReceive.Length - nPackageHead - 1);
nIndex = buffReceive.Length - nPackageHead - 1;
Array.Clear(buffer, 0, buffer.Length);
}
else
{
Array.Clear(buffReceive, 0, buffReceive.Length);
}
}
}
Application.DoEvents();
}
socketclient.Shutdown(SocketShutdown.Both);
socketclient.Close();
Close();
}

private void DoPackage(object obj)
{
while (isLoop)
{
if (m_packageArray.Count > 0)
{
lock (m_packageArray)
{
for (int i = 0; i < m_packageArray.Count; i++)
{
byte[] buff = m_packageArray.Dequeue();//问题出在这里buff中怎么有这样的数据<.....><....>,正常数据应该只有一对<...>,不知道是不是我解包有误 //找文件长度
string strData = System.Text.Encoding.Default.GetString(buff, 0, 8);
if (strData == "<1FILELE")//找到文件长度包
{
strData = System.Text.Encoding.Default.GetString(buff, 0, buff.Length);
FileLen = long.Parse(strData.Substring(9, strData.Length - 10));
}
else if (buff[1] == (byte)49 && (buff[2] >= (byte)48 && buff[2] <= (byte)57))//文件类型的包
{
//去除包头,包标记等
fs.Write(buff, 8, buff.Length - 9);
}
if (FileLen > 0)
{
if (fs.Length == FileLen)
{
fs.Close();
isLoop = false;
}
}
}
}
}
System.Threading.Thread.Sleep(100);
}
}
...全文
409 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
dlut_qiong 2010-09-14
  • 打赏
  • 举报
回复
public byte[] ConvertToByte(int len)
{
//C#中int是4个byte长
byte[] b=new byte[4];
for(int i=0;i<4;i++)
{
byte[i]=(byte)(i>>8*i);
}
return b;
}

public int ConvertToInt(Byte[] b)
{
int len=0;
for(int i=0;i<4;i++)
{
i=i|(((int)byte[i])<<(8*i));
}
return len;
}
_roni_ 2010-07-15
  • 打赏
  • 举报
回复
学习啦!
马少华 2010-07-15
  • 打赏
  • 举报
回复
有没有朋友有更好的方法
jizhehaha 2010-07-12
  • 打赏
  • 举报
回复
好啊……
Valefish 2010-07-12
  • 打赏
  • 举报
回复
Mark
  • 打赏
  • 举报
回复
有些系统是很简单的,例如 msn(看起来不算简单吧?)其实它每一个消息都是一行,因此可以按行来解析。当你发现接收缓冲区中还有数据(socket的Avaliable>0或者NetworkStream的DataAvaliable==true),继续接收数据;然后对收集所有数据的大的buffer,提取一行一行的消息进行处理;最后不能成为一行的数据留待下一次接收到更多数据时再处理。
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 zaiduxinling 的回复:]
想不明白,
为什么都习惯在报头和报尾上加标记呢?如果不加,不可以吗?一个完整的数据包自定义协议如下:

包头字节数组的长度(int,4个字节) + 包体的字节数组长度(int,4个字节) +
包头【说明性字符串】(字节数组) + 包体【数据】(字节数组)

这样做有弊端吗?
请前辈们指点。。。
[/Quote]

不知道你想说明什么呢?你写的也是标记方法,只不过标记的长度值而已。

当使用xml的方式标记时,它本身就有解析为对象的功能,有许多快速的xml解析器可以从已经收到的buffer中迅速搜索出完整的xml元素(而剩下的不能构成完整xml元素的部分可以等以后收到新数据时再解析)。而标记长度顶多只是标记出了长度。

标记长度有利于简单地控制分包,不过只是在应用层上简单了一些而已。
让爱延续 2010-07-12
  • 打赏
  • 举报
回复
想不明白,
为什么都习惯在报头和报尾上加标记呢?如果不加,不可以吗?一个完整的数据包自定义协议如下:

包头字节数组的长度(int,4个字节) + 包体的字节数组长度(int,4个字节) +
包头【说明性字符串】(字节数组) + 包体【数据】(字节数组)

这样做有弊端吗?
请前辈们指点。。。
王向飞 2010-07-12
  • 打赏
  • 举报
回复
帮顶 等高手
雾霭流岚 2010-07-12
  • 打赏
  • 举报
回复
得分了
yinjianjing 2010-07-12
  • 打赏
  • 举报
回复
每天回帖即可获得10分可用分
马少华 2010-07-12
  • 打赏
  • 举报
回复
听说可以直接用位移的方法进行转换,可惜我不会.
马少华 2010-07-12
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 wodegege10 的回复:]
byte[] buffer = new byte[4];
int nlen = socketclient.Receive(buffr , 0, 4, SocketFlags.None);
if(nlen == 0){
//socket断开连接 退出
return -1;
}
int length = BitConverter.ToInt32(buffer, 0);

buf……
[/Quote]

BitConverter.ToInt32
BitConverter.GetBytes
这两个方法在PC机和PDA上执行的结果不一样.所以不能用.
yrg1006 2010-07-12
  • 打赏
  • 举报
回复
还得讲清楚一些。
CGabriel 2010-07-12
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 sp1234 的回复:]

有些系统是很简单的,例如 msn(看起来不算简单吧?)其实它每一个消息都是一行,因此可以按行来解析。当你发现接收缓冲区中还有数据(socket的Avaliable>0或者NetworkStream的DataAvaliable==true),继续接收数据;然后对收集所有数据的大的buffer,提取一行一行的消息进行处理;最后不能成为一行的数据留待下一次接收到更多数据时再处理。
[/Quote]

它是靠换行符来切割数据报,如果有人伪造一个非常长的句子(例如一二十G),服务器很有可能会因为内存不足而华丽地挂掉。

当然,也可能增加别的功能来限制,或者用放火墙,但个人觉得始终不如直接写清楚长度好。
tyg111 2010-07-12
  • 打赏
  • 举报
回复
学习了
wenbin 2010-07-11
  • 打赏
  • 举报
回复
byte[] buffer = new byte[4];
int nlen = socketclient.Receive(buffr , 0, 4, SocketFlags.None);
if(nlen == 0){
//socket断开连接 退出
return -1;
}
int length = BitConverter.ToInt32(buffer, 0);

buffer = new[length];
int recLen = 0;
REC:
nlen = socketclient.Receive(buffr , recLen, length - recLen, SocketFlags.None);
if(nlen == 0){
//socket断开连接 退出
return -1;
}
recLen += nlen;
if(recLen < Length){
goto REC;
}

当然要求发送方也是要先发送数据长度
马少华 2010-07-11
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 hyblusea 的回复:]
楼主是想解决 粘包的问题??
[/Quote]
是的,但是一直还没解决
hyblusea 2010-07-11
  • 打赏
  • 举报
回复
楼主是想解决 粘包的问题??
kkun_3yue3 2010-07-11
  • 打赏
  • 举报
回复
直接拿程序说明问题,强!

110,535

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧