TCP缓存(C++)


系统为每个 socket 创建了发送缓冲区和接收缓冲区,应用程序调用 send()/write()函数发送数据的
时候,内核把数据从应用进程拷贝 socket 的发送缓冲区中;应用程序调用 recv()/read()函数接收数据的时候,内核把数据从 socket 的接收缓冲区拷贝应用进程中。
发送数据即把数据放入发送缓冲区中。
接收数据即从接收缓冲区中取数据。

查看 socket 缓存的大小的代码:
 

int bufsize = 0;
socklen_t optlen = sizeof(bufsize);
getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, &optlen); // 获取发送缓冲区的大小。
cout << "send bufsize=" << bufsize << endl;
getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen); // 获取接收缓冲区的大小。
cout << "recv bufsize=" << bufsize << endl;

演示:

问题:
1)send()函数有可能会阻塞吗? 如果自己的发送缓冲区和对端的接收缓冲区都满了,会阻塞,在高并发的场景有可能发生。
2)向 socket 中写入数据后,如果关闭了 socket,对端还能接收到数据吗?可以,事件和数据都已经储存在接收缓存区。

Nagle 算法
在 TCP 协议中,无论发送多少数据,都要在数据前面加上协议头,同时,对方收到数据后,也需要回复 ACK 表示确认。为了尽可能的利用网络带宽,TCP 希望每次都能够以 MSS(Maximum SegmentSize,最大报文长度)的数据块来发送数据。
Nagle 算法就是为了尽可能发送大块的数据,避免网络中充斥着小数据块。
Nagle 算法的定义是:任意时刻,最多只能有一个未被确认的小段,小段是指小于 MSS 的数据
块,未被确认是指一个数据块发送出去后,没有收到对端回复的 ACK。

举个例子:发送端调用 send()函数将一个 int 型数据(称之为 A 数据块)写入到 socket 中,A 数
据块会被马上发送到接收端,接着,发送端又调用 send()函数写入一个 int 型数据(称之为 B 数据
块),这时候,A 块的 ACK 没有返回(已经存在了一个未被确认的小段),所以 B 块不会立即被发送,而是等 A 块的 ACK 返回之后(大概 40ms)才发送。
TCP 协议中不仅仅有 Nagle 算法,还有一个 ACK 延迟机制:当接收端收到数据之后,并不会马上
向发送端回复 ACK,而是延迟 40ms 后再回复,它希望在 40ms 内接收端会向发送端回复应答数据,这样 ACK 就可以和应答数据一起发送,把 ACK 捎带过去。
如果 TCP 连接的一端启用了 Nagle 算法,另一端启用了 ACK 延时机制,而发送的数据包又比较
小,则可能会出现这样的情况:发送端在等待上一个包的 ACK,而接收端正好延迟了此 ACK,那么这个正要被发送的包就会延迟 40ms。
解决方案
开启 TCP_NODELAY 选项,这个选项的作用就是禁用 Nagle 算法。

#include <netinet/tcp.h> // 注意,要包含这个头文件。
int opt = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,&opt,sizeof(opt));

对时效要求很高的系统,例如联机游戏、证券交易,一般会禁用 Nagle 算法。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Linux C++中,可以通过`setsockopt`函数设置套接字的接收缓存大小。以下是一个示例代码: ```c++ // 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sockfd == -1) { perror("Create socket failed"); return 1; } // 设置接收缓存大小为2048字节 int recvBufSize = 2048; if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBufSize, sizeof(recvBufSize)) == -1) { perror("Set socket receive buffer size failed"); close(sockfd); return 1; } // 连接到目标主机 struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr("目标主机IP地址"); serverAddr.sin_port = htons(目标主机端口号); if (connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) { perror("Connect failed"); close(sockfd); return 1; } // 接收数据 char recvBuf[2048]; ssize_t recvLen = recv(sockfd, recvBuf, sizeof(recvBuf), 0); if (recvLen == -1) { perror("Receive data failed"); close(sockfd); return 1; } // 关闭套接字 close(sockfd); ``` 在上面的代码中,我们通过调用`setsockopt`函数来设置套接字的接收缓存大小,然后通过调用`recv`函数来接收数据。需要注意的是,设置套接字的接收缓存大小可能会影响网络性能,因此应该根据具体情况进行适当的调整。 另外,Linux系统也提供了`sysctl`命令来动态调整网络参数,包括接收缓存大小。您可以使用`sysctl`命令来查询和修改系统参数,例如: ```sh # 查询当前的TCP接收缓存大小 sysctl net.ipv4.tcp_rmem # 修改TCP接收缓存大小为2048, 4096, 8192字节 sysctl -w net.ipv4.tcp_rmem="2048 4096 8192" ``` 需要注意的是,修改系统参数可能会影响整个系统的性能和稳定性,因此应该根据具体情况进行适当的调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值