通用套接字选项

在[url=http://aisxyz.iteye.com/admin/blogs/2405269]获取和设置套接字选项[/url]一节中对可获取和设置的套接字选项进行了简单描述,这一节将对其中的通用套接字选项作更深入的介绍。既然是“通用”,那就表明这些选项是协议无关的(不过其中有些选项只能应用到某些特定类型的套接字中,比如 SO_BROADCAST 只能应用于数据报套接字)。
* SO_BROADCAST
本选项开启或禁止进程发送广播消息的能力。只有数据报套接字支持广播,并且还必须是在支持广播消息的网络上(如以太网、令牌环网等),而不能再点对点链路上进行广播。当目的地址是一个广播地址但本选项没有设置时,将会放缓 EACCES 错误。
* SO_DEBUG
该选项仅由 TCP 支持。当给一个 TCP 套接字开启该选项时,内核将为 TCP 在该套接字上发送和接收的所有分组保留详细的跟踪信息。这些信息保存在内核的某个环形缓冲区中,并可使用 trpt 程序进行检查。
* SO_DONTROUTE
本选项规定外出的分组将绕过底层协议的正常路由机制。比如 IPv4 的外出分组会被定向到由其目的地址的网络和子网部分确定的本地接口。如果这样的本地接口无法由其目的地址确定(譬如目的主机不在一个点对点链路的另一端,也不在一个共享的网络上),那么将返回 ENETUNREACH 错误。给函数 send、sendto 或 sendmsg 使用 MSG_DONTROUTE 标志也能在个别的数据报上取得于本选项相同的效果。路由守护进程(routed 和 gated)经常使用本选项来绕过路由表(路由表不正确时),以强制将分组从特定的接口送出。
* SO_ERROR
该选项可以获取但不能设置。当一个套接字上发生错误时,源自 Berkeley 的内核中的协议模块会将该套接字的名为 so_error 的变量设为标准的 Unix_Exxx 值中的一个,称之为该套接字的待处理错误。内核能够以下面两种方式之一立即通知进程这个错误。
(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。进程调用 write 时同调用 read。
* SO_KEEPALIVE
给一个 TCP 套接字设置保持存活选项后,如果 2 小时内在该套接字的任一方向上都没有数据交换,TCP 就自动给对端发送一个保持存活探测分节,它会导致以下三种情况之一。
(1)对端以期望的 ACK 响应,此时应用进程收不到通知。
(2)对端以 RST 响应,表明对端已崩溃且已重新启动。该套接字的待处理错误被置为 ECONNRESET,套接字本身则被关闭。
(3)对端对保持存活探测分节没有任何响应。此时源自 Berkeley 的 TCP 将另外发送至多 8 个探测分节。
如果根本没有对 TCP 的探测分节的响应,则该套接字的待处理错误就被置为 ETIMEOUT,套接字本身则被关闭。而如果该套接字收到一个 ICMP 错误作为某个探测分节的响应,那就返回相应的错误,套接字本身也被关闭。常见的 ICMP 错误是“主机不可达”,这种情况下待处理错误会被置为 EHOSTUNREACH。
下图是对一个 TCP 连接的另一端发生某些事件时可以采用的各种检测方法的汇总。
[img]http://dl2.iteye.com/upload/attachment/0128/3469/3b553fcb-4579-3894-8e8c-fda4a4131edf.png[/img]
* SO_LINGER
本选项指定 close 函数对面向连接的协议如何操作。默认是 close 立即返回,但如果有数据残留在套接字发送缓冲区中,系统将试着把这些数据发送给对端。使用本选项可以改变这个默认设置,它要求在用户进程与内核间传递如下结构。

#include <sys/socket.h>

struct linger{
int l_onoff; // 0xoff, nonzero = on
int l_linger; // linger time, POSIX specifies units as seconds
};

对 setsockopt 的调用将根据其中两个结构成员的值形成下列 3 种情形之一。
(1)如果 l_onoff 为 0,则关闭本选项,忽略 l_linger,默认设置生效,close 立即返回。
(2)如果 l_onoff 非 0 且 l_linger 为 0,那么当 close 某个连接时 TCP 将终止该连接。这表示 TCP 将丢弃保留在套接字发送缓冲区中的数据,并发送一个 RST 个对端,而没有通常的四分组连接终止序列,也就没有了 TCP 的 TIME_WAIT 状态,因而可能产生在 2MSL 秒内创建的该连接的另一个化身收到旧的重复分节的情况。这种情形下 SCTP 也通过发送一个 ABORT 块给对端而终止性地关闭关联。
(3)如果 l_onoff 和 l_linger 都不为 0 时,那么当套接字关闭时内核将拖延一段时间,直到发送缓冲区中的数据发送完毕且均被确认或延滞时间到为止。不过非阻塞型套接字不会等待 close 完成,即使延滞时间非 0 也一样。如果使用该特性,则检查 close 的返回值是必要的。因为如果在数据发送完并被确认前延迟时间到的话,close 将把 errno 置为 EWOULDBLOCK,且套接字发送缓冲区中的任何残留数据都将被丢弃。此处需要注意的是:close 的成功返回只是表明先前发送的数据已由对端确认,但不能告知对端是否已读取数据。要知道对端已读取其排队数据的一个方法是调用把第二个参数设为 SHUT_WR 的 shutdown 函数,并后跟一个 read 调用来读取直到接收了对端的 FIN 为止。另一个方法是在调用 close 之前调用 read 来读取直到某个标志数据出现,这一般被称为应用 ACK,下图展示了可能的分组交换过程。
[img]http://dl2.iteye.com/upload/attachment/0128/3471/99044ec4-a9a7-346d-ac07-c269d3d48339.png[/img]
下表汇总了对 shutdown 的两种可能调用和对 close 的三种可能调用,以及它们对 TCP 套接字的影响。
[img]http://dl2.iteye.com/upload/attachment/0128/3473/169d96e6-840d-3902-a23a-d990eb243979.png[/img]
* SO_OOBINLINE
当本选项开启时,带外数据将被留在正常的输入队列中(即在线留存)。这种情况下接收函数的 MSG_OOB 标志不能用来读带外数据。
* SO_RCVBUF 和 SO_SNDBUF
每个套接字都有一个发送缓冲区和接收缓冲区。接收缓冲区被 TCP、UDP 和 SCTP 用来保存接收到的数据,直到应用进程读取。对于 TCP 来说,套接字接收缓冲区可用空间的大小限定了 TCP 通告对端的窗口大小。由于 TCP 的流量控制,TCP 套接字接收缓冲区不可能溢出,超过通告窗口大小的数据将被本端 TCP 丢弃。而对于没有流量控制的 UDP 来说,当接收到的数据报装不进套接字接收缓冲区时,该数据报将会被丢弃。这表示较快的发送端可以很容易地淹没较慢的接收端,导致接收端的 UDP 丢弃数据报。
这两个选项可以用来改变这两个缓冲区的默认大小。当设置 TCP 套接字接收缓冲区的大小时,函数调用的顺序很重要。因为 TCP 的窗口规模选项是在建立连接时用 SYN 分节与对端互换得到的,这意味着 SO_RCVBUF 选项必须在调用 connect 和 listen 之前设置。给已连接套接字设置该选项对于可能存在的窗口规模选项没有任何影响,因为 accept 要到 TCP 的三路握手完成才会创建并返回已连接套接字。套接字缓冲区的大小总是由新创建的已连接套接字从监听套接字继承而来。
TCP 套接字缓冲区的大小至少应该是相应连接的 MSS 值的四倍,这一点的依据是 TCP 快速恢复算法的工作机制。TCP 发送端使用三个重复的确认来检测某个分节是否丢失。发现某个分节丢失后,接收端将给接收到的每个分节发送一个重复的确认。因此如果窗口大小不足以存放四个这样的分节,就不可能连发三个重复的确认,从而无法激活快速恢复算法。另外,为避免潜在的缓冲区空间浪费,TCP 套接字缓冲区大小还应该是相应连接的 MSS 值的偶数倍,因为套接字缓冲区中 MSS 整数倍大小以外的空间不会被使用。有些实现会替应用进程处理这个细节,它们会在连接建立后向上舍入套接字缓冲区大小,这也是应在建立连接前设置这两个套接字选项的一个原因。
* SO_RCVLOWAT 和 SO_SNDLOWAT
每个套接字还有一个接收低水位标记和一个发送低水位标记,它们由 select 函数使用。接收低水位标记是让 select 返回“可读”时套接字接收缓冲区中所需的数据量,而发送低水位标记是让 select 返回“可写”时套接字发送缓冲区中所需的可用空间。
* SO_RCVTIMEO 和 SO_SNDTIMEO
这两个选项允许给套接字的发送和接收设置一个超时值。
* SO_REUSEADDR 和 SO_REUSEPORT
SO_REUSEADDR 能起到以下 4 个不同的功用。
(1)它允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。这个条件通常是这样碰到的:
a)启动一个监听服务器;
b)连接请求到达,派生一个子进程来处理这个客户;
c)监听服务器终止,但子进程继续为现有连接上的客户提供服务;
d)重启监听服务器。
默认情况下,当监听服务器在步骤 d 通过调用 socket、bind 和 listen 重启时,由于它试图捆绑一个现有连接(即正由早先派生的那个子进程处理着的连接)上的端口,所以 bind 会失败。但如果服务器在 socket 和 bind 两个调用之间设置了 SO_REUSEADDR,那么 bind 就会成功。一般所有的 TCP 服务器都应该指定本套接字选项,以允许服务器在这种情形下被重新启动。
(2)SO_REUSEADDR 允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地 IP 地址即可。在捆绑的地址中有通配地址时,为了防止把恶意的服务器捆绑到某个系统服务正在使用的 IP 地址和端口上,造成合法请求被截取,应该把执行通配地址捆绑的服务器进程放在最后一个启动。
(3)SO_REUSEADDR 允许单个进程捆绑同一端口到多个套接字上。
(4)SO_REUSEADDR 允许完全重复的捆绑:当一个 IP 地址和端口已绑定到某个套接字时,如果传输协议支持,则同样的 IP 地址和端口还可以捆绑到另一个套接字上。不过本特性一般仅支持 UDP 套接字。当用于多播时,本特性允许在同一个主机上同时运行同一个应用程序的多个副本。
SO_REUSEPORT 选项并非所有系统都支持,它主要引入了以下语义:
(1)本选项允许完全重复的捆绑,不过只有在想要捆绑同一 IP 地址和端口的每个套接字都指定了本套接字选项时才行。
(2)如果捆绑的 IP 地址是一个多播地址,则本选项与 SO_REUSEADDR 被认为是等效的。
* SO_TYPE
本选项返回套接字的类型(如 SOCK_STREAM 等),它通常由启动时继承了套接字的进程使用。
* SO_USELOOPBACK
本选项仅用于路由域(AF_ROUTE)的套接字。对于这些套接字,它的默认设置为打开(这是唯一一个默认值为打开的 SO_xxx 二元套接字选项)。当本选项开启时,相应套接字将接收在其上发送的任何数据报的一个副本(禁止这些环回副本的另一个方法是调用 shutdown,并设置它的第二个参数为 SHUT_RD)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值