一、套接字选项介绍
二、套接字选项
三、常用套接字选项介绍
套接字选项介绍
在Linux网络编程中,有时需要设置地址复用,允许发送广播包,将主机加入某个多播组,设置发送与接收缓冲区的大小,设置发送与接收的超时时间,将套接字绑定到某个接口上,发送TCP探测包查看客户端是否保持连接等,这些都需要对套接字选项进行设置.而对套接字选项进行操作的主要有以下两个函数,setsockopt与getsockopt.这两个函数不仅能够操作套接字层,而且能够操作IP层与TCP层。
SOCKET选项设置方式
•intgetsockopt(ints,intlevel,intoptname,void*optval,socklen_t *optlen);
•参数:
•s-套接字描述符
•level-选项所在的层,主要有SOL_SOCKET(套接字层),IPPROTO_IP(IP选项),IPPROTO_TCP(TCP选项).
•optname-选项名
•optval-所操作的缓冲区,即参数缓冲区
•optlen-传入参数的最大长度的指针(返回参数的实际长度)
•返回值:
•函数执行成功返回0,失败返回-1.
•
•intsetsockopt(ints,intlevel,intoptname,const void*optval,socklen_toptlen);
•参数:
•s-套接字描述符
•level-套接字所在的层
•optname-套接字选项名
•optval-所操作的缓冲区指针
•optlen-所传入参数的实际长度
•返回值:
•函数执行成功返回0,失败返回-1.
套接字层选项
![]()
![]()
![]()
SO_RCVBUF SO_SNDBUF
•对于TCP来说接收缓冲区可用空间的大小限定了TCP通知对端的窗口大小。TCP接受缓冲区不可能溢出,TCP不允许对端发出超过本端所通告窗口大小的数据,这就是TCP的流量控制,如果对端无视窗口大小发出超过窗口大小的数据,TCP将丢弃它。对于UDP,UDP是没有流量控制的,较快的发送端将淹没较慢的接收端,导致数据报的丢失。
•每一个TCP套接字有一个发送缓冲区,SO_SNDBUF套接字选项来更改发送缓冲区的大小。应用层调用send/write时,内核从应用进程的缓冲区复制所有数据到所写套接字的发送缓冲区,如果该套接字的发送缓冲区容不下应用进程的所有数据,对于阻塞套接字,应用进程将投入睡眠直到所有数据复制到发送缓冲区,对于非阻塞套接字将直接返回已经复制到发送缓冲区的数据长度或者返回-1,errno=EAGAIN。对于UDP来说不存在真实的发送缓冲区,发送缓冲区大小表示写到该UDP数据报的大小上限,如果写入一个超过该值得数据报将返回EMSGSIZE。
•客户端必须在connect之前设置SO_SNDBUF SO_RCVBUF,对于服务器必须在listen之前设置。
SO_RCVTIMEO SO_SNDTIMEO
•这两个选项允许我们给套接字的接收和发送设置一个超时值,用于阻塞模式socket。默认这两个选项不开启。
•SO_SNDTIME影响:connect writewritev sendsendtosendmsg
•SO_RCVTIMEO影响:accept readreadvrecvrecvfromrecvmsg。
•超时将返回EAGAIN。
•设置方法:
•setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&t, sizeof(t));
•setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&t, sizeof(t));
SO_LINGER
本选项对TCP有效,指定close如何操作。默认情况下close立即返回,如果有数据残留在套接字发送缓冲区中,系统将尝试把这些数据发给对端。
struct linger {
intl_onoff; /* Linger active */
intl_linger; /* How long to linger for */
};
l_onoff为0,那么关闭本选项,和默认情况一样。
l_onoff非0且l_linger为0,close时发送一个RST给对端避免了TCP的TIME_WAIT状态。
l_onoff非0且l_linger非0,关闭套接字时内核将拖延一段时间
SO_KEEPALIVE设置
•
int
anetKeepAlive
(
int
fd
,
int
interval)
•
{
•
int
val
=1;
•
//
开启
keepalive
机制
•
if (
setsockopt
(
fd
,SOL_SOCKET, SO_KEEPALIVE, &
val
,
sizeof
(
val
)) == -1)
•
{
•
printf
("
setsockopt
SO_KEEPALIVE: %s",
strerror
(
errno
));
•
return -1;
•
}
val =interval;
•
if (
setsockopt
(
fd
,IPPROTO_TCP, TCP_KEEPIDLE, &
val
,
sizeof
(
val
)) < 0) {
•
printf
("
setsockopt
TCP_KEEPIDLE: %s\n",
strerror
(
errno
));
•
return -1;
•
}
•
val
= interval/3;
•
if (
val
== 0)
val
=1;
•
if (
setsockopt
(
fd
,IPPROTO_TCP, TCP_KEEPINTVL, &
val
,
sizeof
(
val
)) < 0) {
•
printf
("
setsockopt
TCP_KEEPINTVL: %s\n",
strerror
(
errno
));
•
return -1;
•
}
•
val
= 3;
•
if (
setsockopt
(
fd
,IPPROTO_TCP, TCP_KEEPCNT, &
val
,
sizeof
(
val
)) < 0) {
•
printf
("
setsockopt
TCP_KEEPCNT: %s\n",
strerror
(
errno
));
•
return -1;
•
}
•
return 0;
•
}
TCP_NODELAY
在网络拥塞控制领域,我们知道有一个非常有名的算法叫做Nagle算法(Nagle algorithm),这是使用它的发明人John Nagle的名字来命名的,John Nagle在1984年首次用这个算法来尝试解决福特汽车公司的网络拥塞问题。该问题的具体描述是:如果我们的应用程序一次产生1个字节的数据,而这个1个字节数据又以网络数据包的形式发送到远端服务器,那么就很容易导致网络由于太多的数据包而过载。针对上面提到的这个状况,Nagle算法的改进在于:如果发送端欲多次发送包含少量字符的数据包(一般情况下,后面统一称长度小于MSS的数据包为小包,与此相对,称长度等于MSS的数据包为大包,为了某些对比说明,还有中包,即长度比小包长,但又不足一个MSS的包),则发送端会先将第一个小包发送出去,而将后面到达的少量字符数据都缓存起来而不立即发送,直到收到接收端对前一个数据包报文段的ACK确认、或当前字符属于紧急数据,或者积攒到了一定数量的数据(比如缓存的字符数据已经达到数据包报文段的最大长度)等多种情况才将其组成一个较大的数据包发送出去。Nagle算法为了增加了网络的吞吐量而牺牲了响应时间体验,这在有些应用中是不合适的。
![]()
![SOCKET套接字选项_第1张图片](http://img.e-com-net.com/image/info8/c42612f69f524065869c011e103d03f5.jpg)
// int off = 1;
// setsockopt(fd,IPPROTO_TCP, TCP_NODELAY, (void*)&off, sizeof(off));
![SOCKET套接字选项_第2张图片](http://img.e-com-net.com/image/info8/2bb9b2642a1d485dba8aa071543e8191.jpg)
TCP_CORK ![]()
•TCP_CORK选项的功能类似于在发送数据管道出口处插入一个“塞子”,使得发送数据全部被阻塞,直到取消TCP_CORK选项(即拔去塞子)或被阻塞数据长度已超过MSS才将其发送出去或者超时。
int on = 1;
setsockopt(fd,IPPROTO_TCP, TCP_CORK, (void*)&on, sizeof(on));
TCP延时应答![]()
•
TCP_QUICKACK
用于让本端立即发送
ACK
,而不进行延迟确认。
•
需要注意的是,这个选项并不是持久的,之后还是有可能进入延迟确认模式的。
•
所以如果需要一直进行快速确认,要在每次调用接收函数后都进行选项设置。
•
•
int
quickack
=1; /*
启用快速确认,如果赋值为
0
表示使用延迟确认 *
/
•
setsockopt
(
fd
,SOL_TCP, TCP_QUICKACK, &
quickack
,
sizeof
(
quickack
));
#defineTCP_DELACK_MIN ((unsigned)(HZ/25)) /* minimal time to delay before sending an ACK*/
#defineTCP_DELACK_MAX ((unsigned)(HZ/5)) /* maximal time to delay before sending an ACK*/