我們知道,tcp客戶端要與服務端通訊,必須先建立連線,即呼叫connect函式完成三次握手,而預設情況下connect是阻塞方式的,也就是說呼叫connect函式會發生阻塞,超時時間可能在75s至幾分鐘之間。當然同一主機除外,同一主機上呼叫connect通常會立即成功。
為避免長時間的connect阻塞,可以使用如下非阻塞connect方式來處理:
一、 建立socket,返回套介面描述符
二、 呼叫fcntl把套介面描述符設定成非阻塞
三、 呼叫connect開始建立連線
四、 判斷連線是否成功建立
a: 如果connect返回0,表示連線成功(伺服器和客戶端在同一臺機器上時就有可能發生這種情況)
b: 呼叫select來等待連線建立成功完成
(berkeley的實現(和posix.1g)有兩條與select和非阻塞io相關的規則:
1.當連線建立成功時,套介面描述符變成可寫;
2.當連線出錯時,套介面描述符變成既可讀又可寫;
注意:當乙個套介面出錯時,它會被select呼叫標記為既可讀又可寫;)
五、 繼續判斷select返回值
如果select返回0,則表示建立連線超時; 我們返回超時錯誤給使用者,同時關閉連線,以防止三路握手操作繼續進行下去
如果select返回大於0的值,則需要檢查套介面描述符是否可讀或可寫;如果套介面描述符可讀或可寫,則我們可以通過呼叫getsockopt來得到套介面上待處理的錯誤(so_error),如果連線建立成功,這個錯誤值將是0,如果建立連線時遇到錯誤,則這個值是連線錯誤所對應的errno值(比如:econnrefused,etimedout等).
#include #include #include #include #include #include #include #include #include #include #include #include #define dstaddr "192.168.1.44"
#define dstport 30040
int main(int argc, char* argv)
; struct sockaddr_in servaddr;
socklen_t addr_size = 0;
char destaddr[16] = ;
int destport = 0;
int recv_len = 0;
char who[64] = ;
char sndbuffer[256] = ;
struct timeval tvbegin;
struct timeval tvend;
if(argc < 3)
else
/*---- create socket ----*/
if((sockfd = socket(pf_inet, sock_stream, 0)) < 0)
/*---- set non-block ----*/
int options = fcntl(sockfd, f_getfl, 0);
fcntl(sockfd, f_setfl, options | o_nonblock);
/*---- configure settings of connection ----*/
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = af_inet;
servaddr.sin_port = htons(destport);
servaddr.sin_addr.s_addr = inet_addr(destaddr);
/* set padding field to 0 */
//memset(servaddr.sin_zero, 0, sizeof(servaddr.sin_zero));
/*---- connect ----*/
ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(ret == 0)
else if(ret < 0)
if (ret == 0)
else
else
if(err != 0)
else}}
}}
/*---- send ----*/
strcpy(sndbuffer,"hello!\r\n");
if((ret = send(sockfd, sndbuffer, strlen(sndbuffer), 0)) > 0)
else
gettimeofday(&tvbegin, null);
while(1)
else
}else if(recv_len == 0)
else
else if(strstr(buffer, "heartbeat"))
usleep(100);
memset(sndbuffer, 0, sizeof(sndbuffer));
sprintf(sndbuffer, "%s request other...\r\n", who);
if((ret = send(sockfd,sndbuffer,strlen(sndbuffer),0)) > 0)}}
//gettimeofday(&tvend, null);
unsigned int subtime = tvend.tv_sec-tvbegin.tv_sec;
//printf(".............%d....\n", subtime);
if(subtime > 3)
}usleep(1000);
}return 0;
}
TCP非阻塞accept和非阻塞connect
非阻塞accept 當乙個已完成的連線準備好被accept的時候,select會把監聽socket標記為可讀。因此,如果用select等待外來的連線時,應該不需要把監聽socket設定為非阻塞模式,因為如果select告訴我們連線已經就緒,accept就不應該被阻塞。不過這樣做的時候有乙個bug 當...
TCP和UDP阻塞和非阻塞之間的區別
首先socket在預設情況下是阻塞狀態的,這就使得傳送以及接收操作處於阻塞的狀態,即呼叫不會立即返回,而是進入睡眠等待操作完成。下面把討論點分為傳送以及接收。一.傳送選用send 這裡特指tcp 以及sendto 這裡特指udp 來描述 首先需要說明的是,不管阻塞還是非阻塞,在傳送時都會將資料從應用...
知乎上乙個關於同步非同步阻塞非阻塞的有趣解釋
老張愛喝茶,廢話不說,煮開水。出場人物 老張,水壺兩把 普通水壺,簡稱水壺 會響的水壺,簡稱響水壺 1 老張把水壺放到火上,立等水開。同步阻塞 老張覺得自己有點傻 2 老張把水壺放到火上,去客廳看電視,時不時去廚房看看水開沒有。同步非阻塞 老張還是覺得自己有點傻,於是變高階了,買了把會響笛的那種水壺...