linux非阻塞
❶ linux 怎樣實現非阻塞connect
1. 設置socket
int oldOption = fcntl(sockfd, F_GETFL);
int newOption = oldOption | O_NONBLOCK;
//設置sockfd非阻塞
fcntl(sockfd, F_SETFL, newOption);12345
2. 執行connect
如果返回0,表示連接成功,這種情況一般在本機上連接時會出現(否則怎麼可能那麼快)
否則,查看error是否等於EINPROGRESS(表明正在進行連接中),如果不等於,則連接失敗
int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == 0)
{
//連接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}
else if(errno != EINPROGRESS)
{
//連接沒有立即返回,此時errno若不是EINPROGRESS,表明錯誤
perror("connect error != EINPROGRESS");
return -1;
}12345678910111213141516
3. 使用select,如果沒用過select可以去看看
用select對socket的讀寫進行監聽
那麼監聽結果有四種可能
1. 可寫(當連接成功後,sockfd就會處於可寫狀態,此時表示連接成功)
2. 可讀可寫(在出錯後,sockfd會處於可讀可寫狀態,但有一種特殊情況見第三條)
3. 可讀可寫(我們可以想像,在我們connect執行完到select開始監聽的這段時間內,
如果連接已經成功,並且服務端發送了數據,那麼此時sockfd就是可讀可寫的,
因此我們需要對這種情況特殊判斷)
說白了,在可讀可寫時,我們需要甄別此時是否已經連接成功,我們採用這種方案:
再次執行connect,然後查看error是否等於EISCONN(表示已經連接到該套接字)。
4. 錯誤
if(FD_ISSET(sockfd, &writeFds))
{
//可讀可寫有兩種可能,一是連接錯誤,二是在連接後服務端已有數據傳來
if(FD_ISSET(sockfd, &readFds))
{
if(connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
{
int error=0;
socklen_t length = sizeof(errno);
//調用getsockopt來獲取並清除sockfd上的錯誤.
if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &length) < 0)
{
printf("get socket option failed\n");
close(sockfd);
return -1;
}
if(error != EISCONN)
{
perror("connect error != EISCONN");
close(sockfd);
return -1;
}
}
}
//此時已排除所有錯誤可能,表明連接成功
fcntl(sockfd, F_SETFL, oldOption);
return sockfd;
}0
4. 恢復socket
因為我們只是需要將連接操作變為非阻塞,並不包括讀寫等,所以我們吃醋要將socket重新設置。
fcntl(sockfd, F_SETFL, oldOption);關於Linux命令的介紹,看看《linux就該這么學》,具體關於這一章地址3w(dot)linuxprobe/chapter-02(dot)html
❷ linux網路編程中阻塞和非阻塞socket的區別
阻塞的是意思是這樣:read函數讀的時候,如果此時數據包沒有來,那就程序就會暫停執行,在read函數裡面暫停。它如何繼續執行呢?那就是數據包來之後它繼續執行。非阻塞就是說,如何執行read函數的時候,數據包沒有,那麼read函數返回沒有讀到任何東西,如果執行read函數時候恰好有數據包,那麼read函數將返回讀到的數據包。也就是說,阻塞的socket使用read的時候,你都能保證讀到數據包。而非阻塞就不一定了,所以往往非阻塞需要配合循環,不停的讀,或者設置一個超時。如果讀了幾次,或者等待了多少秒沒有讀到,就超時。阻塞的,無法控制時間。
❸ 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 到一部分的情況.
❹ linux網路編程中阻塞和非阻塞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);
❺ 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 中的read系統調用到底是阻塞還是非阻
所謂阻塞,即當內核發現請求條件不滿足時(可能需要產生IO)將調用進程掛起,讓出CPU給需要的進程執行,提高效率,調用者進程被阻塞至條件滿足時再被喚醒。
我們來深入跟蹤read/write系統調用,因為Linux內核中對文件的讀寫採用了緩存,文件數據按照頁面(默認大小為4096位元組)為單位緩存在內存中,對於read系統調用,內核會根據應用程序發出的讀偏移在緩存中查找所讀位置對應的緩存頁面是否存在,如果存在,那麼萬事大吉,只需將數據從緩存頁面至用戶緩沖區即可,但如果此頁面尚未被緩存,那麼沒有別的辦法,只能從磁碟上讀出該頁面數據並緩存在內存中,所謂的讀過程,其實文件系統所需做的只是鎖定頁面,然後構造一個讀請求,並將請求發送給底層的IO子系統即可。文件系統發送完請求並不代表該頁面已經從磁碟中讀出,如果此時read系統調用返回,那就意味著該調用是非阻塞,不等IO完成即返回至調用者,但閱讀內核代碼發現,文件系統在發送完IO請求後並不立即返回,而是在接下來的流程中去嘗試鎖定該讀頁面,因為在前面文件系統發IO請求時頁面已經被鎖定,因此,如果頁面尚未被讀出的話,此時鎖定的話必然會阻塞,至此,我們就清楚了Linux內核中的read系統調用默認實現是阻塞方式。
❼ linux下阻塞,非阻塞,輪詢
用淺顯的話來說吧。
在一般的情況下,在系統和應用程序之間有一個請求隊列層,起到調度的作用,應用程序不會直接訪問系統,而是把訪問請求放進隊列層中;而系統也在不停的從隊列層中提取請求然後不斷的分發執行,這種請求方式就是阻塞式訪問。
但是有些特殊的請求是不允許停止和等待的,這種請求就不會被放入隊列層中,而是直接插入到系統的當前處理的前端,而被優先執行,這種請求方式就是非阻塞式訪問。
這二者的區別是由於其工作性質決定的,單純從理論角度來說,與CPU佔用等沒有任何關系,CPU佔用只和和演算法復雜度有關。
一般非阻塞功能都是使用在系統級的請求上,比如某些驅動級的中斷請求或實時類請求,因為繞過了請求隊列,編制不良的非阻塞程序可能會導致系統失去響應。