介绍
在网络通讯中,Linux系统为每一个socket创建了接收缓冲区与发送缓冲区,对于TCP协议来说,这两个缓冲区是必须的.应用程序在调用send
/recv
函数时,Linux内核会把数据从应用进程拷贝到socket的发送缓冲区中,应用程序在调用recv
/read
函数时,内核把接收缓冲区中的数据拷贝到应用进程的接收缓冲区中.
我们也可以查看socke缓冲区的大小:
int bufsize=0; socket_t optlen=sizeof(bufsize); getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&bufsize,(socklen_t*)&optlen); //获取发送缓冲区大小 cout<<"send buffer size:"<<bufsize<<endl; //打印 getsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufsize,(socklen_t*)&optlen); //获取接收缓冲区大小 cout<<"recv buffer size:"<<bufsize<<endl; //打印
一些思考
- send函数会阻塞吗
会,如果发送端的发送缓冲区与接收端的接收缓冲区已满,那么send函数会阻塞,直到缓冲区有空闲位置。 - 向socket发送数据后,如果关闭socket,对端会接收到数据吗
会,因为socket缓冲区是双向的,发送端和接收端都会缓冲数据
Nagle算法
前言
在TCP协议中,无论发送多少数据,都要在数据前面加上协议头,同时,对方收到数据后,也需要回复ACK表示确认。为了尽可能的利用网络带宽,TCP希望每次都能够以MSS(Maximum Segment Size,最大报文长度)的数据块来发送数据。
Nagle算法的目的
Nagle算法就是为了尽可能发送大块的数据,避免网络中充斥着小数据块。
Nagle算法的工作原理
Nagle算法的定义是:任意时刻,最多只能有一个未被确认的小段,小段是指小于MSS的数据块,未被确认是指一个数据块发送出去后,没有收到对端回复的ACK。
举个例子:发送端调用send()函数将一个int型数据(称之为A数据块)写入到socket中,A数据块会被马上发送到接收端,接着,发送端又调用send()函数写入一个int型数据(称之为B数据块),这时候,A块的ACK没有返回(已经存在了一个未被确认的小段),所以B块不会立即被发送,而是等A块的ACK返回之后(大概40ms)才发送。
ACK延迟机制
TCP协议中不仅仅有Nagle算法,还有一个ACK延迟机制:当接收端收到数据之后,并不会马上向发送端回复ACK,而是延迟40ms后再回复,它希望在40ms内接收端会向发送端回复应答数据,这样ACK就可以和应答数据一起发送,把ACK捎带过去。
Nagle的缺点与解决方案
如果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算法。
Tcp的分包与粘包
- 分包:tcp报文的大小缺省是1460字节,如果发送缓冲区中的数据超出了1460字节,那么Tcp将会拆分多个包来发送,如果接收方及时从接收缓冲区中取走了数据,看起来就像是接收了多个报文。
- 粘包:tcp接收到数据后,有序的放在缓冲区中,由于数据与数据之间不存在分隔符的说法,如果接收方没有及时的从缓冲区中取走数据,看起来就会和粘起来一样。
解决方案
为了解决上述出现的问题,我们对发送报文与接收报文的方式进行了修改。
- 发送端:先发送报文长度,再发送报文内容。
- 服务端:先接收报文长度,再接收报文内容。
结语
这一篇文章主要是为下一篇网络编程的服务端与客户端编写做一个引子,介绍一些有关网络编程的基本知识,因为下一篇文章将会给大家介绍在c++网络编程中有关服务端与客户端的编写,本文只做一些理论介绍,大家下篇见!