TCP非堵塞IO的connect连接处理模型

本文详细探讨了TCP非阻塞IO的connect连接处理模型,包括非阻塞connect的三个应用场景,如资源利用优化、多连接技术和自定义超时设置。还深入分析了非阻塞connect的代码实现,特别指出在不同操作系统中获取待处理错误的差异,并提供了三种判断连接成功的方法。最后总结了非阻塞IO的重要性和相关注意事项。

一、非堵塞connect版本

当在非堵塞TCP套接字上, 我们调用connect函数会立刻返回EINPROGRESS, 不过此时TCP三路握手正在进行, 我们可以通过检查套接字的状态。


*非堵塞connect有三个用途

1)、我们可以把三路握手叠加到其他的处理上面。 因为在连接过程中花费1个RTT时间,在局域网或者广域网上的时间幅度偏差很大(几毫秒甚至几秒), 那么我们可以利用这些时间让CPU处理其他事情。

2)、在web程序当中的多连接技术。

3)、通常情况非堵塞的connect连接的超时间是75秒, 但是通过非堵塞connect版本 ,我们可以自己设置等待时间。


*非堵塞connect细节处理

●  如果套接字是非堵塞的情况下, 如果我们连接到的服务器在同一个主机上面, 那么连接将会立刻完成。

●  在非堵塞套接字连接的时候, 我们需要用select来检查connect连接情况有以下两个规则:(1)当连接成功建立时, 描述符号将会变的可写。(2)当连接遇到错误的时候, 描述符变为即可读又可写。


二、非堵塞connect代码分析

源代码如下:

connect_nonb.c

#include "unp.h"

int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec)
{
    int flags, n, error;
    socklen_t len;
    fd_set rset, wset;
    struct timeval tval;

    flags = Fcntl(sockfd, F_GETFL, 0);
    Fcntl(sockfd, F_SETFL, flags | O_ONOBLOCK);

    error = 0;
    if((n = connect(sockfd, saptr, salen)) < 0){
        if(error != EINPROGRESS) return -1;
    }
    else if(n == 0)
        goto done;

    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    wset = rset;
    tval.tv_sec = nsec;
    tval.tv_usec = 0;

    if((n = select(sockfd + 1, &rset, &wset, NULL, (nsec? &tval:NULL))) == 0){
        close(sockfd);
        errno = ETIMEOUT;
        return -1;
    }

    if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)){
        len = sizeof(error);
        if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) return -1;  // Solaris可移植问题
    }else{
        fprintf(stderr, "select error: sockfd not set\n");
    }
done:
    fcntl(sockfd, F_SETFL, flags);
    if(error){   // berkeley可移植问题。
        close(sockfd);
        errno = error;
        return -1;
    }

    return 0;
}
10-11 行:设置套接字为非堵塞

13-16行: 发起非堵塞的connect。期望错误值是EINPROGRESS, 那表示连接已经启动但是尚未完成。

17-18行:非堵塞连接成功, connect返回0。一般只有客户和服务器在同一台主机上面才会发生这样的情况
26-28行:超时调用,errno置为ETIMEDOUT。

32-37行:造成可读可写条件, 我们通过调用getsockopt去的套接字的待处理错误条件, 如果连接成功返回的是0。在这里存在一个可移植的问题,就是在Berkeley中我们的变量error中返回待处理错误时候, getsockopt本身返回0; 而在Solaris中让是getsockopt返回-1, 将errno处理置为待处理错误。我们的程度充分考虑到了该问题

38-46行:恢复套接字标志, 如果是在Berkeley的实现下, 对error进行判断,若有错误则将errno置于error的值。


*针对代码中的第34行中的getsockopt的条件我们还可以有以下解决方案:

1)、调用getpeername代替getsockopt。如果getpeername以ENOTCONN错误返回,那么建立连接失败, 那么我们必须使用SOL_SOCKET中的SO_ERROR标志来调用getsockopt函数已获得待处理错误。

2)、read(sockfd, buffer, 0)调用socket。如果read失败, 那么connect失败, 那么read返回errno将会给出失败原因。 若果建立成功, read将会返回0.

3)、根据TCP套接字的connect只能连接一次的原因, 再次调用connect应该是失败的, 看错误原因,如果返回错误EISCONN, 那么代表连接成功


三、总结

1、了解非堵塞IO的三个用途。

2、非堵塞connect情况下, EINPROGRESS错误值分析, 以及连接成功的条件。

3、判断连接是否成功的三个解决方案。

4、connect可移植问题研究

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值