本章主要学习getsocketopt()/setsocketopt()、ioctl() 、fcntl()
getsocketopt()/setsocketopt()
getsockopt():获得套接字选项设置情况
setsockopt():设置套接字选项的函数
#include <sys/types.h> #include <sys/socket.h> int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
s:将要获取或者设置的套接字描述符,可以通过socket()函数获得
level:选项所在协议层。
optname:选项名。
optval:操作的内存缓冲区
optlen:第4个参数的长度
返回:成功:0,失败:-1
SOL_SOCKET 协议族选项
SO_BROADCAST
广播默认禁止,需要时打开,广播使用UDP套接字,其含义是允许将数据发送到子网网络的每个主机上。
#define YES 1 /*设置有效*/ #define NO 0 /*设置无效*/ int s; /*套接字变量*/ int err; /*错误值*/ int optval = YES; /*将选项设置为有效*/ s = socket(AF_INET, SOCK_DGRAM,0); /*建立套接字*/ err = setsockopt( /*设置选项*/ s, SOL_SOCKET, SO_BROADCAST, /*SO_BROADCAST 选项*/ &optval, /*值为有效*/ sizeof(optval)); /*值的长度*/ if(err) /*判断是否发生错误*/ perror("setsockopt"); /*打印错误信息*/
SO_DEBUG
表示允许调试套接字,打开此选项时,Linux内核程序跟踪在此套接字上的发送和接收的数据,并将调试信息放到一个环形缓冲区中。仅支持TCP
#define YES 1 /*设置有效*/ #define NO 0 /*设置无效*/ int s; /*套接字变量*/ int err; /*错误值*/ int optval = YES; /*将选项设置为有效*/ s = socket(AF_INET,SOCK_STREAM,0); /*建立一个TCP套接字*/ err = setsockopt( /*设置选项*/ s, SOL_SOCKET, SO_DEBUG, /*SO_BROADCAST 选项*/ &optval, /*值为有效*/ sizeof(optval)); /*值的长度*/
SO_DONTROUTE
选项设置后,网络数据不通过网关发送,只能发送给直接连接的主机或者用一个子网内的主机。
SO_KEEPALIVE
用于设置TCP连接的保持,设置SO_KEEPALIVE 选项后,如果在两个小时内没有数据通信时, TCP会自动发送一个活动探测数据报文,对方必须对此进行响应;若收到ACK,两小时后再发,若收到RST,对方重启或崩溃,连接断开;若无响应,间隔75秒再发,还无响应就断开
#define YES 1 /*设置有效*/ #define NO 0 /*设置无效*/ int s; /*套接字变量*/ int err; /*错误值*/ int optval = YES; /*将选项设置为有效*/ s = socket(AF_INET, SOCK_DGRAM,0); /*建立套接字*/ err = setsockopt( /*设置选项*/ s, SOL_SOCKET, SO_KEEPALIVE, /*SO_KEEPALIVE选项*/ &optval, /*值为有效*/ sizeof(optval)); /*值的长度*/ if(err) /*判断是否发生错误*/ perror("setsockopt"); /*打印错误信息*/
SO_LINGER
用于设置TCP连接关闭时的行为方式,就是关闭流式连接时,发送缓冲区中的数据如何处理。
选项SO_LINGER的操作是通过结构struct linger来进行的
struct linger { int l_onoff; /*是否设置延时关闭,0:否,1:时*/ int l_linger; /*超时时间*/ };
#define YES 1 /*设置有效*/ #define NO 0 /*设置无效*/ int s; /*套接字变量*/ int err; /*错误值*/ struct linger optval; /*建立一个1inger类型的套接字选项变量*/ optval.l_onoff = YES; /*设置linger生效*/ optval.l_linger = 60; /*linger 超时时间为60s*/ s = socket(AF_INET, SOCK_DGRAM,0); /*建立套接字*/ err = setsockopt( /*设置选项*/ s, SOL_SOCKET, SO_LINGER, /*SO LINGER 选项*/ &optval, /*值为有效*/ sizeof(optval)); /*值的长度*/ if(err) /*判断是否发生错误*/ perror("setsockopt"); /*打印错误信息*/
以上,在调用close()函数后,在60s之内允许发送数据,当缓冲区内的数据发送完毕后,会正常关闭;不能正常发送数据则返回错误。
SO_OOBINLINE
带外数据不再通过另外的通道获得,带外数据放入正常数据流,
#define YES 1 /*设置有效*/ #define NO 0 /*设置无效*/ int s; /*套接字变量*/ int err; /*错误值*/ int optval = YES; /*将选项设置为有效*/ s = socket(AF_INET, SOCK_DGRAM,0); /*建立套接字*/ err = setsockopt( /*设置选项*/ s, SOL_SOCKET, SO_OOBINLINE, /*SO_OOBINLINE选项*/ &optval, /*值为有效*/ sizeof(optval)); /*值的长度*/ if(err) /*判断是否发生错误*/ perror("setsockopt"); /*打印错误信息*/
在设置选项之后,带外数据就会与一般数据一起接收。在这种方式下,所接收的越界数据与通常数据相同,即增加了带宽。
SO_RCVBUF 和 SO_SNDBUF
用于操作发送缓冲区和接收缓冲区的大小
在UDP连接中,由于它是无状态连接,发送缓冲区在数据通过网络设备发送后就可以丢弃,不用保存。而接收缓冲区则需要保存数据直到应用程序读取,由于UDP没有流量控制,当缓冲区过小时,发送端局部时间内会产生爆发性数据传输,由于接收端来不及读取数据,很容易造成缓冲区溢出,将原来的数据覆盖,淹没接收端。因此使用UDP连接时,需要将接收的缓冲区调整为比较大的值,
在TCP 连接中,接收缓冲区大小就是滑动窗口大小,而滑动窗口的协商是在建立连接时通过SYN获得的。对于客户端,接收缓冲区的大小要在connect()函数调用之前进行设置;对于服务器,需要在listen()之前进行设置接收缓冲区的大小,
例子
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <assert.h> int main(int argc,char **argv) { int err = -1; /*返回值*/ int s = -1; /*socket描述符*/ int snd_size = 0; /*发送缓冲区大小*/ int rcv_size = 0; /*接收缓冲区大小*/ socklen_t optlen; /*选项值长度*/ /* 建立一个TCP套接字*/ s = socket(PF_INET,SOCK_STREAM,0); if( s == -1){ printf("建立套接字错误\n"); return -1; } /* * 先读取缓冲区设置的情况 * 获得原始发送缓冲区大小 */ optlen = sizeof(snd_size); err = getsockopt(s, SOL_SOCKET, SO_SNDBUF,&snd_size, &optlen); if(err){ printf("获取发送缓冲区大小错误\n"); } /* * 打印原始缓冲区设置情况 */ printf("发送缓冲区原始大小为: %d 字节\n",snd_size); printf("接收缓冲区原始大小为: %d 字节\n",rcv_size); /* * 获得原始接收缓冲区大小 */ optlen = sizeof(rcv_size); err = getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); if(err){ printf("获取接收缓冲区大小错误\n"); } /* * 设置发送缓冲区大小 */ snd_size = 4096; /*发送缓冲区大小为8K*/ optlen = sizeof(snd_size); err = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &snd_size, optlen); if(err){ printf("设置发送缓冲区大小错误\n"); } /* * 设置接收缓冲区大小 */ rcv_size = 8192; /*接收缓冲区大小为8K*/