概述
获取和设置影响套接字选项方法
a. getsockopt
和setsockopt
b. fcntl
c. ioctl
按以下分类介绍:通用,IPv4
,TCP
.
fcntl
是把套接字设置为非阻塞式I/O
型或信号驱动式I/O
型及设置套接字属主的POSIX
方法
getsockopt和setsockopt函数
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen);
int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen);
参数1
需指向一个打开的套接字描述符
参数2
,类别(通用/IPv4
/IPv6
/TCP
/SCTP
/…)
参数4
,执行设置/接收,设置值
参数5
,参数4
指向对象长度
套接字选项
如:SO_DEBUG,SO_DONTROUTE,SO_KEEPALIVE,SO_LINGER,SO_OOBINLINE,SO_RCVBUF,SO_RCVLOWAT,SO_SNDBUF,SO_SNDLOWAT,TCP_MAXSEG,TCP_NODELAY
通用套接字选项
由内核中协议无关代码处理
SO_BROADCAST套接字选项
开启或禁止进程发广播消息能力,只有数据报套接字+在支持广播消息网络上(以太网,令牌环网等)才支持广播.
应用进程在发送广播数据报前需设置本套接字选项,故它能防止一个进程在其应用程序没设计成可广播时就发送广播数据报.如目的地址为一个广播地址但广播选项未设置,返回EACCES
.
SO_DEBUG套接字选项
本选项仅由TCP
支持,给一个TCP
套接字开启本选项时,内核将为TCP
在该套接字发送和接收的所有分组保留详细跟踪信息
SO_DONTROUTE套接字选项
规定外出的分组将绕过底层协议的正常路由机制,路由守护进程经常用本选项来绕过路由表以强制将分组从特定接口送出
SO_ERROR套接字选项
一个套接字上发生错误时,源自Berkeley
内核的协议模块将该套接字的名为so_error
变量设为标准Unix Exx
x中的一个,称其为待处理错误.
内核能以以下方式之一通知进程此错误
(1). 如进程阻塞在对该套接字的select
调用上,
无论检查读/写,均返回并设置
(2). 如进程使用信号驱动式I/O模型,则给进程或进程组产生一个SIGIO
信号
进程可通过访问SO_ERROR
套接字选项获取so_error
值,由getsockopt
返回的整数值,即为该套接字的待处理错误.so_error
随后由内核复位.read
时,如无数据可读+
出错,返回-1
,errno
被设置,so_error
被复位.write
时,返回-1
,errno
被设置,so_error
被复位.
SO_KEEPALIVE套接字选项
给一个TCP
套接字设置保持存活选项后,如两小时内,在该套接字任一方向无数据交换,TCP
自动给对端发一个保持存活探测分节,对端必须响应.
(1). 对端以期望的ACK
响应
(2). 对端以RST
响应,
表示对端已崩溃,套接字待处理错误置为ECONNRESET
,套接字关闭
(3). 无响应
再发探测,一段时间后仍无响应,则放弃(认为线路故障).套接字本身被关闭.
如该套接字收到一个ICMP
错误作为某个探测分节的响应,则返回相应的错误,套接字关闭.
本选项经常由服务器使用,在其相连客户断线/崩溃时,提供了一种识别方法.以便服务端关闭半开连接.
SO_LINGER套接字选项
#include <sys/socket.h>
struct linger
{
int l_onoff;
int l_linger;
};
(1). 如l_onoff
为0
,则关闭本选项.l_linger
值被忽略,close
立即返回
(2). 如l_onoff
为非0
,且l_linger
为0
close
某连接时,TCP
将中止该连接,即TCP
将丢弃保留在套接字发送缓冲区的任何数据,发一个RST
给对端
(3). 如l_onoff
为非0
值且l_linger
也为非0
值
则当套接字关闭时,close
阻塞,内核将拖延一段时间,直到
a. 所有数据都已发送完,且均被对方确认
b. 延滞时间到
c. EWOULDBLOCK
,发送缓冲区残留数据被丢弃
设置SO_LINGER
套接字选项后,close
的成功返回告诉我们先前发送的数据(和FIN
)已由对端TCP确认
不能告诉我们对端应用进程是否已读取数据,如不设该选项,连对端TCP
是否确认了数据也不知
shutdown(参数2
为SHUT_WR
)+ read
,可让客户知道服务器已读取其数据
当关闭连接的本地端,根据所调函数(close
或shutdown
)及是否设置了SO_LINGER
可在3
个不同时机返回
a. close
立即返回,不等待
b. close
拖延到接收了FIN
的ACK
才返回
c. shutdown+read
,等到接收了对端的FIN
才返回
获取服务器已读取数据,也可用应用级ACK
.服务器每读取客户数据后,发回1
字节ACK
,客户阻塞读取.
函数 | 说明 |
---|---|
shutdown,SHUT_RD | 接收缓冲区数据被丢弃,后续接收的数据被丢弃 |
shutdown,SHUT_WR | 发送缓冲区内容发到对端,后跟FIN |
close,l_onoff=0 | 发送缓冲区内容被发到对端.如描述符引用计数为0,发送FIN,接收缓冲区内容被丢弃. |
close,l_onoff=1,l_linger=0 | 发送缓冲区内容被丢弃,如描述符引用计数变为0,发RST.连接状态变为CLOSED. |
close,l_onoff=1,l_linger=1 | 发送缓冲区内容被发到对端,如引用计数变为0,发FIN.接收缓冲区内容被丢弃,如在变为CLOSED前延滞时间到,EWOULDBLOCK |
通过shutdown
可以仅仅关闭发送,仅仅关闭接收.
通过close
必然是同时关闭发送和接收.
对发送的关闭,允许立即关闭,延迟关闭.立即关闭的意思是将发送缓存区内容丢弃,且立即发RST给对端.延迟关闭的意思是将发送缓存区现有内容有序发送完毕,再发FIN
.
对接收的关闭,总是立即的.
SO_OOBINLINE套接字选项
选项开启时,带外数据将被留在正常的输入队列中,此时,接收函数的MSG_OOB
标记不能读带外数据
SO_RCVBUF和SO_SNDBUF套接字选项
接收缓冲区被TCP/UDP/SCTP
用来保存接收到的数据,直到应用读取.对TCP
,套接字接收缓冲区可用空间大小限定了TCP
通告对端的窗口大小,流量控制:对端不允许发超过本端通告窗口大小的数据,即使发了超过窗口大小数据,超出部分被丢弃.
设置TCP
套接字接收缓冲区大小时,TCP
窗口规模选项是在建立连接时用SYN
分节与对端互换得到.对客户,SO_RCVBUF
选项需在调用connect
前设置.对服务器,该选项需在调listen
前给监听套接字设置.给已连接套接字设置该选项对可能存在的窗口规模选项无任何影响.
TCP
套接字缓冲区大小至少应是相应连接的MSS
值的4
倍,对单向数据传输,说"套接字缓冲区大小"时,指的是发送端主机上的套接字发送缓冲区大小,和接收端主机上的套接字接收缓冲区大小.
对双向,每端均指接收/发送缓冲区大小,套接字缓冲区中MSS
整数倍外的空间不会被使用.
管道的容量称为带宽-延迟积,将带宽(bit/s
)和RTT
(秒)相乘,再将结果由位转换为字节计算得到.
SO_RCVLOWAT和SO_SNDLOWAT套接字选项
这两个套接字选项允许我们修改套接字的接收/发送低水位标记,接收低水位标记是让select
返回"可读"时套接字接收缓冲区所需的数据量.对TCP/UDP/SCTP
,默认为1
.发送低水位标记是让select
返回"可写"时套接字发送缓冲区所需的可用空间,对TCP
,默认值通常为2048
.
SO_RCVTIMEO和SO_SNDTIMEO套接字选项
允许给套接字的接收和发送设置一个超时值:getsockopt/setsockopt(timeval)
.
接受超时:read/readv/recv/recvfrom/recvmsg
发送超时:write/writev/send/sendto/sendmsg
SO_REUSEADDR和SO_REUSEPORT套接字选项
SO_REUSEADDR
可起到以下4
个不同功用
(1). SO_REUSEADDR
允许启动一个监听服务器并捆绑众所周知端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在
发生条件举例:
a. 启动一监听服务器
b. 连接请求到达,派生一个子进程处理此客户
c. 监听服务器终止,子进程继续为现有连接上的客户提供服务
d. 重启监听服务器(采用现有连接上端口)
(2). SO_REUSEADDR
允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP
地址
a. 在所有TCP
服务器程序中,调bind
之前设置SO_REUSEADDR
套接字选项
b. 编写一个可在同一时刻在同一主机上运行多次的多播应用时,设置SO_REUSEADDR
套接字选项,将所参加多播组的地址作为本地IP
地址捆绑
SO_TYPE套接字选项
返回套接字的类型,如SOCK_STREAM/SOCK_DGRAM/...
SO_USELOOPBACK套接字选项
本选项仅用于路由域(AF_ROUTE
)的套接字.默认设置为打开,开启时,相应套接字将接收在其上发送的任何数据报的一个副本
IPv4套接字选项
由IPv4
处理,级别为IPPROTO_IP
IP_HDRINCL套接字选项
如是给一个原始IP套接字设置的,则必须为所有在该原始套接字上发送的数据报构造自己的IP
首部
一般,在原始套接字上发送的数据报其IP首部是由内核构造的,有些应用需构造自己的IP
首部
开启时,构造完整的IP
首部.下列情况例外,
a. IP
总是计算并存储IP首部校验和
b. 如将IP
标识字段置为0
,内核将设置该字段
c. 如源IP
地址是INADDR_ANY
,IP
将把它设置为外出接口的主IP
地址
d. 如何设置IP
选项取决于实现
有些实现取出预先使用IP_OPTIONS
选项设置的任何IP
选项,把其添加到我们构造的首部,其他实现要求,我们亲自在首部指定期望的IP
选项.
e. IP
首部中有些字段必须以主机字节序填写,有些字段必须以网络字节序添加
IP_OPTIONS套接字选项
允许在IP
首部设置IP
选项
IP_RECVDSTADDR套接字选项
导致所收到UDP
数据报的目的IP
地址由recvmsg
函数作为辅助数据返回
IP_RECVIF套接字选项
导致所收到UDP
数据报的接收接口索引由recvmsg
函数作为辅助数据返回
IP_TOS套接字选项
允许我们为TCP/UDP/SCTP
套接字设置IP
首部中的服务类型字段
如给本选项调getsockopt
,则用于放入外出IP
数据报首部的DSCP
和ECN
字段中的TOS
当前值将返回
应用进程可把DSCP
设为用户和网络业务供应商预先协商号的某个值,以便接受预定的服务
把IP_TOS
设置成<netinet/ip.h>
中定义的某个常值的应用应改为使用由用户指定的某个DSCP
值
IP_TTL套接字选项
可用本选项设置或获取系统用在从某给定套接字发送的单播分组上的默认TTL
值,多播TTL
用IP_MULTICAST_TTL
设置
调用getsockopt
返回的是系统将用于外出数据报的字段的默认值
套接字选项
级别为IPPROTO_TCP
TCP_MAXSEG套接字选项
允许我们获取或设置TCP
连接的最大分节大小(MSS
),返回值是我们的TCP
可发送给对端的最大数据量,通常是由对端使用SYN
分节通告的MSS
.除非TCP
选择使用一个比对端通告的MSS
小些的值,如该值在相应套接字的连接建立前取得,则返回值是未从对端收到MSS
选项的情况下所用的默认值
如用上时间戳选项,则实际用于连接中最大分节大小可能小于本套接字选项的返回值,时间戳选项在每个分节要占用12
字节的TCP
选项容量,如TCP
支持路径MTU
发现功能,则它将发送的每个分节的最大数据量还可能在连接存活期内改变,如到对端的路径变动,值会调整,可由应用设置,一旦连接建立,本选项的值就是对端通告的MSS
选项值,TCP
不能发送超过该值的分节
TCP_NODELAY套接字选项
开启时,将禁止TCP
的Nagle
算法.默认下算法启动
算法:
如某个给定连接上有待确认数据,则原本应作为用户写操作之响应在该连接上立即发送相应小分组的行为就不会发生,直到数据被确认.小分组是小于MSS
的任何分组,TCP
总是尽可能发送最大大小分组,用于防止一个连接在任何时刻有多个小分组待确认
ACK
延滞算法:
使得TCP
在接收到数据后不立即发送ACK
,而是等一小段时间,才发送ACK
,TCP
期待在这一小段时间内自身有数据发送回对端,被延滞的ACK
可由这些数据捎带,省掉一个TCP
分节
对于其服务器不再相反方向产生数据以便携带ACK
的客户,ACK
延滞算法存问题
fcntl函数
fcntl
提供了与网络编程相关的如下特性
a. 非阻塞式I/O
通过F_SETFL
设置O_NONBLOCK
b. 信号驱动式I/O
用F_SETFL
设置O_ASYNC
文件状态标志
c. F_SETOWN
允许我们指定用于接收SIGIO
和SIGURG
信号的套接字属主
d. F_GETOWN
返回套接字当前属主
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
// 使用fcntl开启非阻塞式I/O
int flags;
if((flags = fcntl(fd, F_GETFL, 0)) < 0)
err_sys("error");
// 设置代码
flags |= O_NONBLOCK;
// 取消代码
flags &= ~O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) < 0)
err_sys("errot");
SIGIO/SIGURG
仅在已用F_SETOWN
给套接字设了属主(进程/进程组)才产生,F_GETOWN
返回属主(进程/进程组).