設置成非阻塞模式:異步
先用fcntl的F_GETFL獲取flags,用F_SETFL設置flags|O_NONBLOCK; socket
即:blog
flags = fcntl(sockfd, F_GETFL, 0); //獲取文件的flags值。進程
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //設置成非阻塞模式;事件
同時在接收和發送數據時,須要使用MSG_DONTWAIT標誌get
即:博客
在recv,recvfrom和send,sendto數據時,將flag設置爲MSG_DONTWAIT。it
設置成阻塞模式:多進程
先用fcntl的F_GETFL獲取flags,用F_SETFL設置flags&~O_NONBLOCK; class
即:
flags = fcntl(sockfd,F_GETFL,0); //獲取文件的flags值。
fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //設置成阻塞模式;
同時在接收和發送數據時,須要使用阻塞標誌
即:
在recv,recvfrom和send,sendto數據時,將flag設置爲0,默認是阻塞。
在將socket設置成非阻塞模式後,每次的對於sockfd 的操做都是非阻塞的;
非阻塞模式下:
connect
=0 當返回0時,表示當即建立了socket連接,
<0 當返回-1時,須要判斷errno是不是EINPROGRESS(表示當前進程正在處理),不然失敗。
例如:下面會有select或epoll監聽fd是否創建連接,
select監聽connect是否成功的例子,注意getsockopt驗證,由於三次握手的第三個ACK有可能會丟失,可是客戶端認爲連接已經創建:
int ret = ::connect(_socket_fd, add.addr(), add.length());
if(ret == 0)
{
//創建連接成功
}
else if(ret < 0 && errno == EINPROGRESS) //errno == EINPROGRESS表示正在創建連接
{
// 等待鏈接完成,errno == EINPROGRESS表示正在創建連接
fd_set set;
FD_ZERO(&set);
FD_SET(_socket_fd,&set); //相反的是FD_CLR(_sock_fd,&set)
time_t = 10; //(超時時間設置爲10毫秒)
struct timeval timeo;
timeo.tv_sec = timeout / 1000;
timeo.tv_usec = (timeout % 1000) * 1000;
int retval = select(_socket_fd + 1, NULL, &set, NULL, &timeo); //事件監聽
if(retval < 0)
{
//創建連接錯誤close(_socket_fd)
}
else if(retval == 0) // 超時
{
//超時連接沒有創建close(_socket_fd)
}
//將檢測到_socket_fd讀事件或寫時間,並不能說明connect成功
if(FD_ISSET(_socket_fd,&set))
{
int error = 0;
socklen_t len = sizeof(error);
if(getsockopt(_socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
{
//創建簡介失敗close(_socket_fd)
}
if(error != 0) // 失敗
{
//創建連接失敗close(_socket_fd)
}
else
{
//創建連接成功
}
}
}
else
{
//出現錯誤 close(_sock_fd)
}
注意:這裏主要是想強調當epoll或select監聽到sockfd上有EPOLL_IN或EPOLL_OUT時,即讀寫事件時,並不能說明連接已經創建,如上面的代碼。
/*
int error = 0;
socklen_t ilen = sizeof(error);
ret = getsockopt(fd,SOL_SOCKET,SO_ERROR,&error,&ilen);
if(ret < 0)
{
//說明連接創建失敗,close(fd);
}
else if(error != 0 )
{
//說明連接創建失敗,close(fd);
}
else
{
//說明連接創建成功。便可以向fd上寫數據。
}
*/
recv 和 recvfrom
=0 當返回值爲0時,表示對端已經關閉了這個連接,咱們應該本身關閉這個連接,即close(sockfd)。另外由於異步操做會用select或epoll作事件觸發,因此:
一、若是使用select,應該使用FD_CLR(sockfd,fd_set)將sockfd清除掉,再也不監聽。
二、若是使用epoll,系統會本身將sockfd清除掉,再也不進行監聽。
>0 當返回值大於0 且 小於sizeof(buffer)時,表示數據確定讀完。(若是等於sizeof(buffer),可能有數據還沒讀,應該繼續讀,不可能有大於)
<0 當返回值小於0,即等於-1時,分狀況判斷:
一、若是 errno 爲 EAGAINE 或 EWOULDBLOCK
表示暫時無數據可讀,能夠繼續讀,或者等待epoll或select的後續通知。(EAGAINE,EWOULDBLOCK產生的
緣由:多是多進程讀同一個sockfd,可能一個進程讀到數據,其餘進程就讀取不到數據(相似驚羣效應),固然
單個進程也可能出現這種狀況。對於這種錯誤,不需用close(sockfd)。能夠等待select或epoll的下一次觸發,
繼續讀。)
二、若是 errno 爲 EINTR
表示被中斷了,能夠繼續讀,或者等待epoll或select後續的通知。
不然,真的是讀取數據失敗。(此時應該close(sockfd))
send和sendto
返回值是實際發送的字符數,由於咱們知道要發送的總長度,因此,若是沒有發送完,咱們能夠繼續發送。
<0 當返回值爲 -1 時, 咱們須要判斷 errno:
一、若是errno爲 EAGAINE 或 EWOULDBLOCK ,表示當前緩衝區寫滿,能夠繼續寫,
或者等待epoll或select的後續通知,一旦有緩衝區,就會觸發寫操做,這個也是常常利用的一個特性。
二、若是errno爲EINTR ,表示被中斷了,能夠繼續寫,或者等待epoll或select的後續通知。
不然真的出錯了,即errno不爲EAGAINE或EWOULDBLOCK或EINTR,此時應該close(sockfd)
>=0 >=0且不等於要求發送的長度,應該繼續send,若是等於要求發送的長度,發送完畢。