获取和设置影响套接口选项的函数:
getsockopt :获取套接口选项 setsockopt: 获取套接口选项 fcntl: 设置套接口为非阻塞I/O型信号驱动I/O型等 oictl 套接口选项 SO-KEEPALIVE SO-LINGER SE-RCVBUF 和 SO-SNDBUF SO-RCVWAT和 SO-SNDLOWAT SO-RCVTIMEO和 SO-SNDTIMEO SO-REUSEADDR和 SO-REUSPORT IP-TTL TCP-KEEPALIVE
--------------------------------------------------------------------------------
getsockopt 和 setsockopt
获得套接口选项: int getsockopt ( int sockfd, int level, int optname, void * optval, socklen_t *opteln ) 设置套接口选项: int setsockopt ( int sockfd, int level, int optname, const void * optval, socklen_t *opteln ) sockfd(套接字): 指向一个打开的套接口描述字 level:(级别): 指定选项代码的类型。 SOL_SOCKET: 基本套接口 IPPROTO_IP: IPv4套接口 IPPROTO_IPV6: IPv6套接口 IPPROTO_TCP: TCP套接口 optname(选项名): 选项名称 optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ } optlen(选项长度) :optval 的大小 返回值:标志打开或关闭某个特征的二进制选项
--------------------------------------------------------------------------------
检查套接口选项的程序
输出套接口的选项: 定义感兴趣的套接口选项 调用getsockopt 输出套接口选项 定义联合:不同的套接口选项有不同类型
union val { //套接口选项可能有的5个类型分别作为一个成员: int i_val; long l_val; char c_val[10]; struct linger linger_val; struct timeval timeval_val; //struct {int S; int uS} } val;
//函数原型(prototype),这些函数用于输出套接口选项的值 static char *sock_str_flag(union val *, int); //静态函数,只可在本文件中被调用 static char *sock_str_int(union val *, int); static char *sock_str_linger(union val *, int); static char *sock_str_timeval(union val *, int); //定义结构sock_opts, 其中包含了获得或输出套接口选项的所有信息 struct sock_opts { char *opt_str; //字符名称 int opt_level; //级别 int opt_name; //名称 char *(*opt_val_str)(union val *, int); //函数指针,用于输出, }
//定义结构数组并初始化 struct sock_opts sock_opts[ ] = { //全局变量数组才可以初始化 "SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, sock_str_flag, "SO_DEBUG", SOL_SOCKET, SO_DEBUG, sock_str_flag, #ifdef SO_REUSEPORT //编译时用的宏定义 "SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, sock_str_flag, #else //没有这个选项 "SO_REUSEPORT", 0, 0, NULL, //NULL表示没有定义 #endif "SO_TYPE", SOL_SOCKET, SO_TYPE, sock_str_int, "IP_TTL", IPPROTO_IP, IP_TTL, sock_str_int, "TCP_MAXSEG", IPPROTO_TCP,TCP_MAXSEG, sock_str_int, NULL, { /*结束标志 */} 0, 0, NULL }; 源程序: int main(int argc, char **argv) { int fd, len; struct sock_opts *ptr; //结构类型 fd = Socket(AF_INET, SOCK_STREAM, 0); //获得套接字 for (ptr = sock_opts; ptr->opt_str != NULL; ptr++) { //从第一个选项到最后一个 printf(“%s: ”, ptr->opt_str); //输出字符名 if (ptr->opt_val_str == NULL) //没有定义的情况 printf("(undefined)\n"); else { len = sizeof(val); //获得套接口选项 if (getsockopt(fd, ptr->opt_level, ptr->opt_name, val, len) == -1) { //返回值为1,函数调用失败 err_ret("getsockopt error");} //输出选项的缺省值 else printf("default = %s\n", (*ptr->opt_val_str)(&val, len)); } } //End of for loop exit(0); }
static char strres[128]; //静态变量,在函数调用后保留原值 static char * sock_str_flag(union val *ptr, int len) { if (len != sizeof(int)) //长度不相符 snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len); else //转换字符串 snprintf(strres, sizeof(strres),"%s", (ptr->i_val == 0) ? "off" : "on"); return(strres); }
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
基本套接口选项
SO_KEEPALIVE
检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。 设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导
致以下三种情况: 对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。 对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭。 对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到 一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错 误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不 可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。
SO_RCVBUF和SO_SNDBUF
每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由应用进程来读。 TCP:TCP通告另一端的窗口大小。 TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制,如果对方无视窗口大小而发出了超过宙口大小的数据,则接 收方TCP将丢弃它。 UDP:当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP是没有 流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。
SO_LINGER 指定函数CLOSE对面相连接的协议如何操作——当由数据残留在套接口发送缓冲区时的处理 LINGER结构 struct linger { int l_onoff; // 0=off, nonzero=on int l_linger; //linger time in seconds };
SO_RCVLOWAT 和SO_SNDLOWAT
每个套接口都有一个接收低潮限度和一个发送低潮限度。它们是函数selectt使用的, 接收低潮限度是让select返回“可读”而在套接口接收缓冲区中必须有的数据总量。 ——对于一个TCP或UDP套接口,此值缺省为1。发送低潮限度是让select返回“可写” 而在套接口发送缓冲区中必须有的可用空间。对于TCP套接口,此值常缺省为2048。 对于UDP使用低潮限度, 由于其发送缓冲区中可用空间的字节数是从不变化的,只要 UDP套接口发送缓冲区大小大于套接口的低潮限度,这样的UDP套接口就总是可写的。 UDP没有发送缓冲区,只有发送缓冲区的大小。
--------------------------------------------------------------------------------
TCP 套接口选项
TCP_KEEPALIVE
指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间。缺省值至少必须
为7200秒,即2小时。此选项仅在SO_KEPALIVEE套接口选项打开时才有效。
TCP_MAXSEG
获取或设置TCP连接的最大分节大小(MSS)。返回值是我们的TCP发送给另一端的最大 数据量,它常常就是由另一端用SYN分节通告的MSS,除非我们的TCP选择使用一个比 对方通告的MSS小些的值。如果此值在套接口连接之前取得,则返回值为未从另·—端 收到Mss选项的情况下所用的缺省值。小于此返回值的信可能真正用在连接上,因为譬 如说使用时间戳选项的话,它在每个分节上占用12字节的TCP选项容量。我们的TcP将 发送的每个分节的最大数据量也可在连接存活期内改变,但前提是TCP要支持路径MTU 发现功能。如果到对方的路径改变了,此值可上下调整。
--------------------------------------------------------------------------------
例程序:
//获得发送缓冲区大小和MSS大小,设置发送缓冲区大小
//获得发送缓冲区大小和MSS大小 #include "unp.h" #include /* for TCP_MAXSEG */ int main(int argc, char **argv) { int sockfd, rcvbuf, mss; socklen_t len; struct sockaddr_in servaddr; if (argc != 2) err_quit("usage: rcvbuf "); sockfd = Socket(AF_INET, SOCK_STREAM, 0); len = sizeof(rcvbuf); Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, rcvbuf, len); len = sizeof(mss); Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len); printf("defaults: SO_RCVBUF = %d, MSS = %d\n", rcvbuf, mss); bzero(servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(13); /* daytime server */ Inet_pton(AF_INET, argv[1], servaddr.sin_addr); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); len = sizeof(rcvbuf); Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len); len = sizeof(mss); Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len); printf("after connect: SO_RCVBUF = %d, MSS = %d\n", rcvbuf, mss); exit(0); }
//设置发送缓冲区大小 #include "unp.h" #include /* for TCP_MAXSEG value */ int main(int argc, char **argv) { int sockfd, mss, sendbuff; socklen_t optlen; float kk; sockfd = Socket(AF_INET, SOCK_STREAM, 0); /* Fetch and print the TCP maximum segment size. */ optlen = sizeof(mss); sendbuff =2048; Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, sizeof(sendbuff)); optlen = sizeof(sendbuff); Getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, &optlen); printf("After send buffer size = %d\n", sendbuff); exit(0); }