网络编程学习——套接字选项
网络编程学习——套接字选项
thanatos_y 发表于2年前
网络编程学习——套接字选项
  • 发表于 2年前
  • 阅读 11
  • 收藏 0
  • 点赞 1
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

摘要: 通过《UNIX网络编程卷1:套接字联网API》学习网络编程

1 getsockopt和setsockopt函数

  这两个函数仅用于套接字。

#include <sys/socket.h>
/* Set socket FD's option OPTNAME at protocol level LEVEL
   to *OPTVAL (which is OPTLEN bytes long).
   Returns 0 on success, -1 for errors.  */
extern int setsockopt (int __fd, int __level, int __optname,
               const void *__optval, socklen_t __optlen) __THROW;

/* Set socket FD's option OPTNAME at protocol level LEVEL
   to *OPTVAL (which is OPTLEN bytes long).
   Returns 0 on success, -1 for errors.  */
extern int setsockopt (int __fd, int __level, int __optname,
               const void *__optval, socklen_t __optlen) __THROW;

  其中__fd必须指向一个打开的套接字描述符,__level(级别)指定系统中解释选项的代码或为通用套接字代码,或为某个特定于协议的代码(例如IPv4、IPv6、TCP或SCTP)。

  __optval是一个指向某个变量(*optval)的指针,setsockopt从*optval中取得选项待设置的新值,getsockopt则把已获取的选项当前值存放到*optval的大小由最后一个参数指定。它对于setsockopt是一个值参数,对于getsockopt是一个值-结果参数。

  图1-1和图1-2总结了可由getsockopt获取或由setsockopt设置的选项。其中的“数据类型”列给出了指针optval必须指向的每个选项的数据类型。我们用后跟一堆花括号的记法来表示一个结构,如linger{}就表示struct linger。

  套接字选项粗分为两大基本类型:以使启用或禁止某个特性的二元选项(称为标志选项),二是取得并返回我们可以设置或检查的特定值的选项(称为值选项)。标有“标志”的列指出一个选项是否为标志选项。当给这些选项调用getsockopt函数时,*optval是一个整数。*optval中返回的值为0表示相应选项被禁止,不为0表示相应项被启用。类似地,setsockopt函数需要一个不为0的*optval值来启用选项,一个为0的*optval值来禁止选项。如果“标志”列不含有“.”,那么相应选项用于在用户进程与系统之间传递所指定数据类型的值。

图1-1 套接字层和IP层的套接字选项总汇

图1-2 传输层的套接字选项总汇

 

2 通用套接字选项

 2.1 SO_BROADCAST套接字选项

  本选项开启或禁用进程发送广播消息的能力。只有数据报套接字支持广播,并且还必须是在支持广播消息的网络上(例如以太网、令牌环网等)。

 

 2.2 SO_DEBUG套接字选项

  本选项仅由TCP支持。当给一个TCP套接字开启本选项时,内核将为TCP在该套接字发送发送和接收的所有分组保留跟踪信息。这些信息保存在内核的某个环形缓冲区中,并可使用trpt程序进行检查。

 2.3 SO_DONTROUTE套接字选项

  本选项规定外出的分组将绕过底层协议的正常路由机制。路由守护进程(routed和gated)经常使用本选项来绕过路由表(路由表不正确的情况下),以强制将分组从特定接口送出。

 

 2.4 SO_ERROR套接字选项

  当一个套接字上发生错误时,so_error的变量设为标准的Unix Exxx值中的一个,我们称它为该套接字的待处理错误(pending error)。内核能够以下面两种方式之一立即通知进程这个错误。

  1. 如果进程阻塞在对该套接字的select调用上,那么无论是检查可读条件还是可写条件,select均返回并设置其中一个或所有两个条件。

  2. 如果进程使用信号驱动式I/O模型,那就给进程或进程组产生一个SIGIO信号。

  进程然后可以通过访问SO_ERROR套接字选项获取so_error的值。由getsockopt返回的整数值就是该套接字的待处理错误。so_error随后由内核复位为0。

  当进程调用read且没有数据返回时,如果so_error为非0值,那么read返回-1且errno被置为so_error的值。so_error随后被复位为0。这是我们遇到的第一个可以获取但不能设置的套接字选项。

 

 2.5 SO_KEEPALIVE套接字选项

  给一个TCP套接字设置保持存活(keep-alive)选项后,如果2小时内在该套接字的任一方向上都没有数据交换,TCP就自动给对端发送一个保持存活探测分节(keep-alive probe)。这是一个对端必须相应的TCP分节,它会导致三种情况之一。

  1. 对端以期望的ACK响应。应用进程得不到通知(因为一切正常)。在又经历过仍无动静的2小时候,TCP将发出另一个探测分节。

  2. 对端以RST响应,它告知本端TCP:对端已崩溃且重新启动。该套接字的待处理错误被置为ECONNRESET,套接字本身被关闭。

  3. 对端对保持存活探测分节没有任何响应。源自Berkely的TCP将另外发送8个探测分节,两两相隔75,企图得到一个响应。TCP在发出第一个探测分节后11分15秒内若没有得到任何响应则放弃。

  本选项的功用是检测对端主机是否崩溃或变得不可达。

 

 2.6 SO_LINGER套接字选项

  本选项指定close函数对面向连接的协议(例如TCP和SCTP,但不是UDP)如何操作。默认操作是close立即返回,但是如果有数据残留在套接字发送缓冲区中,系统将把这些数据发送给对端。

  SO_LINGER套接字选项使得我们可以改变这个默认设置。本选项要求在用户进程与内核间传递如下结构,它在头文件<bits/socket.h>中定义:

/* Structure used to manipulate the SO_LINGER option.  */
struct linger
  {
    int l_onoff;        /* Nonzero to linger on close.  */
    int l_linger;        /* Time to linger.  */
  };

  对setsocket的调用将根据其中两个结构成员的值形成下列3种情形之一。

  1. 如果l_onoff为0,那么关闭本选项。l_linger的值被忽略,先前讨论的TCP默认设置生效,即close立即返回。

  2. 如果l_onoff为非0且l_linger为0,那么当close某个连接时TCP将中止该连接。这就是说TCP将丢弃保留在套接字发送缓冲区的任何数据,并发送一个RST给对端,而没有通常的四分组连接终止序列。

  3. 如果l_onoff为非0值且l_linger也为非0值,那么当套接字关闭时内核将拖延一段时间。这就是说如果在套接字发送缓冲区中仍有残留数据,那么进程将被投入睡眠,直到所有数据都已发送完且均被对方确认或延滞时间到。

    下面图1-3是默认情况。

图1-3 close的默认操作:立即返回

  客户可以设置SO_LINGER套接字选项,指定一个正的延滞时间。这种情况下客户的close要看到它的数据和FIN已被服务器主机的TCP确认后才返回,如图1-4所示。

图1-4 设置SO_LINGER套接字选项且l_linger为正值时的close

  图1-5展示了当给SO_LINGER选项设置偏低的延滞时间值时可能发生的现象。

图1-5 设置SO_LINGER套接字选项且l_linger为偏小正值时的close

  让客户知道服务器已读其数据的一个方法是改为调用shutdown(并设置它的第二个参数为SHUT_WR)而不是调用close,并等待对端close连接的当地端(服务器端),如图1-6所示。

 

图1-6 用shutdown来获知对方已接收数据

  应用ACK如图1-7所示。

图1-7 应用ACK

  图1-8汇总了对shutdown的两种可能调用和对close的三种可能调用,以及它们对TCP套接字的影响。

图1-8 shutdown和SO_LINGER各种情况的总结

 

 2.7 SO_OOBINLINE套接字选项

  当本选项开启时,带外数据被留在正常的输入队列中(即在线留存)。这种情况下接收函数的MSG_OOB标志不能用来读带外数据。

 

 2.8 SO_REVBUF和SO_SNDBUF套接字选项

  每个套接字都有一个发送缓冲区和一个接收缓冲区。

  接收缓冲区被TCP、UDP和SCTP用来保存接收到的操作。直到由应用进程来读取。对于TCP来说,套接字接收缓冲区中可用空间的大小限定了TCP通告对端的窗口大小。TCP套接字接收缓冲区不可能溢出,因为不允许对端发出超过本端所通告窗口大小的数据。这就是TCP的流量控制,如果对端无视窗口大小而发出了超过该窗口大小的数据,本端TCP将丢弃它们。然而对于UDP来说,当接收到的数据报装不进套接字接收缓冲区时,该数据报就被丢弃。

  这两个套接字选项运行我们改变这两个缓冲区的默认大小。当设置TCP套接字接收缓冲区大小时,函数调用的顺序很重要。TCP套接字缓冲区的大小至少应该是相应连接的MSS值的四倍。为了避免潜在的缓冲区空间浪费,TCP套接字缓冲区大小还必须是相应连接的MSS值的偶数倍。

  在设置套接字缓冲区大小时另一个需考虑的问题涉及性能。图1-9展示了两个端点之间容量为8个分节的一个TCP连接(我们称其为管道)。

图1-9 8个分节容量的TCP连接

  我们在顶部给出4个数据分节,在底部给出4个ACK。即使管道中只有四个数据分节,客户也必须有至少8个分节容量的发送缓冲区,因为客户TCP必须为每个分节保留一个副本,直到接收到来自服务器的相应ACK。

  理解的重点在于全双工管道的概念、它的容量以及它们如何关系到连接两端的套接字缓冲区大小。管道的容量称为带宽-延迟积(bandwidth-delay product),它通过将带宽(bit/s)和RTT(秒)相乘,再将结果由位转换为字节计算得到。其中RTT可以很容易地使用ping程序测得。

  当带宽-延迟积超过TCP的最大正常窗口大小(65535字节)时,两端就得设置TCP长胖管道(long fat pipe)。

 

 2.9 SO_REVLOWAT和SO_SNDLOWAT套接字选项

  每个套接字还有一个接收低水位标记和一个发送低水位标记。它们由select函数使用。这两个套接字选项允许我们修改这两个低水位标记。

  接收低水位标记是让select返回“可读”时套接字接收缓冲区所需的数据量。对于TCP、UDP和SCTP套接字,其默认值为1。发送低水位标记是让select返回“可写”时套接字发送缓冲区中所需的可用空间。对于TCP而言默认值通常是2048。

 

 2.10 SO_REVTIMEO和SO_SNDTIMEO套接字选项

  这两个选项允许我们给套接字的接收和发送设置一个超时值。注意,访问它们的getsockopt和setsockopt函数的参数是指向timeval结构的指针,与select所用参数相同。这可让我们用秒数和微秒数来规定超时。我们通过设置其值为0s和0us来禁止超时。默认情况下这两个超时都使禁止的。

  接收超时影响5个输入函数:read、readv、recv、recvfrom和recvmsg。发送超时影响5个输出函数:write、writev、send、sendto和sendmsg。

 

 2.11 SO_REUSEADDR和SO_REUSEPORT套接字选项

  SO_REUSEADDR套接字选项能起到以下4个不同的功能。

  1. SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。这个条件通常是这样碰到的:

    (a)启动一个监听服务器。

    (b)连接请求到达,派生一个子进程来处理这个客户。

    (c)监听服务器终止,但子进程继续为现有连接上的客户提供服务。

    (d)重启监听服务器。

  2. SO_REUSEADDR允许在同一端口上启动同一服务的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。

  3. SO_REUSEADDR允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。

  4. SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以绑定到另一个套接字上。一般来说本特性仅支持UDP套接字。

  本特性用于多播时,允许在同一个主机上同时运行同一个应用程序的多个副本。

 2.12 SO_TYPE套接字选项

  本选项返回套接字的类型,返回的整数值是一个诸如SOCK_STREAM或SOCK_DGRAM之类的值。本选项通常由启动时继承了套接字的进程使用。

 

 2.12 SO_USELOOPBACK套接字选项

  本选项仅用于路由域(AF_ROUTE)的套接字。对于这些套接字,它的默认设置为打开(这是唯一一个默认值为打开而不是关闭的SO_xxx二元套接字选项)。当本选项开启时,相应套接字将接受其上发送的任何数据的一个副本。

 

3 IPv4套接字选项

  这些套接字选项是由IPv4处理,它们的级别为IPPROTO_IP。

 3.1 IP_HDRINCL套接字选项

  如果本选项是给一个原始IP套接字设置的,那么我们必须为所在该原始套接字上发送的数据报构造自己的IP首部。

 

 3.2 IP_OPTIONS套接字选项

  本选项的设置允许我们在IPv4首部中设置IP选项。这要求我们熟悉IP首部中IP选项的格式。

 

 3.3 IP_RECVDSTADDR套接字选项

  本套接字选项导致所收到UDP数据报的目的IP地址由recvmsg函数作为辅助数据返回。

 

 3.4 IP_RECVIF套接字选项

  本套接字选项导致所收到UDP数据报的接收接口索引由recvmsg函数作为辅助函数返回。

 3.5 IP_TOS套接字选项

  本套接字选项允许我们为TCP、UDP或SCTP套接字设置IP首部中的服务类型字段。

 

 3.6 IP_TLL套接字选项

  我们可以使用本选项设置或获取系统用在从某个给定套接字发送的单播分组上的默认TLL值。

4 ICMPv6套接字选项

  这个唯一的套接字选项由ICMPv6处理,它的级别为IPPROTO_ICMPV6。 

 ICPM6_FILTER套接字选项

  本选项允许我们获取或设置一个icmp6_filter结构,该结构指出256个可能的ICMPv6消息类型中哪些将经由某个原始套接字传递给所在进程。

5 IPv6套接字选项

  这些套接字选项由Ipv6处理,它们的级别为IPPROtO_IPV6。

 5.1 IPV6_CHECKSUM套接字选项

  本选项指定用户数据中校验和所在位置的字节偏移。

 5.2 IPV6_DONTFRAG套接字选项

  开启本选项将禁止为UDP套接字或原始套接字自动插入分片首部,外出分组中大小超过发送接口MTU的那些分组将被丢弃。

 5.3 IPV6_NEXTHOP套接字选项

  本选项将外出数据报的下一跳地址指定为一个套接字地址结构。这是一个特权操作。

 

 5.4 IPV6_PATHMTU套接字选项

  本选项不能被设置,只能被获取。获取本选项时,返回值为由路径MTU发现功能确定当前的MTU。

 

 5.5 IPV6_RECVDSTOPTS套接字选项

  开启本选项表明,任何接收到的IPv6目的地选项都将由recvmsg作为辅助数据返回。本选项默认是关闭的。

 

 5.6 IPV6_RECVHOPLIMIT套接字选项

  开启本选项表明,任何接收到的跳限字段都将由recvmsg作为辅助数据返回。本选项默认是关闭的。

 

 5.7 IPV6_RECVHOPOPTS套接字选项

  开启本选项表明,任何接收到的IPv6步跳选项都将由recvmsg作为辅助数据返回。本选项默认是关闭的。

 

 5.8 IPV6_RECVPATHMTU套接字选项

  开启本选项表明,某条路径的路径MTU在发生变化时将由recvmsg作为辅助数据返回。

 

 5.9 IPV6_RECVPKTINFO套接字选项

  开启本选项表明,接收到的IPv6数据报的以下两条信息将由recvmsg作为辅助数据返回:目的IPv6地址和到达接口索引。

 

 5.10 IPV6_RECVTHDR套接字选项

  开启本选项表明,接收到的IPv6路由首部将由recvmsg作为辅助数据返回。本选项默认是关闭的。

 

 5.11 IPV6_RECVTCLASS套接字选项

  开启本选项表明,接收到的流通类别(包含DSCP和ECN字段)将由recvmsg作为辅助数据返回。本选项默认是关闭的。

 

 5.12 IPV6_UNICAST_HOPS套接字选项

  本IPv6选项类似于IPv4的IP_TTL套接字选项,设置本选项会给在相应套接字上发送的外出数据报指定默认跳限,获取本选项会返回内核用于相应套接字的跳限值。

 5.13 IPV6_USE_MIN_MTU套接字选项

  把本选项设置为1表明,路径MTU发现功能不必执行,为避免分片,分组就使用IPv6的最小MTU发送。把本选项设置为-1表明,路径MTU发现功能仅对单播目的地执行,对于多播目的地就使用最小MTU。本选项默认是-1。

 5.14 IPV6_V6ONLY套接字选项

  在一个AF_INET6套接字上开启本选项将限制它只执行IPv6通信。本选项默认是关闭的。

 

 5.15 IPV6_XXX套接字选项

  大多数用于修改协议首部的IPv6选项假设:就UDP套接字而言,信息由recvmsg和sendmsg作为辅助数据在内核和应用进程之间传递;就TCP套接字而言,同样的信息改用getsockopt和setsockopt获取和设置。

6 TCP套接字选项

  TCP有两个套接字选项,它们的级别为IPPROTO_TCP。

 6.1 TCP_MAXSEG套接字选项

  本选项允许我们获取或设置TCP连接的最大分节大小(MSS)。返回值是我们的TCP可以发送给对端的最大数据量。

 6.2 TCP_NODELAY套接字选项

  开启本选项将禁止TCP的Nagle算法。默认情况下该算法是启动的。

  Nagle算法的目的在于减少广域网(WAN)上小分组的数目。该算法指出:如果某个给定连接上有待确认数据(outstanding data),那么原本应该作为用户写操作之响应的在该连接上立即发送相应小分组的行为就不会发生,直到现有数据被确认为止。这里“小”分组的定义就是小于MSS的任何分组。TCP总是尽可能地发送最大大小的分组,Nagle算法的目的在于防止一个连接在任何时刻有多个小分组待确认。

7 SCTP套接字选项

  数目较多的SCTP套接字选项反映出SCTP为应用程序开发人员提供了较细粒度的控制能力。它们的级别为IPPROTO_SCTP。

 7.1 SCTP_ADAPTION_LAYER套接字选项

  在关联初始化期间,任何一个断点都可能指定一个适配层指示(adaption layer indication)。这个指示是一个32位无符号整数,可由两端的应用进程用来协调任何本地应用适配层。

 7.2 SCTP_ASSOCINFO套接字选项

  本套接字选项可用于以下三个目的:(a)获取关于某个现有关联的信息,(b)改变某个已有关联的参数,(c)为未来的关联设置默认信息。

 

 7.3 SCTP_AUTOCLOSE套接字选项

  本选项允许我们获得或设置一个SCTP端点的自动关闭时间。自动关闭时间是一个SCTP关联在空闲时保持打开的秒数。默认是禁止的。

 

 7.4 SCTP_DEFAULT_SEND_PARAM套接字选项

  SCTP有许多可选的发送参数,它们通常作为辅助数据传递,或者由sctp_sendmsg函数使用。

 

 7.5 SCTP_DISABLE_FRAGMENTS套接字选项

  SCTP通常把太大而不适合置于单个SCTP分组中的用户消息分割成多个DATA块。

 

 7.6 SCTP_EVENTS套接字选项

  本套接字选项允许调用者获取、开启或禁用各种SCTP通知。

 

 7.7 SCTP_GET_PEER_ADDR_INFO套接字选项

  本选项仅用于获取某个给定对端地址的相关信息,包括拥赛窗口、平滑化后的RTT和MTU等。

 

 7.8 SCTP_I_WANT_MAPPED_V4_ADDR套接字选项

  这个标志套接字选项用于为AF_INET6类型套接字开启或禁止IPv4映射地址,其默认状态是开启的。

 

 7.9 SCTP_INITMSG套接字选项

  本套接字选项用于获取或设置某个SCTP套接字在发送INIT消息时所用的默认初始参数。

 

 7.10 SCTP_MAXBURST套接字选项

  本套接字选项允许应用进程获取或设置用于分组发送的最大猝发大小(maximum burst size)。

 

 7.11 SCTP_MAXSEG套接字选项

  本套接字选项允许应用进程获取或设置用于SCTP的最大片端大小(maximum fragment size)。

 

 7.12 SCTP_NODEELAY套接字选项

  开启本选项将禁止SCTP的Nagle算法。默认是关闭的。

 

 7.13 SCTP_PEER_ADDR_PARAMS套接字选项

  本套接字选项运行应用进程获取或设置关于某个关联的对端地址的各种参数。

 

 7.14 SCTP_PRIMARY_ADDR套接字选项

  本套接字选项用于获取或设置本地断点所用的主目的地址。

 

 7.15 SCTP_RTOINFO套接字选项

  本套接字选项用于获取或设置各种RTO信息,它们既可以是关于某个给定关联的设置,也可以是用于本地断点的默认设置。

 

 7.16 SCTP_SET_PEER_PRIMARY_ADDR套接字选项

  设置本套接字选项导致发送一个消息:请求对端把所指定的本地地址作为它的主目的地址。

 

 7.17 SCTP_STATUS套接字选项

  本套接字选项用于获取某个SCTP关联的状态。

8 fcntl函数

  与代表“file control”(文件控制)的名字相符,fcntl函数可执行各种描述符控制操作。图1-10汇总了由fcntl、ioctl和路由套接字执行的不同操作。

图1-10 fcntl、ioctl和路由套接字操作小结

  fcntl函数提供了与网络编程相关的如下特性。

  • 非阻塞式I/O。

  • 信号驱动式I/O。

  • F_SETOWN命令运行我们指定用于接收SIGIO和SIGURG信号的套接字属主(进程ID或进程组ID)。

#include <fcntl.h>
/* Do the file control operation described by CMD on FD.
   The remaining arguments are interpreted depending on CMD.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int fcntl (int __fd, int __cmd, ...);

 

 

 

 

 

 

 

 

 

 

共有 人打赏支持
粉丝 7
博文 90
码字总数 314854
×
thanatos_y
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: