设置和影响套接口选项的方法有
1.getsockopt和setsockopt
2.fcntl
3.ioctl
getsockopt和setsockopt
这两个方法仅适用于套接字
有两种基本类型的套接口选项:打开或关闭某个特性的二进制标志,取得并返回我们可以设置或检验的特定值的选项,标有标志的列指明是否为标志选项,对于这些项,0表示关闭标志,非0表示打开标志。
并不是所有的系统的套接字都支持所有的选项,必要时候自行验证一番。
套接字的不支持分为两种
1.未实现相关的定义,比如SO_REUSEPORT宏未定义
2.定义了名字,但并不支持,比如SO_REUSEPORT虽然定义,调用getsockopt却会报错。
针对这两种不支持的测试
1.#ifdef #else #endif
2.getsockopt返回失败或者取回的值为空。
套接口选项应该什么时候设置
抽取redis的一段代码来示例
//监听套接字接收accept事件的处理器
acceptTcpHandler
{
//accept的包裹函数
anetTcpAccept
//设置socket的选项:设置非阻塞;禁用Nagle算法;设置keepalive
acceptCommonHandler
}
某些设置要更早一些,比如SO_RCVBUF和SO_SNDBUF,需要在服务器的监听套接口上设置,或者在未connect的客户端套接口上设置。
套接口基本选项
基本选项是协议无关的,但有些选项只能应用于特定类型的套接口中
SO_BROADCAST
此选项禁止或使能进程发送套接口广播的能力。只有数据报套接口支持广播,还必须是支持广播的网络。你不可能在一个点对点网络上发起广播。
SO_DEBUG
仅有TCP支持,当打开时,内核对TCP在此套接口上的发送和接收消息都做详细跟踪,这些信息保存在内核缓冲区,由程序trpt进行检查。
SO_ERROR
当套接口发生错误时,套接口名为so_error的变量被设置为标准的UNIX Exxx值的一个,它称为套接字的待处理错误。
内核可以通过select等多路复用I/O立即通知调用进程,进程可以通过SO_ERROR选项获取so_error的值,so_error随后由内核恢复为0.
当进程调用read且没有数据返回时,如果so_error为非0值,则read返回-1且errno设置为so_error的值,so_error随后复位为0.如果此套接口上有数据排队,则read返回那些数据,而不是返回错误条件。
如果在进程调用write时,so_error为非0值,则write返回-1值,并且errno设置为so_error的值,so_error也被复位为0.
SO_KEEPALIVE
给TCP一个保存存活选项以后,如果2个小时内此套接口的任何一方都没有数据交换。TCP就自动给对方发送一个保持存活探测分节,这是一个必须回复的TCP分节,它会导致以下三种情况:
1.对方回复一个正常的ACK,应用进程得不到任何通知(因为一切正常)。
2.对方以RST响应,它告诉本地TCP,对方已崩溃并且重新启动,套接口的待处理错误被设置成ECONNRESET,套接口本身被关闭。
3.对方对保持存活分节无任何响应,源自berkeley的TCP将发送另外8个分节,每隔75s一个,试图得到响应,如果对TCP的探测存活分节没有响应,套接口的错误被设置成ETIMEOUT,套接字本身被关闭。但如果收到某个ICMP的错误,则返回相应的错误。
主要是检测对方主机是否崩溃,如果对方进程崩溃,它的TCP将跨连接发送一个FIN,这可以通过多路复用检测到。
所有的TCP探测分节均无响应,也不意味这连接已中断,也有可能是某个中间路由器暂时性的有问题,也就有可能关闭一个有效连接。
这个选项一般由服务器使用,因为如果客户主机崩溃,服务器将永远不知道,并继续等待不会到来的输入,这称为半打开连接,保持存活消息将检测这些连接并终止它们。
当TCP对端发生某些事件必须检测时的一些手段
SO_LINGER选项
只是函数close对面向连接的套接字如何操作,缺省设置是立即返回,如果有数据残留在套接口发送缓冲区,系统试着将这些数据发送给对端。
SO_LINGER可以使我们改变这些缺省操作。SO_LINGER的主要作用是去掉TIME_WAIT状态,使重启服务器进程是够重新绑定端口。作为替代,我们可以在bind的时候使用SO_REUSERADDR选项。
SO_LINGER的另一个作用是,告诉我们发送的数据和FIN已由对方TCP确认,它并不能告诉我们对方应用进程是否读取了数据,如果不设置该选项,我们连对方是否接收了数据都不知道。
close的缺省操作,立即返回
设置了SO_LINGER,并且l_linger为正值时的close
让客户知道服务器已读其数据的一个方法是:调用SHUT_WR的shutdown,并等待对方close连接的本地端。
使客户端知道服务器已读其数据的另一个方式是:使用应用级的ACK。
SO_RCVBUF和SO_SNDBUF
每一个套接口都有一个接收缓冲区和一个发送缓冲区
接收缓冲区被TCP和UDP用来保存收到的数据,一直保存到应用程序来读。对于TCP来说,接收缓冲区的大小就是告知对方的窗口大小。TCP套接口接收缓冲区不可能溢出,因为对方不可能发出超出所通告窗口大小的数据,这就是TCP的流量控制。如果对方无视窗口大小而发出了超出窗口大小的数据,则接收方TCP将丢弃它。对于UDP来说,如果接收到的数据装不进套接口缓冲区时,将会丢弃它。UDP是没有流量控制的,快的发送方可以轻易的淹没慢的接受者。这导致接收方的UDP丢弃数据报。
当设置TCP套接字缓冲区大小时,函数调用顺序非常重要。TCP的窗口规模是在建立连接时用SYN交换来的,对于客户来说,必须在connect之前设置,对于服务器来说,意味着调用listen之前必须给监听套接字设置缓存区大小。给已连接的套接字设置该选项可能没有任何影响。
TCP套接字缓冲区的大小至少是MSS的三倍,并且必须是MSS的偶数倍,因为客户TCP必须为客户数据保存一份拷贝,直至接收到对端的确认。
fpathconf获取套接字缓冲区的上限。
SO_RCV_LOWAT和SO_SNDLOWAT
每个套接口也都有一个发送低潮限度和一个接收低潮限度。
接收低潮限度是在多路复用API返回可读时,套接口接收缓冲区至少有的数据量。对于TCP和UDP,缺省值为1.
发送低潮限度是在多路复用API返回可写时,套接口发送缓冲区至少有的可用空间。对于TCP套接字,其值常缺省为2048.UDP没有发送缓冲区,只有发送缓冲区大小,对于UDP来说,只要发送缓冲区大小大于套接口的低潮限度,UDP套接口就总是可写的。
SO_RCVTIMEO和SO_SNDTIMEO
给套接字设置一个接收和发送超时,缺省超时是禁止的。
接收超时影响:read、readv、recvfrom、recv和recvmsg
发送超时影响:write、writev、send、sendto和sendmsg。
SO_REUSEADDR和SO_REUSEPORT
SO_REUSEADDR通常为以下4个目的服务
1.SO_REUSEADDR允许启动一个监听服务器并绑定其众所周知端口,哪怕以前建立的将此端口用作本地端口的连接仍然存在。
2.SO_REUSERADDR允许在一个端口上启动同一个服务器的不同实例,只要每个实例绑定一个不同的本地IP地址即可。
3.SO_REUSEADDR允许单个进程捆绑同一端口到多个套接字,只要每个套接字捆绑不同的本地IP端口。
4.SO_REUSEADDR还允许完全重复的绑定,当一个IP和端口捆绑到一个套接字时,允许把同一个IP和端口捆绑到另一个套接字。一般来说,这个特性仅在支持多播的系统上才有,而且仅对UDP套接字(TCP不支持多播)。这个作用并不是每个系统都支持。
TCP套接口选项
TCP_KEEPALIVE
由于SO_KEEPALIVE的超时时间是2小时,并且难以修改,在linux上通常还会设置TCP_KEEPALIVE,设置TCP_KEEPALIVE仅在SO_KEEPALIVE套接口选项打开时才有效。
TCP_NODELAY
禁止TCP的nagle算法,缺省时,该功能是打开的。
Nagle算法:如果给定TCP连接上有待确认的分组,则直到现有数据被确认前,不往TCP上发送任何小分组。小分组即小于MSS的分组。如果可能,TCP总是发送最大大小的分组;Nagle算法的目的是防止TCP连接上有待确认的小分组存在。
Nagle算法常与另一个TCP算法一起使用,延滞ACK算法,此算法导致在接收到数据时不立即发送ACK。而是由TCP等待一小段时间(50-200ms)才发出ACK,所希望的是这段时间TCP有数据回复给对端,这样ACK可以捎带在这些数据上。又是就节省了一个TCP分节。
不适合Nagle和延滞ACK的客户端:以若干小数据分片发送单个逻辑请求给服务器的客户。对这类客户的修复办法是
1.使用writev合并小数据分片为完整的逻辑请求,然后发送
2.拷贝属于单个逻辑的多个小数据分片到一个缓冲区,然后完整发送
3.设置TCP_NODELAY并继续调用write两次。
fcntl函数
fcntl、ioctl和路由套接口操作小结
从表中来看,posix推荐使用fcntl