導航:首頁 > 編程系統 > linux設置socket為非阻塞方式

linux設置socket為非阻塞方式

發布時間:2023-02-28 22:35:55

① socket通信可不可以Server端設成非阻塞方式,Client端設成阻塞模式

Windows用socket設置非阻塞式 :

unsigned long ul=1;

SOCKET s=socket(AF_INET,SOCK_STREAM,0);

int ret=ioctlsocket(s, FIONBIO, (unsigned long *)&ul);//設置非阻塞模式

if(ret==SOCKET_ERROR)//設置失敗

{

}

linux用socket設置非阻塞式

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags | O_NONBLOCK);

用socket設置非阻塞式

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags | O_NONBLOCK);

非阻塞設置阻塞用

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);

功能描述:根據文件描述詞操作文件特性

用:

int fcntl(int fd, int cmd);

int fcntl(int fd, int cmd, long arg);

int fcntl(int fd, int cmd, struct flock *lock);

參數:

fd:文件描述詞

cmd:操作命令

arg:供命令使用參數

lock:同

操作命令供使用

. F_DUPFD :復制文件描述詞

二. FD_CLOEXEC :設置close-on-exec標志FD_CLOEXEC位0執行execve程文件保持打反則關閉

三. F_GETFD :讀取文件描述詞標志

四. F_SETFD :設置文件描述詞標志

五. F_GETFL :讀取文件狀態標志

六. F_SETFL :設置文件狀態標志

其O_RDONLY O_WRONLY O_RDWR O_CREAT O_EXCL O_NOCTTY O_TRUNC受影響

更改標志 O_APPENDO_ASYNC O_DIRECT O_NOATIME O_NONBLOCK

七. F_GETLK, F_SETLK F_SETLKW :獲取釋放或測試記錄鎖使用參數結構體指針:

F_SETLK:指定位元組范圍獲取鎖(F_RDLCK, F_WRLCK)或者釋放鎖(F_UNLCK)與另進程鎖操作發沖突返 -1並errno設置EACCES或EAGAIN

F_SETLKW:行同F_SETLK除能獲取鎖睡眠等待外等待程接收信號立即返並errno置EINTR

F_GETLK:獲取文件鎖信息

F_UNLCK:釋放文件鎖

設置讀鎖文件必須讀式打設置寫鎖文件必須寫式打設置讀寫鎖文件必須讀寫式打

② 如何設置linux socket為非阻塞

int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);

③ linux網路編程中阻塞和非阻塞socket的區別

阻塞socket和非阻塞socket的區別:
1、讀操作
對於阻塞的socket,當socket的接收緩沖區中沒有數據時,調用會一直阻塞住,直到有數據到來才返回。當socket緩沖區中的數據量小於期望讀取的數據量時,返回實際讀取的位元組數。當sockt的接收緩沖區中的數據大於期望讀取的位元組數時,讀取期望讀取的位元組數,返回實際讀取的長度。
對於非阻塞socket而言,socket的接收緩沖區中有沒有數據,read調用都會立刻返回。接收緩沖區中有數據時,與阻塞socket有數據的情況是一樣的,如果接收緩沖區中沒有數據,則返回錯誤號為EWOULDBLOCK,表示該操作本來應該阻塞的,但是由於本socket為非阻塞的socket,因此立刻返回,遇到這樣的情況,可以在下次接著去嘗試讀取。如果返回值是其它負值,則表明讀取錯誤。
因此,非阻塞的rea調用一般這樣寫:
if ((nread = read(sock_fd, buffer, len)) < 0)
{
if (errno == EWOULDBLOCK)
{
return 0; //表示沒有讀到數據
}else return -1; //表示讀取失敗
}else return nread;讀到數據長度
2、寫操作
對於寫操作write,原理是類似的,非阻塞socket在發送緩沖區沒有空間時會直接返回錯誤號EWOULDBLOCK,表示沒有空間可寫數據,如果錯誤號是別的值,則表明發送失敗。如果發送緩沖區中有足夠空間或者是不足以拷貝所有待發送數據的空間的話,則拷貝前面N個能夠容納的數據,返回實際拷貝的位元組數。
而對於阻塞Socket而言,如果發送緩沖區沒有空間或者空間不足的話,write操作會直接阻塞住,如果有足夠空間,則拷貝所有數據到發送緩沖區,然後返回.
非阻塞的write操作一般寫法是:
int write_pos = 0;
int nLeft = nLen;
while (nLeft > 0)
{
int nWrite = 0;
if ((nWrite = write(sock_fd, data + write_pos, nLeft)) <= 0)
{
if (errno == EWOULDBLOCK)
{
nWrite = 0;
}else return -1; //表示寫失敗
}
nLeft -= nWrite;
write_pos += nWrite;
}
return nLen;
3、建立連接
阻塞方式下,connect首先發送SYN請求道伺服器,當客戶端收到伺服器返回的SYN的確認時,則connect返回.否則的話一直阻塞.
非阻塞方式,connect將啟用TCP協議的三次握手,但是connect函數並不等待連接建立好才返回,而是立即返回。返回的錯誤碼為EINPROGRESS,表示正在進行某種過程.
4、接收連接
對於阻塞方式的傾聽socket,accept在連接隊列中沒有建立好的連接時將阻塞,直到有可用的連接,才返回。
非阻塞傾聽socket,在有沒有連接時都立即返回,沒有連接時,返回的錯誤碼為EWOULDBLOCK,表示本來應該阻塞。
無阻塞的設置方法
方法一:fcntl
int flag;
if (flag = fcntl(fd, F_GETFL, 0) <0) perror("get flag");
flag |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flag) < 0)
perror("set flag");
方法二:ioctl
int b_on = 1;
ioctl (fd, FIONBIO, &b_on);

④ Linux系統I/O模型及select、poll、epoll原理和應用

理解Linux的IO模型之前,首先要了解一些基本概念,才能理解這些IO模型設計的依據

操作系統使用虛擬內存來映射物理內存,對於32位的操作系統來說,虛擬地址空間為4G(2^32)。操作系統的核心是內核,為了保護用戶進程不能直接操作內核,保證內核安全,操作系統將虛擬地址空間劃分為內核空間和用戶空間。內核可以訪問全部的地址空間,擁有訪問底層硬體設備的許可權,普通的應用程序需要訪問硬體設備必須通過 系統調用 來實現。

對於Linux系統來說,將虛擬內存的最高1G位元組的空間作為內核空間僅供內核使用,低3G位元組的空間供用戶進程使用,稱為用戶空間。

又被稱為標准I/O,大多數文件系統的默認I/O都是緩存I/O。在Linux系統的緩存I/O機制中,操作系統會將I/O的數據緩存在頁緩存(內存)中,也就是數據先被拷貝到內核的緩沖區(內核地址空間),然後才會從內核緩沖區拷貝到應用程序的緩沖區(用戶地址空間)。

這種方式很明顯的缺點就是數據傳輸過程中需要再應用程序地址空間和內核空間進行多次數據拷貝操作,這些操作帶來的CPU以及內存的開銷是非常大的。

由於Linux系統採用的緩存I/O模式,對於一次I/O訪問,以讀操作舉例,數據先會被拷貝到內核緩沖區,然後才會從內核緩沖區拷貝到應用程序的緩存區,當一個read系統調用發生的時候,會經歷兩個階段:

正是因為這兩個狀態,Linux系統才產生了多種不同的網路I/O模式的方案

Linux系統默認情況下所有socke都是blocking的,一個讀操作流程如下:

以UDP socket為例,當用戶進程調用了recvfrom系統調用,如果數據還沒准備好,應用進程被阻塞,內核直到數據到來且將數據從內核緩沖區拷貝到了應用進程緩沖區,然後向用戶進程返回結果,用戶進程才解除block狀態,重新運行起來。

阻塞模行下只是阻塞了當前的應用進程,其他進程還可以執行,不消耗CPU時間,CPU的利用率較高。

Linux可以設置socket為非阻塞的,非阻塞模式下執行一個讀操作流程如下:

當用戶進程發出recvfrom系統調用時,如果kernel中的數據還沒准備好,recvfrom會立即返回一個error結果,不會阻塞用戶進程,用戶進程收到error時知道數據還沒准備好,過一會再調用recvfrom,直到kernel中的數據准備好了,內核就立即將數據拷貝到用戶內存然後返回ok,這個過程需要用戶進程去輪詢內核數據是否准備好。

非阻塞模型下由於要處理更多的系統調用,因此CPU利用率比較低。

應用進程使用sigaction系統調用,內核立即返回,等到kernel數據准備好時會給用戶進程發送一個信號,告訴用戶進程可以進行IO操作了,然後用戶進程再調用IO系統調用如recvfrom,將數據從內核緩沖區拷貝到應用進程。流程如下:

相比於輪詢的方式,不需要多次系統調用輪詢,信號驅動IO的CPU利用率更高。

非同步IO模型與其他模型最大的區別是,非同步IO在系統調用返回的時候所有操作都已經完成,應用進程既不需要等待數據准備,也不需要在數據到來後等待數據從內核緩沖區拷貝到用戶緩沖區,流程如下:

在數據拷貝完成後,kernel會給用戶進程發送一個信號告訴其read操作完成了。

是用select、poll等待數據,可以等待多個socket中的任一個變為可讀,這一過程會被阻塞,當某個套接字數據到來時返回,之後再用recvfrom系統調用把數據從內核緩存區復制到用戶進程,流程如下:

流程類似阻塞IO,甚至比阻塞IO更差,多使用了一個系統調用,但是IO多路復用最大的特點是讓單個進程能同時處理多個IO事件的能力,又被稱為事件驅動IO,相比於多線程模型,IO復用模型不需要線程的創建、切換、銷毀,系統開銷更小,適合高並發的場景。

select是IO多路復用模型的一種實現,當select函數返回後可以通過輪詢fdset來找到就緒的socket。

優點是幾乎所有平台都支持,缺點在於能夠監聽的fd數量有限,Linux系統上一般為1024,是寫死在宏定義中的,要修改需要重新編譯內核。而且每次都要把所有的fd在用戶空間和內核空間拷貝,這個操作是比較耗時的。

poll和select基本相同,不同的是poll沒有最大fd數量限制(實際也會受到物理資源的限制,因為系統的fd數量是有限的),而且提供了更多的時間類型。

總結:select和poll都需要在返回後通過輪詢的方式檢查就緒的socket,事實上同時連的大量socket在一個時刻只有很少的處於就緒狀態,因此隨著監視的描述符數量的變多,其性能也會逐漸下降。

epoll是select和poll的改進版本,更加靈活,沒有描述符限制。epoll使用一個文件描述符管理多個描述符,將用戶關系的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的只需一次。

epoll_create()用來創建一個epoll句柄。
epoll_ctl() 用於向內核注冊新的描述符或者是改變某個文件描述符的狀態。已注冊的描述符在內核中會被維護在一棵紅黑樹上,通過回調函數內核會將 I/O 准備好的描述符加入到一個就緒鏈表中管理。
epoll_wait() 可以從就緒鏈表中得到事件完成的描述符,因此進程不需要通過輪詢來獲得事件完成的描述符。

當epoll_wait檢測到描述符IO事件發生並且通知給應用程序時,應用程序可以不立即處理該事件,下次調用epoll_wait還會再次通知該事件,支持block和nonblocking socket。

當epoll_wait檢測到描述符IO事件發生並且通知給應用程序時,應用程序需要立即處理該事件,如果不立即處理,下次調用epoll_wait不會再次通知該事件。

ET模式在很大程度上減少了epoll事件被重復觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,必須使用nonblocking socket,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。

【segmentfault】 Linux IO模式及 select、poll、epoll詳解
【GitHub】 CyC2018/CS-Notes

⑤ linux socket 連接超時 怎麼解決

今天發現自己的系統存在很嚴重缺陷,當前台關閉的時候後台就無法正常工作,原因很好定位,後台的socket連接超時時間過長,系統默認時間好像是75秒,於是找資料,根據下邊文章中的內容解決了,把超時時間設為5秒後,感覺好多了。看來還有好多東西需要慢慢挖掘阿!

如何設置socket的Connect超時(linux)
[From]http://dev.cbw.com/c/c/200510195601_4292587.shtml
1.首先將標志位設為Non-blocking模式,准備在非阻塞模式下調用connect函數
2.調用connect,正常情況下,因為TCP三次握手需要一些時間;而非阻塞調用只要不能立即完成就會返回錯誤,所以這里會返回EINPROGRESS,表示在建立連接但還沒有完成。
3.在讀套介面描述符集(fd_set rset)和寫套介面描述符集(fd_set wset)中將當前套介面置位(用FD_ZERO()、FD_SET()宏),並設置好超時時間(struct timeval *timeout)
4.調用select( socket, &rset, &wset, NULL, timeout )
返回0表示connect超時
如果你設置的超時時間大於75秒就沒有必要這樣做了,因為內核中對connect有超時限制就是75秒。

[From]http://www.ycgczj.com.cn/34733.html
網路編程中socket的分量我想大家都很清楚了,socket也就是套介面,在套介面編程中,提到超時的概念,我們一下子就能想到3個:發送超時,接收超時,以及select超時(註: select函數並不是只用於套介面的,但是套介面編程中用的比較多),在connect到目標主機的時候,這個超時是不由我們來設置的。不過正常情況下這個超時都很長,並且connect又是一個阻塞方法,一個主機不能連接,等著connect返回還能忍受,你的程序要是要試圖連接多個主機,恐怕遇到多個不能連接的主機的時候,會塞得你受不了的。我也廢話少說,先說說我的方法,如果你覺得你已掌握這種方法,你就不用再看下去了,如果你還不了解,我願意與你分享。本文是已在Linux下的程序為例子,不過拿到Windows中方法也是一樣,無非是換幾個函數名字罷了。
Linux中要給connect設置超時,應該是有兩種方法的。一種是該系統的一些參數,這個方法我不講,因為我講不清楚:P,它也不是編程實現的。另外一種方法就是變相的實現connect的超時,我要講的就是這個方法,原理上是這樣的:
1.建立socket
2.將該socket設置為非阻塞模式
3.調用connect()
4.使用select()檢查該socket描述符是否可寫(注意,是可寫)
5.根據select()返回的結果判斷connect()結果
6.將socket設置為阻塞模式(如果你的程序不需要用阻塞模式的,這步就省了,不過一般情況下都是用阻塞模式的,這樣也容易管理)
如果你對網路編程很熟悉的話,其實我一說出這個過程你就知道怎麼寫你的程序了,下面給出我寫的一段程序,僅供參考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超時時間20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以伺服器地址填充結構serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //設置為非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
} else ret = false;
}
else ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //設置為阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面還可以進行發包收包操作
……………
}
以上代碼片段,僅供參考,也是為初學者提供一些提示,主要用到的幾個函數,select, ioctl, getsockopt都可以找到相關資料,具體用法我這里就不贅述了,你只需要在linux中輕輕的敲一個man <函數名>就能夠看到它的用法。
此外我需要說明的幾點是,雖然我們用ioctl把套介面設置為非阻塞模式,不過select本身是阻塞的,阻塞的時間就是其超時的時間由調用select 的時候的最後一個參數timeval類型的變數指針指向的timeval結構變數來決定的,timeval結構由一個表示秒數的和一個表示微秒數(long類型)的成員組成,一般我們設置了秒數就行了,把微妙數設為0(註:1秒等於100萬微秒)。而select函數另一個值得一提的參數就是上面我們用到的fd_set類型的變數指針。調用之前,這個變數裡面存了要用select來檢查的描述符,調用之後,針對上面的程序這裡面是可寫的描述符,我們可以用宏FD_ISSET來檢查某個描述符是否在其中。由於我這里只有一個套介面描述符,我就沒有使用FD_ISSET宏來檢查調用select之後這個sockfd是否在set裡面,其實是需要加上這個判斷的。不過我用了getsockopt來檢查,這樣才可以判斷出這個套介面是否是真的連接上了,因為我們只是變相的用select來檢查它是否連接上了,實際上select檢查的是它是否可寫,而對於可寫,是針對以下三種條件任一條件滿足時都表示可寫的:
1)套介面發送緩沖區中的可用控制項位元組數大於等於套介面發送緩沖區低潮限度的當前值,且或者i)套介面已連接,或者ii)套介面不要求連接(UDP方式的)
2)連接的寫這一半關閉。
3)有一個套介面錯誤待處理。
這樣,我們就需要用getsockopt函數來獲取套介面目前的一些信息來判斷是否真的是連接上了,沒有連接上的時候還能給出發生了什麼錯誤,當然我程序中並沒有標出那麼多狀態,只是簡單的表示可連接/不可連接。
下面我來談談對這個程序測試的結果。我針對3種情形做了測試:
1. 目標機器網路正常的情況
可以連接到目標主機,並能成功以阻塞方式進行發包收包作業。
2. 目標機器網路斷開的情況
在等待設置的超時時間(上面的程序中為20秒)後,顯示目標主機不能連接。
3. 程序運行前斷開目標機器網路,超時時間內,恢復目標機器的網路
在恢復目標主機網路連接之前,程序一隻等待,恢復目標主機後,程序顯示連接目標主機成功,並能成功以阻塞方式進行發包收包作業。
以上各種情況的測試結果表明,這種設置connect超時的方法是完全可行的。我自己是把這種設置了超時的connect封裝到了自己的類庫,用在一套監控系統中,到目前為止,運行還算正常。這種編程實現的connect超時比起修改系統參數的那種方法的有點就在於它只用於你的程序之中而不影響系統。

⑥ recv是阻塞還是非阻塞的

網路編程函數如recv是阻塞(同步)還是非阻塞(非同步)取決於在調用recv函數前創建的套接字socket是阻塞還是非阻塞。socket默認創建時設定為阻塞模式;若要將socket設定為非阻塞模式,可以在socket創建時設定為非阻塞模式,那麼函數recv就是非阻塞的。
可以通過一下幾種方法設定socket為非阻塞:
1.linux平台可以在利用socket()函數創建socket時指定socket是非同步(非阻塞)的:
int socket(int domain, int type, int protocol);
在參數type中設置SOCK_NONBLOCK標志即可,例如:
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
2.windows和linux平台accept()函數返回的socekt也是阻塞的,linux另外提供了一個accept4()函數,可以直接將socket設置為非阻塞模式:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
只要將accept4()最後一個參數flags設置成SOCK_NONBLOCK即可。

3.除了在創建socket時,將socket設置為非阻塞模式,還可以通過以下函數來設置:

linux平台可以調用fcntl()或ioctl()函數,例如:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞
windows平台可調用ioctlsocket函數:

int ioctlsocket(
_In_ SOCKET s,
_In_ long cmd,
_Inout_ u_long *argp
);
將cmd參數設置為FIONBIO,*argp=0即設置成阻塞模式,而*argp非0即可設置成非阻塞模式。但windows平台一個地方需要注意,如果對一個socket調用了WSAAsyncSelect()或WSAEventSelect()函數後,你再調用ioctlsocket()函數將該socket設置為阻塞模式,則會失敗,必須先調用WSAAsyncSelect()設置lEvent參數為0或調用WSAEventSelect()設置lNetworkEvents參數為0來分別禁用WSAAsyncSelect()或WSAEventSelect(),再次調用ioctlsocket()將該socket設置成阻塞模式才會成功。因為調用WSAAsyncSelect()或WSAEventSelect()函數會自動將socket設置成非阻塞模式。

⑦ socket非阻塞方式下的Linux c++編程步驟是怎樣的

Windows用以下方法將socket設置為非阻塞方式 :

unsigned long ul=1;

SOCKET s=socket(AF_INET,SOCK_STREAM,0);

int ret=ioctlsocket(s, FIONBIO, (unsigned long *)&ul);//設置成非阻塞模式。

if(ret==SOCKET_ERROR)//設置失敗。

{

}

Linux用以下方法將socket設置為非阻塞方式

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags | O_NONBLOCK);

用以下方法將socket設置為非阻塞方式

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags | O_NONBLOCK);

將非阻塞的設置回阻塞可以用

int flags = fcntl(socket, F_GETFL, 0);

fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);

功能描述:根據文件描述詞來操作文件的特性。

用法:

int fcntl(int fd, int cmd);

int fcntl(int fd, int cmd, long arg);

int fcntl(int fd, int cmd, struct flock *lock);

參數:

fd:文件描述詞。

cmd:操作命令。

arg:供命令使用的參數。

lock:同上。

有以下操作命令可供使用

一. F_DUPFD :復制文件描述詞 。

二. FD_CLOEXEC :設置close-on-exec標志。如果FD_CLOEXEC位是0,執行execve的過程中,文件保持打開。反之則關閉。

三. F_GETFD :讀取文件描述詞標志。

四. F_SETFD :設置文件描述詞標志。

五. F_GETFL :讀取文件狀態標志。

六. F_SETFL :設置文件狀態標志。

其中O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NOCTTY 和 O_TRUNC不受影響,

可以更改的標志有 O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。

七. F_GETLK, F_SETLK 和 F_SETLKW :獲取,釋放或測試記錄鎖,使用到的參數是以下結構體指針:

F_SETLK:在指定的位元組范圍獲取鎖(F_RDLCK, F_WRLCK)或者釋放鎖(F_UNLCK)。如果與另一個進程的鎖操作發生沖突,返回 -1並將errno設置為EACCES或EAGAIN。

F_SETLKW:行為如同F_SETLK,除了不能獲取鎖時會睡眠等待外。如果在等待的過程中接收到信號,會立即返回並將errno置為EINTR。

F_GETLK:獲取文件鎖信息。

F_UNLCK:釋放文件鎖。

為了設置讀鎖,文件必須以讀的方式打開。為了設置寫鎖,文件必須以寫的方式打開。為了設置讀寫鎖,文件必須以讀寫的方式打開。

⑧ linux網路編程,為什麼要將文件描述符設置成非阻塞模式

非阻塞IO 和阻塞IO:

在網路編程中對於一個網路句柄會遇到阻塞IO 和非阻塞IO 的概念, 這里對於這兩種socket 先做一下說明:
基本概念:
阻塞IO::
socket 的阻塞模式意味著必須要做完IO 操作(包括錯誤)才會
返回。
非阻塞IO::
非阻塞模式下無論操作是否完成都會立刻返回,需要通過其他方
式來判斷具體操作是否成功。(對於connect,accpet操作,通過select判斷,
對於recv,recvfrom,send,sendto通過返回值+錯誤碼來判斷)

IO模式設置:
SOCKET
對於一個socket 是阻塞模式還是非阻塞模式的處理方法::
方法::
用fcntl 設置;用F_GETFL獲取flags,用F_SETFL設置flags|O_NONBLOCK;
同時,recv,send 時使用非阻塞的方式讀取和發送消息,即flags設置為MSG_DONTWAIT
實現
fcntl 函數可以將一個socket 句柄設置成非阻塞模式:
flags = fcntl(sockfd, F_GETFL, 0); //獲取文件的flags值。
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //設置成非阻塞模式;
flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //設置成阻塞模式;
並在接收和發送數據時:
將recv, send 函數的最後有一個flag 參數設置成MSG_DONTWAIT
recv(sockfd, buff, buff_size,MSG_DONTWAIT); //非阻塞模式的消息發送
send(scokfd, buff, buff_size, MSG_DONTWAIT); //非阻塞模式的消息接受

普通文件
對於文件的阻塞模式還是非阻塞模式::
方法1、open時,使用O_NONBLOCK;
方法2、fcntl設置,使用F_SETFL,flags|O_NONBLOCK;

消息隊列
對於消息隊列消息的發送與接受::
//非阻塞 msgsnd(sockfd,msgbuf,msgsize(不包含類型大小),IPC_NOWAIT)
//阻塞 msgrcv(scokfd,msgbuf,msgsize(**),msgtype,IPC_NOWAIT);


阻塞與非阻塞讀的區別: //阻塞和非阻塞的區別在於沒有數據到達的時候是否立刻返回.
讀(read/recv/msgrcv):
讀的本質來說其實不能是讀,在實際中, 具體的接收數據不是由這些調用來進行,是由於系統底層自動完成的。read 也好,recv 也好只負責把數據從底層緩沖 到我們指定的位置.
對於讀來說(read, 或者recv) ::
阻塞情況下::
在阻塞條件下,read/recv/msgrcv的行為::
1、如果沒有發現數據在網路緩沖中會一直等待,
2、當發現有數據的時候會把數據讀到用戶指定的緩沖區,但是如果這個時候讀到的數據量比較少,比參數中指定的長度要小,read 並不會一直等待下去,而是立刻返回。
read 的原則::是數據在不超過指定的長度的時候有多少讀多少,沒有數據就會一直等待。
所以一般情況下::我們讀取數據都需要採用循環讀的方式讀取數據,因為一次read 完畢不能保證讀到我們需要長度的數據,
read 完一次需要判斷讀到的數據長度再決定是否還需要再次讀取。
非阻塞情況下::
在非阻塞的情況下,read 的行為::
1、如果發現沒有數據就直接返回,
2、如果發現有數據那麼也是採用有多少讀多少的進行處理.
所以::read 完一次需要判斷讀到的數據長度再決定是否還需要再次讀取。

對於讀而言:: 阻塞和非阻塞的區別在於沒有數據到達的時候是否立刻返回.
recv 中有一個MSG_WAITALL 的參數::
recv(sockfd, buff, buff_size, MSG_WAITALL),
在正常情況下recv 是會等待直到讀取到buff_size 長度的數據,但是這里的WAITALL 也只是盡量讀全,在有中斷的情況下recv 還是可能會被打斷,造成沒有讀完指定的buff_size的長度。
所以即使是採用recv + WAITALL 參數還是要考慮是否需要循環讀取的問題,在實驗中對於多數情況下recv (使用了MSG_WAITALL)還是可以讀完buff_size,
所以相應的性能會比直接read 進行循環讀要好一些。

注意:: //使用MSG_WAITALL時,sockfd必須處於阻塞模式下,否則不起作用。
//所以MSG_WAITALL不能和MSG_NONBLOCK同時使用。
要注意的是使用MSG_WAITALL的時候,sockfd 必須是處於阻塞模式下,否則WAITALL不能起作用。



阻塞與非阻塞寫的區別: //
寫(send/write/msgsnd)::
寫的本質也不是進行發送操作,而是把用戶態的數據 到系統底層去,然後再由系統進行發送操作,send,write返回成功,只表示數據已經 到底層緩沖,而不表示數據已經發出,更不能表示對方埠已經接收到數據.
對於write(或者send)而言,
阻塞情況下:: //阻塞情況下,write會將數據發送完。(不過可能被中斷)
在阻塞的情況下,是會一直等待,直到write 完,全部的數據再返回.這點行為上與讀操作有所不同。
原因::
讀,究其原因主要是讀數據的時候我們並不知道對端到底有沒有數據,數據是在什麼時候結束發送的,如果一直等待就可能會造成死循環,所以並沒有去進行這方面的處理;
寫,而對於write, 由於需要寫的長度是已知的,所以可以一直再寫,直到寫完.不過問題是write 是可能被打斷嗎,造成write 一次只write 一部分數據, 所以write 的過程還是需要考慮循環write, 只不過多數情況下一次write 調用就可能成功.

非阻塞寫的情況下:: //
非阻塞寫的情況下,是採用可以寫多少就寫多少的策略.與讀不一樣的地方在於,有多少讀多少是由網路發送的那一端是否有數據傳輸到為標准,但是對於可以寫多少是由本地的網路堵塞情況為標準的,在網路阻塞嚴重的時候,網路層沒有足夠的內存來進行寫操作,這時候就會出現寫不成功的情況,阻塞情況下會盡可能(有可能被中斷)等待到數據全部發送完畢, 對於非阻塞的情況就是一次寫多少算多少,沒有中斷的情況下也還是會出現write 到一部分的情況.

⑨ C++ socket非阻塞模式

一、前言

初期學習socket的時候,為了方便理解,使用默認的阻塞模式比較多。而實際做項目時,我們必須考慮程序的並發性,非阻塞模式在其中擔任著很重要的角色,是必會的點之一。本文不對阻塞IO和非阻塞IO的概念做說明,不了解的請自行了解。下文代碼以linux平台為例。

二、設置非阻塞模式

設置非阻塞模式,通過fcntl方法設置,為了保存socket其他設置,一般選擇先獲取 status flags, 並在其基礎上設置O_NONBLOCK屬性, 代碼如下:

fcntl失敗返回值為-1, 同時errno會被設置成對應的錯誤碼。(errno在此不做說明,不了解的自行了解。) 考慮失敗的情況,個人注意到網上有些例子(包括ss-libev項目)在 F_GETFL 失敗後,給了flags默認值,代碼如下:

經過測試,默認情況下,flags得到的值為2,也就是O_RDWR 讀寫, 而 0 對應的相關宏為O_RDONLY只讀,明顯不合理。個人感覺,對於一個正常的socket來說,F_GETFL 出錯的機會不大吧, 至少我是沒遇到過。如果實在出錯了,還是建議走錯誤流程而不是給個默認值。

三、 非阻塞server

server端通常在accept後,我們為客戶端連接的fd設置為非阻塞。設置O_NONBLOCK後,recv和send發生了變化。默認阻塞模式下,recv在沒有數據可以接收(對方未發數據,或者緩沖區的數據已讀完對方沒有繼續發)情況下,recv會阻塞等待,直到下次有數據發送過來。而非阻塞模式下,recv在沒有數據可以接收的時候, recv會直接返回-1, 同時errno會被設置為EAGAIN/EWOULDBLOCK 。同理,非阻塞send也會在對方緩沖區滿的情況下直接返回-1並設置errno, 而不是阻塞等待。 非阻塞模式下server代碼大致如下:

四、非阻塞client

client除了在send/recv, 還可以在connect前設置非阻塞模式,這樣在connect時候可以直接返回。

client 非阻塞connect的時候,如果返回0表示連接成功,如果返回-1, 則需要判斷errno 是否為EINPROGRESS,EINPROGRESS表示非阻塞連接不能立刻獲取connect結果,後面可使用select/poll/epoll等對socket 可寫性進行判斷,如果socket已可寫,使用 getsockopt(iSocket, SOL_SOCKET, SO_ERROR ,&err, &len)進行判斷。。。好像挺麻煩是不是,但是我還是建議在大部分項目中connect前設置非阻塞(小工具之類的就無所謂了,項目中一定要保證效率)。如果使用阻塞模式,有可能的問題:

下面是個非阻塞connect的部分代碼, 使用select, 至於poll/epoll請自行搜索代碼,跟非阻塞邏輯無關:

⑩ linux網路編程中阻塞和非阻塞socket的區別

阻塞socket和非阻塞socket
讀操作
對於阻塞的socket,當socket的接收緩沖區中沒有數據時,read調用會一直阻塞住,直到有數據到來才返
回。當socket緩沖區中的數據量小於期望讀取的數據量時,返回實際讀取的位元組數。當sockt的接收緩沖
區中的數據大於期望讀取的位元組數時,讀取期望讀取的位元組數,返回實際讀取的長度。
對於非阻塞socket而言,socket的接收緩沖區中有沒有數據,read調用都會立刻返回。接收緩沖區中有
數據時,與阻塞socket有數據的情況是一樣的,如果接收緩沖區中沒有數據,則返回錯誤號為
EWOULDBLOCK,
表示該操作本來應該阻塞的,但是由於本socket為非阻塞的socket,因此立刻返回,遇到這樣的情況,可
以在下次接著去嘗試讀取。如果返回值是其它負值,則表明讀取錯誤。
因此,非阻塞的rea調用一般這樣寫:
if ((nread = read(sock_fd, buffer, len)) < 0)
{
if (errno == EWOULDBLOCK)
{
return 0; //表示沒有讀到數據
}else return -1; //表示讀取失敗
}else return nread;讀到數據長度
寫操作
對於寫操作write,原理是類似的,非阻塞socket在發送緩沖區沒有空間時會直接返回錯誤號EWOULDBLOCK,
表示沒有空間可寫數據,如果錯誤號是別的值,則表明發送失敗。如果發送緩沖區中有足夠空間或者
是不足以拷貝所有待發送數據的空間的話,則拷貝前面N個能夠容納的數據,返回實際拷貝的位元組數。
而對於阻塞Socket而言,如果發送緩沖區沒有空間或者空間不足的話,write操作會直接阻塞住,如果有
足夠空間,則拷貝所有數據到發送緩沖區,然後返回.
非阻塞的write操作一般寫法是:
int write_pos = 0;
int nLeft = nLen;
while (nLeft > 0)
{
int nWrite = 0;
if ((nWrite = write(sock_fd, data + write_pos, nLeft)) <= 0)
{
if (errno == EWOULDBLOCK)
{
nWrite = 0;
}else return -1; //表示寫失敗
}
nLeft -= nWrite;
write_pos += nWrite;
}
return nLen;
建立連接
阻塞方式下,connect首先發送SYN請求道伺服器,當客戶端收到伺服器返回的SYN的確認時,則
connect
返回.否則的話一直阻塞.
非阻塞方式,connect將啟用TCP協議的三次握手,但是connect函數並不等待連接建立好才返回,而是
立即返回。返回的錯誤碼為EINPROGRESS,表示正在進行某種過程.
接收連接
對於阻塞方式的傾聽socket,accept在連接隊列中沒有建立好的連接時將阻塞,直到有可用的連接,才返
回。
非阻塞傾聽socket,在有沒有連接時都立即返回,沒有連接時,返回的錯誤碼為EWOULDBLOCK,表示本來應
該阻塞。

無阻塞的設置方法
方法一:fcntl
int flag;
if (flag = fcntl(fd, F_GETFL, 0) <0) perror("get flag");
flag |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flag) < 0)
perror("set flag");
方法二:ioctl
int b_on = 1;
ioctl (fd, FIONBIO, &b_on);

閱讀全文

與linux設置socket為非阻塞方式相關的資料

熱點內容
鐵路12306密碼找不回 瀏覽:352
默認網路覆蓋的腦區 瀏覽:319
itunes恢復iphone教程 瀏覽:292
爐石現在是什麼版本 瀏覽:825
word兼容包安裝報錯 瀏覽:528
iphone5s包裝4g沒有氣孔 瀏覽:814
html包含文件代碼嗎 瀏覽:50
蘋果appstore日本賬號 瀏覽:532
解密dg加密的文件 瀏覽:206
gsh6什麼格式文件 瀏覽:507
dnf85版本覺醒任務 瀏覽:998
范冰冰蘋果百度雲盤資源鏈接 瀏覽:507
資料庫主機是什麼系統 瀏覽:812
pdf表單教程 瀏覽:715
百度瀏覽器去更新安卓破解版 瀏覽:855
光碟內部應用程序錯誤 瀏覽:83
iphone6升級ios9步驟 瀏覽:873
魔力代碼 瀏覽:497
win10打開區域網文件夾很卡 瀏覽:986
app收益怎麼分 瀏覽:812

友情鏈接