setsockopt的常用选项

一、函数原型
#include <sys/types.h >
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • sockfd:标识一个套接口的描述字

  • level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6

  • optname:层次为SOL_SOCKET时可设置的选项,而有部分选项需在listen/connect调用前设置才有效,选项如下:

    选项名称说明数据类型
    O_BROADCAST允许发送广播数据int
    O_DEBUG允许调试int
    O_DONTROUTE不查找路由int
    O_ERROR获得套接字错误int
    O_KEEPALIVE保持连接int
    O_LINGER延迟关闭连接struct linger
    O_OOBINLINE带外数据放入正常数据流int
    O_RCVBUF接收缓冲区大小int
    O_SNDBUF发送缓冲区大小int
    O_RCVLOWAT接收缓冲区下限int
    O_SNDLOWAT发送缓冲区下限int
    O_RCVTIMEO接收超时struct timeval
    O_SNDTIMEO发送超时struct timeval
    O_REUSERADDR允许重用本地地址和端口int
    O_TYPE获得套接字类型int
    O_BSDCOMPAT与BSD系统兼容int
  • optval:指针,指向存放选项值的缓冲区

  • optlen:optval缓冲区长度

  • 返回值:成功执行时,返回0。失败返回-1,errno被设为以下的某个值:

    • EBADF:sock不是有效的文件描述词
    • EFAULT:optval指向的内存并非有效的进程空间
    • EINVAL:在调用setsockopt()时,optlen无效
    • ENOPROTOOPT:指定的协议层不能识别选项
    • ENOTSOCK:sock描述的不是套接字
二、使用场景
  1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用close(socket)(一般不会立即关闭,而是需要经历TIME_WAIT的过程)后想继续重用该socket:

     int reuseAddrOn = 1;
     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddrOn, sizeof(reuseAddrOn));
    

    注意:必须在调用bind函数之前设置SO_REUSEADDR选项。

    TIME_WAIT是什么?
    UNP书上说,TIME_WAIT状态有两个存在的理由:
    (1)可靠地实现TCP全双工连接的终止;
    (2)允许老的重复分节在网络中消逝。
    在这里插入图片描述
    (1)如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。
    (2)如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。
    回到我们的问题,由于我并不是正常地经过四次断开的方式中断连接,所以并不会存在最后一个ACK的问题。不过,最终的服务器版本,还是不要设置为端口可复用的。

  2. 如果要已经处于连接状态的soket在调用close(socket)后强制关闭,不经历TIME_WAIT的过程:

     int reuseAddrOn = 0;
     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseAddrOn, sizeof(reuseAddrOn));
    
  3. 在send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,而设置收发时限:

     int nNetTimeout=1000; 	//1秒
     //发送时限
     setsockopt(sockfd, SOL_S0CKET, SO_SNDTIMEO, (char *)&nNetTimeout, sizeof(nNetTimeout));
     //接收时限
     setsockopt(sockfd, SOL_S0CKET, SO_RCVTIMEO, (char *)&nNetTimeout, sizeof(nNetTimeout));
    
  4. 在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步),系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:

     //接收缓冲区
     int recvBufLen = 32*1024;	 //设置为32K
     setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&recvBufLen, sizeof(recvBufLen));
     
     //发送缓冲区
     int sendBufLen = 32*1024; 	//设置为32K
     setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&sendBufLen, sizeof(sendBufLen));
    

    注意:
    (1). 并不是说你设置的多大,系统就会设置多大,系统一般会将我们设置的缓冲区大小加倍,并且不得小于tcp的接收缓冲区和发送缓冲区设置的默认最小值。
    (2). TCP有发送缓冲区和接收缓冲区,但是UDP因为是不可靠的,它没有确认重传机制,不保存应用程序数据的副本,所以是没有发送缓冲区的,但是UDP有接收缓冲区。

  5. 如果在发送数据时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

     int nZero = 0;
     setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&nZero, sizeof(nZero));
    
  6. 同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

     int nZero = 0;
     setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&nZero, sizeof(nZero));
    
  7. 一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

     int bBroadcast = 1;
     setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bBroadcast));
    
  8. 设置存活检测

     int opt = 1;
     setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
    

    用于TCP socket检测/保持网络连接,这个选项被设置后,2小时以内双方没有数据交互,将发送一个探测数据段到对方,此时可能出现以下情况:
    a. 得到对方正确响应,继续等待下一次2小时超时;
    b. 收到RST数据段,返回错误ECONNRESET;
    c. 对方无响应,多次发送探测数据段直到超时返回错误ETIMEOUT;
    d. 选项值类型:int,0—不能发送;1—可以发送,默认值为0;对setsockopt和getsockopt有效;

  9. 延迟接收
    实际上就是当接收到第一个数据之后,才会创建连接。对于像http这类非交互式的服务器,这个很有意义,可以防御空连接攻击。

     int val = 5;
     setsockopt(sockfd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
    

    打开这个功能后,内核在val时间之类还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。
    从三次握手上讲,就是设置这个状态之后,就算完成了三次握手,服务器socket状态也不是ESTABLISHED,而依然是 SYN_RCVD,不会去接收数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值