一、为什么使用非阻塞connect
TCP连接的建立涉及一个在三路握手过程,阻塞的connect一直等到客户收到自己的SYN的ACK才返回,这需要至少一个RTT时间,RTT时间波动很大从几毫秒到几秒。而且在没有响应时,会等待数秒再次发送,(详见TCPv2第828页),总共75秒到几分钟的connect超时时间。为了缩短超时时间,将非阻塞connect与select配合使用
二、connect的处理细节
1、当服务器和客户端在同一个主机上时,connect会立刻建立;
2、建立成功select监测到描述符为可写(详见TCPv2第531页);
3、建立失败select检测到描述符为可读可写(详见TCPv2第530页);
4、当可写返回时,通过写成功与否来判断,网络建立成功或失败。
三、代码详解
int MyConnect(int fd, char *addr, int port, int timeout)
{
struct sockaddr_in server_addr;
int ioFlags = 0;
int ret;
char temp_addr[IP_LEN];
//设置非阻塞读写,使connect成为非阻塞的
ioFlags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, ioFlags|O_NONBLOCK);
//设置服务器地址
memset(&server_addr, 0, sizeof(struct sockaddr_in));
memset(temp_addr, 0, IP_LEN);
strcpy(temp_addr,addr);
if (inet_aton(temp_addr, &server_addr.sin_addr) == 0)
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
//连接服务器
ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
if( ret == 0 ) //连接成功,服务器与客户端立即建立连接
{
}
else
{
if( errno != EINPROGRESS ) //连接失败,EINPROGRESS代表正在进行三次握手
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
else //(errno==EINPROGRESS) //正在进行三次握手
{
fd_set rSet, wSet;
struct timeval to;
FD_ZERO(&rSet);
FD_SET(fd, &rSet);
wSet = rSet;
to.tv_sec = timeout;
to.tv_usec= 0;
ret = select(fd+1, &rSet, &wSet, NULL, &to);//监听网络
if(ret<0) //select返回错误,连接失败
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
else if(ret==0) //连接超时
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
else
{
if( FD_ISSET(fd, &wSet) )
{
errno = 0;
char t = 0;
ret = send(fd, &t, 0, 0);
if( (ret==0)&&(errno==0) ) //连接成功
{
}
else
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
}
else
{
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return -1;
}
}
}
}
//再次设置非阻塞读写
fcntl(fd, F_SETFL, ioFlags);
return 0;
}