c語言非阻塞
① c語言中select函數的作用
在編程的過程中,經常會遇到許多阻塞的函數,好像read和網路編程時使用的recv, recvfrom函數都是阻塞的函數,當函數不能成功執行的時候,程序就會一直阻塞在這里,無法執行下面的代碼。這是就需要用到非阻塞的編程方式,使用selcet函數就可以實現非阻塞編程。
selcet函數是一個輪循函數,即當循環詢問文件節點,可設置超時時間,超時時間到了就跳過代碼繼續往下執行。
Select的函數格式:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval*timeout);
select函數有5個參數
第一個是所有文件節點的最大值加1,如果我有三個文件節點1、4、6,那第一個參數就為7(6+1)
第二個是可讀文件節點集,類型為fd_set。通過FD_ZERO(&readfd);初始化節點集;然後通過FD_SET(fd, &readfd);把需要監聽是否可讀的節點加入節點集
第三個是可寫文件節點集中,類型為fd_set。操作方法和第二個參數一樣。
第四個參數是檢查節點錯誤集。
第五個參數是超時參數,類型為struct timeval,然後可以設置超時時間,分別可設置秒timeout.tv_sec和微秒timeout.tv_usec。
然後調用select函數,用FD_ISSET()函數判斷節點是否可讀寫。返回值不為0表示可讀寫,為0表示不可讀寫。select函數的返回值為是一個整數,表示有幾個節點可讀寫。
先說明兩個結構體:
第一,struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符(filedescriptor),即文件句柄,這可以是我們所說的普通意義的文件,當然Unix下任何設備、管道、FIFO等都是文件形式,全部包括在內,所以毫無疑問一個socket就是一個文件,socket句柄就是一個文件描述符。fd_set集合可以通過一些宏由人為來操作,比如清空集合FD_ZERO(fd_set *),將一個給定的文件描述符加入集合之中FD_SET(int ,fd_set*),將一個給定的文件描述符從集合中刪除FD_CLR(int,fd_set*),檢查集合中指定的文件描述符是否可以讀寫FD_ISSET(int ,fd_set* )。
第二,struct timeval是一個大家常用的結構,用來代表時間值,有兩個成員,一個是秒數,另一個是毫秒數。
具體解釋select的參數:
int maxfdp是一個整數值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!在Windows中這個參數的值無所謂,可以設置不正確。
fd_set * readfds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的讀變化的,即我們關心是否可以從這些文件中讀取數據了,如果這個集合中有一個文件可讀,select就會返回一個大於0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的讀變化。
fd_set * writefds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的寫變化的,即我們關心是否可以向這些文件中寫入數據了,如果這個集合中有一個文件可寫,select就會返回一個大於0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的寫變化。
fd_set * errorfds同上面兩個參數的意圖,用來監視文件錯誤異常。
struct timeval * timeout是select的超時時間,這個參數至關重要,它可以使select處於三種狀態,第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化為止;第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大於0,這就是等待的超時時間,即select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。
返回值:返回狀態發生變化的描述符總數。
負值:select錯誤
正值:某些文件可讀寫或出錯
0:等待超時,沒有可讀寫或錯誤的文件
② C語言阻塞、非阻塞和多線程有什麼關系
這個問題我知道!說到阻塞和非阻塞的概念,就要了解同步和非同步的概念吧同步:多個線程可以同時訪問同一個資源。比如對一個變數而言,線程們可以同時對他進行讀寫。使用場景:多個線程同時訪問一塊數據,也叫共享區。對於多個線程同時訪問一塊數據的時候,必須使用同步,否則可能會出現不安全的情況。比如資料庫中的臟讀。但是,多個線程同時訪問一塊數據,有一種情況不需要同步技術,那就是原子操作,也就是說操作系統在底層保證了操作要麼全部做完,要麼不做。非同步:使用場景:只有一個線程訪問當前的數據。比如,觀察者模式,沒有共享區,主題發生變化,通知觀察者更新,主題繼續做自己的事情,不需要等待觀察者更新完成後再工作。同步分為阻塞IO和非同步IO非同步可以分為阻塞IO和非阻塞的IO非同步阻塞IO通過select和epoll實現
③ C語言如何在子線程中等待輸入的同時(scanf或者getchar等等之類的輸入),不阻塞其他線程去進行輸出
要用_kbhit()函數, 非阻塞
//direct代表方向:0-向右,1-向下,2-向左,3-向上
while(1)
{
if(_kbhit())//如果按下的是方向鍵或功能鍵,_getch()要調用兩次,第一次返回0XE0或0
{
c=_getch();//上:72下:80左:75右:77
if(c==0XE0||c==0) c=_getch();
if(c==72&&(direct!=1||direct!=3))
{
direct=3;
}
elseif(c==80&&(direct!=1||direct!=3))
{
direct=1;
}
elseif(c==75&&(direct!=0||direct!=2))
{
direct=2;
}
elseif(c==77&&(direct!=0||direct!=2))
{
direct=0;
}
}
}
④ C語言 阻塞,非阻塞和多線程有什麼關系
阻塞是在傳統的網路編程中我們依賴於ServerSocket,Socket進行通信,大致的框架就是ServerSocket調用accept方法,等待客戶端的連接,如果連接進來的時候則創建一個伺服器端socket,客戶端和伺服器端socket建立好InputStream 和outputStream通道進行通信,在這個網路IO的過程中inputStream的read 和outputStream的write方法都可能發送阻塞。為了減少這種阻塞對其他連接的影響,一般都會在伺服器端為每個連接開辟一個新的線程,或者使用線程池技術來避免線程的創建銷毀同時又一定程度支持並發量。然而這種情況下,如果發生大量的read 或者write阻塞線程池的效率會大大降低,而且操作系統也額外需要頻繁的處理cpu的切換。
非阻塞式通信實際是對上述模式的擴展,它的核心思想是為傳統的socket加入事件監聽的功能,操作系統可以在socket和serversocket上進行事件監聽,一旦監聽的對象發生了連接和可讀可寫的事件,監聽器就會對注冊了事件的對象返回相應的通知。在javaNIO中實現這一套的機制就是把socket 和ServerSocket重寫成為SocketChanel,ServerSocketChanel,他們的底層仍然使用socket實現,所以原則上javaNIO包可以完全實現阻塞和非阻塞兩種編程模式。事件監聽的功能由Selection類完成,他使用select方法一直阻塞式監聽注冊了的事件是否發生,對於每一個發生的事件,他都會返回一個selectionKey,通過這個key我們就可以確定這個事件的發生源(socket)和相關信息。對於ServerSocketChanel,Socketchanel分別對應了不同的事件,serverChanel只有OP_ACCEPT代表是否可以接受連接,而socketChanel則有OP_CONNECT、read、write事件。筆者認為與阻塞IO相比他的優勢在於可以避免read 和write的阻塞,因為這個比較具有實際意義的。比如是一個網路文件傳輸系統,read方法可能會因為網路原因發生多次阻塞,使用非阻塞IO read的話線程可以立即返回去處理其他任務。
多線程是在進程中進一步去劃分的獨立單元。
⑤ c語言中非阻塞單線程輸入循環怎麼做
繼承Thread類,須重寫父類的run()方法,另一種:實現Runnable介面,也是實現run()方法。run() 方法很重要,是當你新建一個線程,運行時的核心,注意,你不要自己去調用run()方法,如果你自己調用,就只是你寫那行調用代碼,運行那行代碼的線程在執行該方法,而不是你新建線程執行的。這一點,也是我之前多線程編程常混亂的。
說到這,如何新建一個線程呢?很簡單: Thread newThread = new Thread( 參數) ; 構造方法中的參數,為前面兩種之前的實現類的一個實例。那麼當你調用了: newThread.start()方法後,就新啟動了一個線程,那麼當該線程執行了run(),即參數中實例的run()方法,就是該線程執行的主體。
注意: 一個線程執行的主體run()方法,是不需要你顯示調用的。
⑥ C語言Socket通信 非阻塞問題 誰能幫我解釋解釋這段代碼呀
非堵塞通信,可以使用MFC的CAsyncSocket類。
⑦ 求高手 詳解阻塞函數與非阻塞函數的區別。再解釋一下getch()和kbhit()的組合應用實例詳解
阻塞
阻塞調用是指調用結果返回之前,當前線程會被掛起。函數只有在得到結果之後才會返回。有人也許會把阻塞調用和同步調用等同起來,實際上他是不同的。對於同
步調用來說,很多時候當前線程還是激活的,只是從邏輯上當前函數沒有返回而已。例如,我們在CSocket中調用Receive函數,如果緩沖區中沒有數
據,這個函數就會一直等待,直到有數據才返回。而此時,當前線程還會繼續處理各種各樣的消息。如果主窗口和調用函數在同一個線程中,除非你在特殊的界面操
作函數中調用,其實主界面還是應該可以刷新。socket接收數據的另外一個函數recv則是一個阻塞調用的例子。當socket工作在阻塞模式的時候,
如果沒有數據的情況下調用該函數,則當前線程就會被掛起,直到有數據為止。
非阻塞
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回。
getch():
所在頭文件:conio.h
函數用途:從控制台讀取一個字元,但不顯示在屏幕上
函數原型:int getch(void)
返回值:讀取的字元
例如:
char ch;或int ch;
getch();或ch=getch();
用getch();會等待你按下任意鍵,再繼續執行下面的語句;
用ch=getch();會等待你按下任意鍵之後,把該鍵字元所對應的ASCII碼賦給ch,再執行下面的語句。
易錯點:
1.所在頭文件是conio.h。而不是stdio.h。
2.在使用之前要調用initscr(),結束時要調用endwin()。否則會出現不輸入字元這個函數
也會返回的情況。
3.在不同平台,輸入回車,getch()將返回不同數值,而getchar()統一返回10(即\n)
1)windows平台下ENTER鍵會產生兩個轉義字元 \r\n,因此getch返回13(\r)。
2)unix、 linux系統中ENTER鍵只產生 \n ,因此getch返回10(\n)。
3)MAC OS中ENTER鍵將產生 \r ,因此getch返回13(\r)。
getch();並非標准C中的函數,不存在C語言中。所以在使用的時候要注意程序的可移植性。國內C語言新手常常使用getch();來暫停程序且不知道此函數來源,建議使用getchar();(如果情況允許)代替此功能或更換一款編譯器。
kbhit()(VC++6.0下為_kbhit())
功 能及返回值: 檢查當前是否有鍵盤輸入,若有則返回一個非0值,否則返回0
用 法:int kbhit(void);
包含頭文件: include <conio.h>
編輯本段程序示例
C語言
#include<conio.h>
int main(void)
{
cprintf("Press any key to continue:");
while (!kbhit()) /* do nothing */ ;
cprintf("\r\nA key was pressed...\r\n");
return 0;
}
下面的代碼,如果沒有鍵盤輸入程序一直輸出Hello World,直到用戶按Esc結束
#include <conio.h>
#include <stdlib.h>
int main( void )
{
char ch;
while( !kbhit() )
{
cprintf("Hello World\n");
if( kbhit() )
ch = getch();
if( 27 == ch )
break;
}
cprintf("End!\n");
system("pause");
return 0;
}
C++語言
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
while(!kbhit()) //當沒有鍵按下
{
cout<<"無鍵按下"<<endl;
}
cout<<"有鍵按下"<<endl; //有鍵按下時輸出這
system("pause");
}
kbhit() 在執行時,檢測是否有按鍵按下,有按下返回非0值,一般是1
沒有按下返回0;是非阻塞函數
getch() 在執行時,檢測按下什麼鍵,如果不按鍵該函數不返回;是阻塞函數
類似地
在Tc2.0中有一個處理鍵盤輸入的函數bioskey();
int bioskey(int cmd);
當cmd為1時,bioskey()檢測是否有鍵按下。沒有鍵按下時返回0;有鍵按下時返回按鍵碼(
任何按鍵碼都不為0),但此時並不將檢測到的按鍵碼從鍵盤緩沖隊列中清除。 是非阻塞參數。
當cmd為0時,bioskey()返回鍵盤緩沖隊列中的按鍵碼,並將此按鍵碼從鍵盤緩沖隊列中清
除。如果鍵盤緩沖隊列為空,則一直等到有鍵按下,才將得到的按鍵碼返回。是阻塞調用。
//個人理解kbhit()有點像bioskey(1)
⑧ c語言的recv()非阻塞方法怎麼弄哦
需要將recv設置超時,Linux下設置超時如下:
//設置發送超時
struct timeval timeout={3,0};//3s
setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));
//設置接收超時
setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
windows下設置超時如下:
int timeout = 3000; //3s
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));
⑨ windows下C語言非阻塞方式讀取鍵盤緩沖區
有所不同。伺服器是網路的節點,存儲、處理網路上80%的數據、信息,在網路中起到舉足輕重的作用。它們是為客戶端計算機提供各種服務的高性能的計算機,其高性能主要表高速度的運算能力、長時間的可靠運行、強大的外部數據吞吐能力等方面。伺服器的構成與普通電腦類似,
⑩ udp非阻塞怎麼寫 linuxc語言
int flag=fcntl(fd,F_GETFL,0); flag |= O_NONBLOCK; if(fcntl(fd,F_SETFL,flag) < 0){ perror("fcntl"); exit(1); }