非阻塞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;
}