首先先說一下,阻塞IO會在哪些地方阻塞住呢?輸入操作read, 輸出操作write,接受請求操作accept,發送請求操作connect,這四個地方阻塞進程。
非阻塞IO的模型圖示在前面的章節有講過,它和阻塞IO的最大區別就是:如果連接或者操作不能立即建立,那么連接的建立照樣能發起,只是會返回一個錯誤信息。
同樣,先說明幾個用到的函數和操作:
1 fcntl函數
其全名為”file control“。顧名思義,fcntl可以執行各種操作符控制操作。
#include <fcntl.h> int fcntl(int fd, int cmd, .. /* int arg */)
第一個參數fd是文件描述符
第二個參數cmd是操作命令,比如設置套接字阻塞非阻塞的命令為F_SETFL, 設置套接字屬主的命令為F_SETOWN
第三個參數以后,是操作命令的參數。比如設置非阻塞IO型的F_SETFL的參數為O_NONBLOCK
所以設置非阻塞IO的典型設置代碼為:
flags = flags | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
2 非阻塞IO返回的錯誤
對於不能滿足的非阻塞IO操作,System V會返回EAGAIN錯誤,而源自Berkeley的實現返回EWOULDBLOCK。大多數當前系統把這兩個錯誤碼定義為相同的值。
對不能滿足的非阻塞IO連接,系統會返回EINPROGRESS
按照非阻塞的定義,我們只需要將cli做下面修改:
3客戶端代碼
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc, char* argv[])
{
int socketfd, n;
socketfd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(socketfd, F_SETFL, O_NONBLOCK);
struct sockaddr_in serv_addr;
bzero((char *)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(7777);
for(;;) {
if(n = connect(socketfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
if(errno == EINPROGRESS) {
printf("EINPROGRESS\n");
}
} else {
break;
}
}
write(socketfd, "client message", 14);
char buffer[256];
bzero(buffer, 256);
read(socketfd, buffer, 255);
printf("server return message:%s\r\n", buffer);
return 0;
}
運行方式:
1 server不啟動
2 client啟動,則會在connect這個地方進入無限循環。
好吧,是不是覺得有問題?
1 這種模型,客戶端使用輪詢不斷調用IO操作,那么,CPU就會一直用於輪詢,造成cpu的浪費。
2 這種模型,代碼量比阻塞的模型大很多
所以這個模型實際上是很少使用的。
