概述
在使用Linux的Socket进行TCP连接时,需要使用Connect进行3次握手以完成连接。其函数原型如下:
1 2 3
| #include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
阻塞状态
当socket处于阻塞状态时,调用Connect将进入阻塞状态,自到三次握手成功或连接失败。
Socket判断连接失败的方式是设置单次握手的超时时间+设置允许超时的次数,Linux通常情况下超时时间呈2的指数次方增长,超时次数设为6次,可以使用sysctl net.ipv4.tcp_syn_retries查看允许超时次数。因此当连接失败时需要等待1s + 2s + 4s + 8s + 16s + 32s + 64s = 127s。该时间明显过长。
当connect连接成功时,将会返回0;当连接失败是,返回-1并设置errno。
非阻塞状态
当socket处于阻塞状态时,调用Connect将立即返回,同时尝试进行三次握手,这使得程序能够在进行握手的同时异步的进行其他事务的处理。
通常情况下,当连接目标非本机时,将会立即返回-1,并设置errno为EINPROGRESS;当连接目标为本机时,立即能够获得连接结果并进行处理。
当进入异步连接状态时,可以通过判断soeket描述符是否可写来判断是否完成连接处理判断。这可以使用select等方式进行处理。
当判断连接完成时,还需要判断连接是否成功。通常情况下,可以通过将getsockopt获取soeket描述符上的错误。
一个简单的非阻塞socket connect demo如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| int socket_connect() { while(true) { int sock = socket(AF_INET, SOCK_STREAM, 0); assert(-1 != sock);
int flag=fcntl(sock, F_GETFL); assert(-1 != flag); flag = fcntl(fd,F_SETFL,flag | O_NONBLOCK); assert(-1 != flag); struct sockaddr_in addr; socklen_t addrlen;
bzero(&addr, sizeof(addr)); addr.sin_family=AF_INET; addr.sin_port=htons(atoi(port)); flag = inet_pton(AF_INET,ip,&addr.sin_addr.s_addr); assert(-1 != flag);
int ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
if(0 == ret) { return sock; }
if(errno != EINPROGRESS) { close(sock); continue; }
fd_set writefds; struct timeval timeout;
timeout.tv_sec = s; timeout.tv_usec = us;
while(true) { FD_ZERO(&writefds); FD_SET(sock, &writefds);
int ret = select(sock + 1, NULL, &writefds, NULL, &timeout);
if(0 == ret) { onTimeout(); } else if(-1 == ret) { if(EINTR == errno) { onTimeout(); } else { close(sock); break; } } else if(1 == ret) { if(!FD_ISSET(sock, &writefds)) { close(sock); break; } else { int error = 0; socklen_t length=sizeof(error); ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &length); if(-1 == ret || 0 != error) { close(sock); break; } else { return sock; } } } else { close(sock); break; } } } }
|