自己动手实现socket的各种超时控制

在高并发的网络编程中,如果对socket的超时不加以控制,程序会死的很辛苦。

在这里,需要控制超时时间的地方基本上分为三个部分:连接超时、读超时以及写超时。

下面将按照上述超时问题依次解决之。


连接超时

处理连接超时有若干种方案,第一种比较trick, 使用socket本身的属性进行设置
可以使用该属性进行设置的原因是connect的内核实现( 源文件地址)采用了如下的实现方式:
 /*
  *      Connect to a remote host. There is regrettably still a little
  *      TCP 'magic' in here.
  */
 int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
                           int addr_len, int flags)
 {
        //省略之前的代码
        timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
 
         if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
                 int writebias = (sk->sk_protocol == IPPROTO_TCP) &&
                                 tcp_sk(sk)->fastopen_req &&
                                 tcp_sk(sk)->fastopen_req->data ? 1 : 0;
 
                 /* Error code is set above */
                 if (!timeo || !inet_wait_for_connect(sk, timeo, writebias))
                         goto out;
 
                 err = sock_intr_errno(timeo);
                 if (signal_pending(current))
                         goto out;
         }
         //省略之后的代码
 }
 EXPORT_SYMBOL(__inet_stream_connect);
该函数的实现使用了socket的发送超时时间的设定。

因此,对connect进行超时控制的时候,可以采用如下的代码对socket的发送超时时间进行设置,具体流程可以描述为:
  1. 创建socket
  2. 使用setsockopt函数设定socket的具体设置,在这里设定发送超时
  3. 最后使用connect执行连接的操作。
代码如下:
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sock_fd < 0) {
        perror("create sock_fd");
        exit(-1);
    }   
    struct sockaddr_in addr;
    bzero(&addr,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port=htons(g_conf.aod_port);
    addr.sin_addr.s_addr = inet_addr(g_conf.aod_host);

    struct timeval timeo = {0, 0}; 
    socklen_t len = sizeof(timeo);
    timeo.tv_usec = 10*1000;//10ms
    setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);

    if(connect(sock_fd, (sockaddr*)&addr,sizeof(addr))<0){
        fprintf(,stdout,"%s","CONNECT WITH AOD SERVER FAILED");
        return -1;
    }


另外一种对connect设定超时的方法是使用网上广为流传的select/poll方法。这种方法来自于connect函数的manuel,使用man connect,可以看见connect函数返回值的如下描述:
       EINPROGRESS
              The  socket  is non-blocking and the connection cannot be completed immediately.  It is possible to select(2) or
              poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use  getsock-
              opt(2)  to  read  the  SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully
              (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the  rea-
              son for the failure).
根据上述的描述,我们可以使用connect的返回值做一些文章,从而完成对connect的设置。其实上述的手册已经给定了一个实现connect超时设定的基本步骤,这种方法比之前的方法复杂一些:
  1. 建立socket的文件描述符
  2. 将socket设置为非阻塞的连接模式
  3. 调用connect函数,对connect的返回值进行判定
  4. 使用select检查该socket的文件描述符是否可写
  5. 将socket重新设置为阻塞的模式



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值