C# 粘包怎么解决

.Net技术 码拜 7年前 (2014-12-13) 2566次浏览 0个评论

10分

处理粘包有2种做法比较常见的做法
1.发送定长包,就是每次发送的数据包长度不变,这样
server端就可以根据收到的数据长度,判定多收了,还是少收了。
2.在数据包头写的数据长度,这种方式灵活,server接收到数据后,
首先获取包长,以此为依据来分包。

—-

引用 1 楼 xian_wwq 的回复:

处理粘包有2种做法比较常见的做法
1.发送定长包,就是每次发送的数据包长度不变,这样
server端就可以根据收到的数据长度,判定多收了,还是少收了。
2.在数据包头写的数据长度,这种方式灵活,server接收到数据后,
首先获取包长,以此为依据来分包。

能简短的来段示范代码吗?

—-

http://blog.csdn.net/bdmh/article/details/25367297
我一般这么干,仅供参考,视具体情况定

—-

引用 3 楼 bdmh 的回复:

http://blog.csdn.net/bdmh/article/details/25367297
我一般这么干,仅供参考,视具体情况定

不管你发送的是多少,即使是1个字节,也将它放到一个1024的buffer中,保证每次发送的都是1024个长度,与服务端保持一致,这样也可以,就是有点浪费网络资源,高并发的情况下,不提倡。你写的这段思路看懂了,但是不会写,因为我刚刚接触这块,只会模仿着写。能给点具体的代码吗?谢谢拉

—- 10分

http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket(v=vs.110).aspx
这个实例中的SocketSendReceive 方法,将

Byte[] bytesReceived = new Byte[256];

放到do while循环里,可能是你想要的。

—- 10分

 …… 粘包啊,
这个涉及到自己定义数据格式了
比如头4个字节定义传输长度,后面接数据,然后根据头4个字节来获取数据。
4|n4|n4|n
当然这只是最简单的方式。主要的解决方式就是自己定义数据格式,这点有些像通信协议。
有兴趣的话可以自己看看socket通信协议,模拟这个写就OK。
数据就像古文,这么断句要先告诉别人,要不就等着百家争鸣吧

—- 10分

先写长度可以大体这样做,你把例子拷贝到你的服务/客户程序上,双方就可以发字符串聊天了:

static void SendString(Socket socket, string str)
{
    byte[] utf8 = Encoding.UTF8.GetBytes(str);
    Send(socket, BitConverter.GetBytes(utf8.Length));  // 先发内容的长度
    Send(socket, utf8);  // 再发实际内容
}

static string ReceiveString(Socket socket)
{
    byte[] lengthBytes = Receive(socket, 4);
    int length = BitConverter.ToInt32(lengthBytes, 0);  // 先收内容长度
    byte[] strBytes = Receive(socket, length);
    return Encoding.UTF8.GetString(strBytes);  //再收内容
}

static byte[] Receive(Socket socket, int length)
{
    byte[] bytes = new byte[length];
    for (int offset = 0; offset < length; )
    {
        int received = socket.Receive(bytes, offset, bytes.Length - offset, SocketFlags.None);
        if (received == 0) throw new Exception("socket closed");
        offset += received;
    }
    return bytes;
}

static void Send(Socket socket, byte[] bytes)
{
    for (int offset = 0; offset < bytes.Length; )
    {
        offset += socket.Send(bytes, offset, bytes.Length - offset, SocketFlags.None);
    }
}

—- 10分

先发长度再发包

—-

引用 7 楼 Forty2 的回复:

先写长度可以大体这样做,你把例子拷贝到你的服务/客户程序上,双方就可以发字符串聊天了:

static void SendString(Socket socket, string str)
{
    byte[] utf8 = Encoding.UTF8.GetBytes(str);
    Send(socket, BitConverter.GetBytes(utf8.Length));  // 先发内容的长度
    Send(socket, utf8);  // 再发实际内容
}

static string ReceiveString(Socket socket)
{
    byte[] lengthBytes = Receive(socket, 4);
    int length = BitConverter.ToInt32(lengthBytes, 0);  // 先收内容长度
    byte[] strBytes = Receive(socket, length);
    return Encoding.UTF8.GetString(strBytes);  //再收内容
}

static byte[] Receive(Socket socket, int length)
{
    byte[] bytes = new byte[length];
    for (int offset = 0; offset < length; )
    {
        int received = socket.Receive(bytes, offset, bytes.Length - offset, SocketFlags.None);
        if (received == 0) throw new Exception("socket closed");
        offset += received;
    }
    return bytes;
}

static void Send(Socket socket, byte[] bytes)
{
    for (int offset = 0; offset < bytes.Length; )
    {
        offset += socket.Send(bytes, offset, bytes.Length - offset, SocketFlags.None);
    }
}

问题在于,如果发送的长度和数据包都粘包了,怎么办,这种多次发送个人认为不可取啊

—- 10分

把实际内容拼接到长度的后面,一个包发送出去,不行?

—-

引用 10 楼 Z65443344 的回复:

把实际内容拼接到长度的后面,一个包发送出去,不行?

不会拼接啊,能否来段代码看下呢?

—-

所谓拼接,不就是操作byte[ ]数组吗
比如先定义一个byte[ ] b1=new byte[1000];
再定义一个byte[ ] b2=new byte[4];
如果b1里是当前要发送的内容,计算出有效数据的长度(字节数),转成byte[ ],赋值给b2
然后循环b1,全部往后移动4个字节,再把b2内容赋值给b1的前4个字节,然后有效数据长度+=4
发送

接收到之后,前4个字节就表示后面的数据位数,如果收到的总长度不等于这个数字+4,说明不是多了就是少了

—-

有个简单办法,发送方发送完一包后就等待接收方回复确认,收到确认后再发下一包,唯一缺点是减慢了数据传输速度,不过大部分情况这都不是问题。

—-

引用 10 楼 Z65443344 的回复:

把实际内容拼接到长度的后面,一个包发送出去,不行?

这样做没有实际的优点。

UDP是基于消息的,因此你一次发一个UDP包,对方要么收不到,要么就完整的收到一个UDP包。
但TCP是基于字节流的,它从来就没有保证过按发送的批次来进行数据分割的。因此,你发两次数据和发一次数据,接收方并不能区别,他可能一次就收到两批数据,也可能五次才收到一批数据。

所谓的’粘包‘,并不是由发送批次造成的,而是由于误解TCP的流式传输而形成的错误程序设计造成的。
TCP是可靠的协议,只要双方的约定正确,那里有’粘包‘的困扰?

—-

另外,科普一下。

目前流行的Socket实现中都用了Nagle algorithm。
它使得少量数据不会被马上传送出去,而是积累到一定数量或超过一定的时间,才进行传输。
这是因为每个IP+TCP包都有至少40个字节的开销,立即传送几个字节的数据会造成浪费。

也就是说,先发几个字节接着发一段数据,跟事先合并字节与数据,没有实际上的区别。

—-

包头+包体+数据
包体=数据的长度(知道固定长度的字节)
接受端,接受包头+包体

—-

包体里存着 数据的实际长度,这样后面怎么粘包,也不会多接收,

—-

引用 14 楼 Forty2 的回复:
Quote: 引用 10 楼 Z65443344 的回复:

把实际内容拼接到长度的后面,一个包发送出去,不行?

这样做没有实际的优点。

UDP是基于消息的,因此你一次发一个UDP包,对方要么收不到,要么就完整的收到一个UDP包。
但TCP是基于字节流的,它从来就没有保证过按发送的批次来进行数据分割的。因此,你发两次数据和发一次数据,接收方并不能区别,他可能一次就收到两批数据,也可能五次才收到一批数据。

所谓的’粘包‘,并不是由发送批次造成的,而是由于误解TCP的流式传输而形成的错误程序设计造成的。
TCP是可靠的协议,只要双方的约定正确,那里有’粘包‘的困扰?

这位朋友你所指的约定正确是怎么个做法,能详细说明下吗?

—-

自定义协议头,协议尾,数据帧= 协议头+数据长度+数据+协议尾。接收到数据后找到协议头,根据数据长度算得协议尾的位置,如果那个位置的数据确实是协议尾 这就是一条完整的数据帧。

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C# 粘包怎么解决
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!