0%

Linux Socket TCP Connect概述

概述

在使用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,并设置errnoEINPROGRESS;当连接目标为本机时,立即能够获得连接结果并进行处理。

当进入异步连接状态时,可以通过判断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;
}
}
}
}