关于socket通信send和recv中buffer长度的问题

beastsam 2020-02-26 11:42:00
本人写了一个socket 关于tcp通信的小程序,但是遇到了一个问题
我设置的recv和send的buf长度为4096,里面用4096字节的字符串填充
当我服务器调用 函数发送
send(server_sock_fd, buf_send, 4096, 0)

客户端用recv接收
int byte_num = recv(server_sock_fd, buf_rec, 4096, 0);
调用前已经用memset清空了buf_rec,可是byte_num却不是4096

这是为什么?我用的虚拟机开了两个系统模仿局域网,丢包率设置的是0,请问哪位大神知道是怎么回事?
...全文
660 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
xian_wwq 2020-02-26
  • 打赏
  • 举报
回复
tcp是基于流的,不是发100,就一定收100 有可能收了95个,然后又收了5 和网络状况没有直接关联, 所以tcp数据接收,进行“拼包”(一次发送多次接收)、“分包”(多次发送一次接收)是基本操作。
qybao 2020-02-26
  • 打赏
  • 举报
回复
offset = byte_num; 改成 offset += byte_num;
qybao 2020-02-26
  • 打赏
  • 举报
回复
一个包不一定能送完数据,可能送多个包
所以你应该用循环接收,直到byte_num为-1才算接受完。每次接收到的数据自己保存,最后再拼装成一个完整的数据
int byte_num = 0, offset = 0;
char data[4096] = {0};
while(byte_num != -1) { //多次接收直到没有数据
byte_num = recv(server_sock_fd, buf_rec, 4096, 0);
memcpy(data+offset, buf_rec, byte_num); //把每次接收的数据保存
offset = byte_num;
}
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 11 楼 beastsam 的回复:
[quote=引用 10 楼 轻箬笠 的回复:] [quote=引用 6 楼 beastsam 的回复:] [quote=引用 4 楼 轻箬笠 的回复:] recv和send的处理方式不太一样。一般我们都是循环接收的,以保证数据的完整性。所以通行的办法是弄个固定长度的报文头,报文头中带报文长度数据,然后循环接收,直到长度满足要求为止。最简单的报文头就是,前4个字节(即int)就代表了报文的长度。
这个难道不是应该是tcp层处理的么?我理解tcp分片,但是不应该是系统接收完所有的4096分片后才把数据返回给recv里的buf么?而且我看很多教程说循环接受是为了防止发送的数据超过recv里buf的长度,但是我这个并没有超过啊?[/quote] 这个还真不是,曾经在项目中碰到过,发送几百个字节,第一次接收只有几十个字节的情况。[/quote] 这样啊,好吧,谢谢您,我还有个关于带宽的问题不知道您了解么? 我的虚拟机开了3个系统,分别运行了10个客户端,一个中继转发,10个服务器,1个客户端通过中继连接1个服务器,我让服务器同时开始传输200KB的文件,大小分成4096的数据块,发送间隔35ms 我的中继上传和下载带宽都设置的是1MiB/S,虚拟机之间延迟为0,但是我在中继用wireshark抓包发现他的上传和下载每秒的峰值只有0.5MiB,请问这个为什么不是1MiB?[/quote] 额,是我蠢了,没事,是时间间隔的问题
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 8 楼 qybao 的回复:
[quote=引用 5 楼 beastsam 的回复:] TCP分片这个我知道,但是按理来说recv的工作原理难道不是等接收够4096所有的分片包后才把数据返回给recv里的buf么?而且buf长度是足够接收的啊?多次接收的这个我理解是recv中设置的buf长度不够,所以需要循环多次接收,不知道我这么理解问题在哪
通道流一般是有缓存的,它会等到缓存满了就往buf送,这个缓存不一定就是4096(也就是不一定会等到4096才往buf送),所以正确的做法就是循环接收,因为你不能保证一次就接收完整。[/quote] 我还想再请教您一个问题 我的虚拟机开了3个系统,分别运行了10个客户端,一个中继转发,10个服务器,1个客户端通过中继连接1个服务器,我让服务器同时开始传输200KB的文件,大小分成4096字节的数据块,发送间隔35ms 我的中继上传和下载带宽都设置的是1MiB/S,客户端和服务器的带宽设置的是不限制,虚拟机之间延迟为0,但是我在中继用wireshark抓包发现他的上传和下载每秒的峰值只有0.5MiB,请问这个为什么不是1MiB?
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 10 楼 轻箬笠 的回复:
[quote=引用 6 楼 beastsam 的回复:] [quote=引用 4 楼 轻箬笠 的回复:] recv和send的处理方式不太一样。一般我们都是循环接收的,以保证数据的完整性。所以通行的办法是弄个固定长度的报文头,报文头中带报文长度数据,然后循环接收,直到长度满足要求为止。最简单的报文头就是,前4个字节(即int)就代表了报文的长度。
这个难道不是应该是tcp层处理的么?我理解tcp分片,但是不应该是系统接收完所有的4096分片后才把数据返回给recv里的buf么?而且我看很多教程说循环接受是为了防止发送的数据超过recv里buf的长度,但是我这个并没有超过啊?[/quote] 这个还真不是,曾经在项目中碰到过,发送几百个字节,第一次接收只有几十个字节的情况。[/quote] 这样啊,好吧,谢谢您,我还有个关于带宽的问题不知道您了解么? 我的虚拟机开了3个系统,分别运行了10个客户端,一个中继转发,10个服务器,1个客户端通过中继连接1个服务器,我让服务器同时开始传输200KB的文件,大小分成4096的数据块,发送间隔35ms 我的中继上传和下载带宽都设置的是1MiB/S,虚拟机之间延迟为0,但是我在中继用wireshark抓包发现他的上传和下载每秒的峰值只有0.5MiB,请问这个为什么不是1MiB?
轻箬笠 2020-02-26
  • 打赏
  • 举报
回复
引用 6 楼 beastsam 的回复:
[quote=引用 4 楼 轻箬笠 的回复:] recv和send的处理方式不太一样。一般我们都是循环接收的,以保证数据的完整性。所以通行的办法是弄个固定长度的报文头,报文头中带报文长度数据,然后循环接收,直到长度满足要求为止。最简单的报文头就是,前4个字节(即int)就代表了报文的长度。
这个难道不是应该是tcp层处理的么?我理解tcp分片,但是不应该是系统接收完所有的4096分片后才把数据返回给recv里的buf么?而且我看很多教程说循环接受是为了防止发送的数据超过recv里buf的长度,但是我这个并没有超过啊?[/quote] 这个还真不是,曾经在项目中碰到过,发送几百个字节,第一次接收只有几十个字节的情况。
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 8 楼 qybao 的回复:
[quote=引用 5 楼 beastsam 的回复:] TCP分片这个我知道,但是按理来说recv的工作原理难道不是等接收够4096所有的分片包后才把数据返回给recv里的buf么?而且buf长度是足够接收的啊?多次接收的这个我理解是recv中设置的buf长度不够,所以需要循环多次接收,不知道我这么理解问题在哪
通道流一般是有缓存的,它会等到缓存满了就往buf送,这个缓存不一定就是4096(也就是不一定会等到4096才往buf送),所以正确的做法就是循环接收,因为你不能保证一次就接收完整。[/quote] 您说的应该没错,因为当我把虚拟机的通信延迟设置成0之后,就没问题了,看来还即使recv的buf足够,但还是需要循环接收。
qybao 2020-02-26
  • 打赏
  • 举报
回复
引用 5 楼 beastsam 的回复:
TCP分片这个我知道,但是按理来说recv的工作原理难道不是等接收够4096所有的分片包后才把数据返回给recv里的buf么?而且buf长度是足够接收的啊?多次接收的这个我理解是recv中设置的buf长度不够,所以需要循环多次接收,不知道我这么理解问题在哪

通道流一般是有缓存的,它会等到缓存满了就往buf送,这个缓存不一定就是4096(也就是不一定会等到4096才往buf送),所以正确的做法就是循环接收,因为你不能保证一次就接收完整。
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 3 楼 xian_wwq 的回复:
tcp是基于流的,不是发100,就一定收100 有可能收了95个,然后又收了5 和网络状况没有直接关联, 所以tcp数据接收,进行“拼包”(一次发送多次接收)、“分包”(多次发送一次接收)是基本操作。
对啊,但是tcp难道不是应该在底层等待接收完所有的4096分片的包之后才把数据返回给recv里的buf么?
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 4 楼 轻箬笠 的回复:
recv和send的处理方式不太一样。一般我们都是循环接收的,以保证数据的完整性。所以通行的办法是弄个固定长度的报文头,报文头中带报文长度数据,然后循环接收,直到长度满足要求为止。最简单的报文头就是,前4个字节(即int)就代表了报文的长度。
这个难道不是应该是tcp层处理的么?我理解tcp分片,但是不应该是系统接收完所有的4096分片后才把数据返回给recv里的buf么?而且我看很多教程说循环接受是为了防止发送的数据超过recv里buf的长度,但是我这个并没有超过啊?
beastsam 2020-02-26
  • 打赏
  • 举报
回复
引用 1 楼 qybao 的回复:
一个包不一定能送完数据,可能送多个包 所以你应该用循环接收,直到byte_num为-1才算接受完。每次接收到的数据自己保存,最后再拼装成一个完整的数据 int byte_num = 0, offset = 0; char data[4096] = {0}; while(byte_num != -1) { //多次接收直到没有数据 byte_num = recv(server_sock_fd, buf_rec, 4096, 0); memcpy(data+offset, buf_rec, byte_num); //把每次接收的数据保存 offset = byte_num; }
TCP分片这个我知道,但是按理来说recv的工作原理难道不是等接收够4096所有的分片包后才把数据返回给recv里的buf么?而且buf长度是足够接收的啊?多次接收的这个我理解是recv中设置的buf长度不够,所以需要循环多次接收,不知道我这么理解问题在哪
轻箬笠 2020-02-26
  • 打赏
  • 举报
回复
recv和send的处理方式不太一样。一般我们都是循环接收的,以保证数据的完整性。所以通行的办法是弄个固定长度的报文头,报文头中带报文长度数据,然后循环接收,直到长度满足要求为止。最简单的报文头就是,前4个字节(即int)就代表了报文的长度。

70,037

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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