【網路程式設計】非阻塞connect詳解
阿新 • • 發佈:2019-02-12
一、為什麼使用非阻塞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);
}
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);
}
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;
}