天天看点

非阻塞connect的使用笔记

        非阻塞connect的三个用途:

       可以把三路握手叠加在其他处理上; 可以使用这个技术同时建立多个连接;  可以给select指定一个时间限制,使得能够缩短connect的超时。

        当一个非阻塞的TCP套接字上调用connect时,connec将立即返回一个EINPROGRESS错误,不过已经发起的TCP三路握手继续进行。接着使用select/poll/epoll等检测这个连接或成功或失败的已建立条件。

        尽管套接字连接是非阻塞的,如果连接到的服务器在同一个主机上,那么当调用connect时,连接通常立刻建立。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>

#define BUFFER_SIZE  1023

int setnonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);

    return old_option;
}


int unblock_connect(const char *ip, int port, int time)
{
    int ret = 0;
    struct sockaddr_in serveraddr;
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &serveraddr.sin_addr);

    int sockfd = socket( AF_INET, SOCK_STREAM, 0);
    assert( sockfd >= 0 );

    int fdopt = setnonblocking(sockfd);

    ret = connect(sockfd, ( struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if( ret == 0 )    //立即建立了连接
    {
	printf("connect with server immediately\n");
        fcntl(sockfd, F_SETFL, fdopt);
        return sockfd;
    }
    else if( errno != EINPROGRESS) // 返回错误不是EINPROGRESS,则无法建立连接
    {
        printf("unblock connect not support\n");
	return -1;
    }

 
    //没有立即建立连接,需要对套机字监听是否可写
    fd_set writefds;
    struct timeval timeout;

    FD_ZERO( &writefds );
    FD_SET( sockfd, &writefds);

    timeout.tv_sec = time;
    timeout.tv_usec = 0;

    ret = select( sockfd+1, NULL, &writefds, NULL, &timeout );
    if( ret <= 0)  //超时或者select调用失败
    {
	printf("connection time out\n");
        close( sockfd );
 
 	return -1;
    }

    if( !FD_ISSET( sockfd, &writefds ) )   //套接字不可写,则建立链接失败
    {
	printf("no events on sockfd found\n");
	close( sockfd );

        return -1;
    }

  
    //调用getsockopt检查套接字上是否存在待处理错误
    //如果错误码是0则表示连接建立成功,否则连接建立失败
    int error = 0;
    socklen_t length = sizeof( error );
    if( getsockopt( sockfd, SOL_SOCKET, SO_ERROR, &error, &length) < 0 )
    {
	printf("get sockfd option failed\n");
        close( sockfd );

        return -1;
    }
    
    if( error != 0 )
    {
	printf( "connection failed after select with the error: %d \n", error );
	close( sockfd );
 
        return -1;
    }

    printf("connection ready after select with the socket: %d\n", sockfd );
    fcntl( sockfd, F_SETFL, fdopt );

    return sockfd;
 
}


int main(int argc, char **argv)
{
    if( argc <= 2 )
    {
	printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
	return -1;
    }

    const char *ip = argv[1];
    int port = atoi( argv[2] );

    int sockfd = unblock_connect( ip, port, 10 );

    if( sockfd < 0 )
    {
	return 1;
    }

    return 0;
}
           

继续阅读