网络编程06-服务器编程非阻塞IO、多路复用
生活随笔
收集整理的這篇文章主要介紹了
网络编程06-服务器编程非阻塞IO、多路复用
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
目錄
一、服務(wù)器編程中四種高性能IO模型
1、阻塞IO
2、非阻塞 IO
3、多路復(fù)用
4、信號(hào)驅(qū)動(dòng)
二、阻塞IO
三、非阻塞IO?
1、阻塞IO與非阻塞IO之間的差異
?2、如何給文件描述符設(shè)置非阻塞屬性----fcntl() ----- man 2 fcntl
四、多路復(fù)用
1、同時(shí)監(jiān)聽多個(gè)套接字IO口
2、什么是多路復(fù)用
3、特點(diǎn)
4、多路復(fù)用的函數(shù)接口-------select -----man 2 select
Client.c
一、服務(wù)器編程中四種高性能IO模型
?
1、阻塞IO
1)read(fd,buf) ; recv(fd) ; recvfrom(fd) 這些函數(shù)本身是不具有阻塞屬性,而是這個(gè)文件描述符的本身阻塞屬性導(dǎo)致這個(gè)函數(shù)執(zhí)行表現(xiàn)出來的形式是阻塞。 2)在默認(rèn)的情況下,Linux下建立的socket套接字 都是 阻塞的。2、非阻塞 IO
1)給文件描述符添加非阻塞屬性 2)由于非阻塞屬性,所以不斷詢問套接字中是否有數(shù)據(jù)到達(dá)3、多路復(fù)用
1)同時(shí)對(duì)多個(gè)IO口進(jìn)行操作(也就是同時(shí)監(jiān)聽?zhēng)讉€(gè)套接字) 2)可以在規(guī)定的時(shí)間內(nèi)檢測(cè)數(shù)據(jù)是否到達(dá)4、信號(hào)驅(qū)動(dòng)
1)屬于異步通信方式 2)當(dāng)socket中有數(shù)據(jù)到達(dá)時(shí),通過發(fā)送信號(hào)告知用戶二、阻塞IO
讀阻塞:當(dāng)數(shù)據(jù)緩沖區(qū)中沒有數(shù)據(jù)可以讀取時(shí),調(diào)用read/recv/recvfrom就會(huì)無限阻塞。 寫阻塞:當(dāng)緩沖區(qū)剩余的大小 小于 寫入的數(shù)據(jù)量,就會(huì)發(fā)生寫阻塞,直到緩沖區(qū)中的數(shù)據(jù)被讀取了三、非阻塞IO?
1、阻塞IO與非阻塞IO之間的差異
?
?2、如何給文件描述符設(shè)置非阻塞屬性----fcntl() ----- man 2 fcntl
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 參數(shù) ????????fd : 你要 設(shè)置 哪個(gè) 文件,將這個(gè)文件的文件描述符傳遞進(jìn)來 ????????cmd: 請(qǐng)求控制的命令字 ????????arg:這個(gè)參數(shù)要不要填,取決于第二個(gè)參數(shù) ????????第二個(gè)參數(shù):?
比如:
int fd = open("xxx"); int status = fcntl(fd, F_GETFL ); //得到文件描述符的狀態(tài) status |= O_NONBLOCK ;//在原來的基礎(chǔ)上新增非阻塞屬性 fcntl(fd, F_SETFL,status); //把變量status的狀態(tài)設(shè)置到文件描述符中?
返回值 ????????F_GETFL Value of file status flags. ????????F_SETFL ????????成功返回0 ????????失敗返回 -1 例子1:設(shè)置一個(gè)非阻塞屬性給套接字,看看這個(gè)套接字還會(huì)不會(huì)阻塞等待客戶端連接??
例子2: 使用TCP通信 將其修改為非阻塞屬性
1 #include<stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <string.h> 7 #include <unistd.h> 8 #include <fcntl.h> 9 #define OWNADDR "192.168.14.3" //我自己電腦的ip地址 10 #define OWNPORT 20000 //我自己電腦的該程序的端口號(hào) 12 int main() 13 { printf("當(dāng)前是服務(wù)器 IP:%s Port:%u\n",OWNADDR,OWNPORT); 14 //1、買手機(jī)(建立套接字) 15 int socketfd = socket(AF_INET, SOCK_STREAM, 0); 16 if(socketfd == -1) 17 { 18 printf("沒錢了....,失敗\n"); 19 return -1; 20 } 21 //因?yàn)榉?wù)器立馬退出之后,端口號(hào)不會(huì)及時(shí)釋放 //此時(shí)如果服務(wù)器又馬上運(yùn)行,那么端口號(hào)會(huì)被占用,導(dǎo)致服務(wù)器分配端口號(hào)失敗,連接失敗 23 //所以設(shè)置端口號(hào)可以復(fù)用 24 int optval = 1; 25 setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval)); 26 27 //2、綁定自己的電話號(hào)碼(綁定自己的IP地址 和端口號(hào)) 28 //定義一個(gè)IPV4結(jié)構(gòu)體變量,初始化自己的IP地址和端口號(hào) 29 struct sockaddr_in ownAddr; 30 ownAddr.sin_family = AF_INET;/*地址族 IPV4*/ 31 ownAddr.sin_port = htons(OWNPORT); //htons 將本地端口號(hào)轉(zhuǎn)為網(wǎng)絡(luò)端口號(hào) 32 ownAddr.sin_addr.s_addr = inet_addr(OWNADDR); //將本地IP地址轉(zhuǎn)為網(wǎng)絡(luò)IP地址 33 34 bind(socketfd, (struct sockaddr *)&ownAddr,sizeof(struct sockaddr_in)); 35 36 //3、設(shè)置鈴聲(監(jiān)聽) listen 37 listen(socketfd,5); 38 39 //4、坐等電話(阻塞接收客戶端的連接) 40 printf("等待客戶端連接.......\n"); 41 //定義一個(gè)IPV4結(jié)構(gòu)體變量,存儲(chǔ)連接上來的客戶端的IP地址 和 端口號(hào) 42 struct sockaddr_in clientAddr; 43 //如果你要想要獲取對(duì)方的IP地址和端口號(hào),第三個(gè)參數(shù)必須把結(jié)構(gòu)體的大小傳遞進(jìn)去 44 int len = sizeof(struct sockaddr_in); 45 46 //給 socketfd設(shè)置非阻塞屬性 47 int status = fcntl(socketfd,F_GETFL);//得到這個(gè)套接字文件描述符的屬性 48 //將得到的文件描述符的全部屬性 中的 其中一個(gè)屬性設(shè)置成 非阻塞 49 status |= O_NONBLOCK; 50 int ret = fcntl(socketfd,F_SETFL,status);//把變量status的狀態(tài)設(shè)置到文件描述符中 51 if(ret == -1) 52 { 53 printf("fcntl error\n"); 54 55 } 56 while(1) 57 { 58 //此時(shí)是非阻塞,會(huì)一直不斷地循環(huán) 59 int newClientFd = accept(socketfd,(struct sockaddr*)&clientAddr,&len); 60 if(newClientFd != -1) 61 { //printf("有客戶端連接上來了............\n"); 63 //打印連接上來的客戶端的IP地址和端口號(hào),將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)為 本地字節(jié)序 64 printf("連接上來的客戶端IP:%s 端口 號(hào):%u\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port)); 65 } 66 //printf("11111111111\n"); 67 } 68 69 //5、關(guān)閉 70 close(socketfd); 71 //close(newClientFd); 72 73 return 0; 74 75 }?
例題1: 寫一個(gè)服務(wù)器,最多可以接受1000個(gè)客戶端連接,可以隨時(shí)接受連接。 只要連接上服務(wù)器的客戶端有數(shù)據(jù)達(dá)到,那么就把數(shù)據(jù)打印出來。要求非阻塞IO實(shí)現(xiàn)。不能開線程 思路: 最多可以接受20個(gè)客戶端連接 ---> 用戶連接成功 ---> 判斷滿人 --> 拒絕連接 --> 斷開該用戶 2 ---> 用戶連接成功 ---> 未滿人 --> 保存套接字到數(shù)組 3 4 5 struct ciient{ int clifd[1000]; //已連接的用戶的套接字 6 int count; //已連接用戶的總數(shù) 7 8 } 9 10 非阻塞IO:不斷詢問這些已連接的用戶以及是否有新的人連接過來! 11 while(1) 12 { 13 int clifd[20]; ??? 有沒有數(shù)據(jù)來? 14 } 1 #include<stdio.h> 2 #include<stdio.h> 3 #include <sys/types.h> /* See NOTES */ 4 #include <sys/socket.h> 5 #include <sys/socket.h> 6 #include <arpa/inet.h> 7 #include <unistd.h> 8 #include<stdlib.h> 9 #include <string.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <sys/wait.h> 13 #include <fcntl.h> 14 15 //#include <pthread.h> 16 17 #define SERVER_PORT 9999 18 19 20 struct client{ int clientFd_Array[1000] ;//存儲(chǔ)所有連接上來的客戶端的套接字文件描述符 21 int clientCount; //連接上來的客戶端的總數(shù) 22 23 }; 24 25 26 void sys_err(const char*err) 27 { fprintf(stderr,"%s\n",strerror(errno)); 28 exit(0); 29 30 } 31 32 33 int main() 34 { int ret; 35 struct sockaddr_in clientAddr;//存儲(chǔ)連接上來的客戶端的IP地址和端口號(hào) 36 int len = sizeof(struct sockaddr_in); 37 struct client clientManager; //客戶端 結(jié)構(gòu)體管理變量 38 39 //初始化結(jié)構(gòu)體 40 clientManager.clientCount = 0; 41 42 printf("服務(wù)器 Port:%d PID:%d \n",SERVER_PORT,getpid()); 43 44 //1、建立套接字 45 int socketFd = socket(AF_INET,SOCK_STREAM, 0); 46 if(socketFd == -1){ 47 sys_err("socket error"); 48 } 49 //端口復(fù)用 50 int optval = 1; 51 setsockopt(socketFd,SOL_SOCKET,SO_REUSEADDR,&optval, sizeof(optval)); 52 53 //2、綁定自己的IP地址和端口號(hào) 54 struct sockaddr_in serverAddr; 55 serverAddr.sin_family = AF_INET; 56 serverAddr.sin_port = htons(SERVER_PORT);//short 57 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 58 59 ret = bind(socketFd,(struct sockaddr*)&serverAddr,sizeof(struct sockaddr_in)); 60 if(ret == -1){ 61 sys_err("bind error"); 62 } 63 64 //3、設(shè)置監(jiān)聽 65 listen(socketFd,128); 66 67 //設(shè)置該監(jiān)聽套接字 為 非阻塞屬性 68 //第一步 先得到原來該套接字原來的屬性 69 int state = fcntl(socketFd,F_GETFL); 70 //第二步 在原來的基礎(chǔ)上新增 一個(gè)非阻塞 屬性 71 state |= O_NONBLOCK; // state = state | O_NONBLOCK 72 //第三步 將 新的屬性 設(shè)置回 文件描述符中 73 fcntl(socketFd,F_SETFL,state); 74 75 while(1){ 76 //4、接收新的客戶端連接.... 77 int newClientFd = accept(socketFd, (struct sockaddr*)&clientAddr,&len); 78 if(newClientFd > 0) //說明有客戶端連接上來了 79 { 80 printf("有新的客戶端連接上來 IP:%s Port:%hu newClientFd:%d\n", 81 inet_ntoa(clientAddr.sin_addr), 82 ntohs(clientAddr.sin_port), 83 newClientFd); 84 85 //將客戶端文件描述符的屬性 設(shè)置成 非阻塞屬性 86 //第一步 先得到原來該套接字原來的屬性 87 int state = fcntl(newClientFd,F_GETFL); 88 //第二步 在原來的基礎(chǔ)上新增 一個(gè)非阻塞 屬性 89 state |= O_NONBLOCK; // state = state | O_NONBLOCK 90 //第三步 將 新的屬性 設(shè)置回 文件描述符中 91 fcntl(newClientFd,F_SETFL,state); 92 93 94 //將每一個(gè)連接上來的客戶端存儲(chǔ)到結(jié)構(gòu)體變量中 95 clientManager.clientFd_Array[clientManager.clientCount] = newClientFd; 96 clientManager.clientCount++; 97 } 98 99 //挨個(gè)輪詢查看 連接上來的客戶端 是否有數(shù)據(jù)到達(dá) 100 for(int i=0; i<clientManager.clientCount; i++){ 101 char buf[1024] = {0}; 102 int ret = recv(clientManager.clientFd_Array[i],buf, sizeof(buf), 0); 103 if(ret == 0) //客戶端斷開了 104 { 105 //printf("客戶端退出了....\n"); 106 //sleep(1); 107 break; 108 } 109 else if(ret > 0) //有數(shù)據(jù) 來了,可以進(jìn)行打印 110 { 111 printf("recv:%s\n",buf); 112 } 113 } 114 115 116 } 117 118 //關(guān)閉 119 close(socketFd); return 0; 123 124 }四、多路復(fù)用
1、同時(shí)監(jiān)聽多個(gè)套接字IO口
阻塞IO ---->只能同時(shí)監(jiān)聽一個(gè)套接字 非阻塞IO--->一直輪詢 問IO口有沒有數(shù)據(jù)到達(dá),非常浪費(fèi)CPU資源2、什么是多路復(fù)用
就是預(yù)先把需要監(jiān)聽的文件描述符加入到一個(gè)集合,然后再規(guī)定的時(shí)間內(nèi)或者無限時(shí)間進(jìn)行等待。如果在規(guī)定的時(shí)間內(nèi),集合中文件描述符沒有數(shù)據(jù)變化,則說明超時(shí)接收,并進(jìn)入下一次規(guī)定的時(shí)間再次等待。一旦集合中的文件描述符有數(shù)據(jù)變化,則其他的沒有數(shù)據(jù)變化的文件描述符就會(huì)被剔除到集合之外,并且再次進(jìn)入下一次的等待狀態(tài)。3、特點(diǎn)
同時(shí)對(duì)多個(gè)IO口進(jìn)行監(jiān)聽。4、多路復(fù)用的函數(shù)接口-------select -----man 2 select
#include <sys/select.h> 2 #include <sys/time.h> 3 #include <sys/types.h> 4 #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); //poll epoll 參數(shù): ????????nfds: 三個(gè)集合中 最大的文件描述符的值 + 1,因?yàn)榇藚?shù)會(huì)告訴內(nèi)核檢測(cè)前多少個(gè)文件描述符的狀態(tài) ????????readfds:監(jiān)控有讀數(shù)據(jù)到達(dá)文件描述符集合,傳入傳出參數(shù) ????????writefds: 監(jiān)控寫數(shù)據(jù)到達(dá)文件描述符集合,傳入傳出參數(shù) ????????exceptfds: 監(jiān)控異常發(fā)生達(dá)文件描述符集合,如帶外數(shù)據(jù)到達(dá)異常,傳入傳出參數(shù) ????????timeout:設(shè)置阻塞等待時(shí)間,3種情況 ????????????????1.設(shè)置NULL,永遠(yuǎn)等下去,這個(gè)函數(shù)是阻塞(無限等待,直到有文件描述符的狀態(tài)發(fā)生變化) ????????????????2.設(shè)置timeval,等待固定時(shí)間 ????????????????3.設(shè)置timeval里時(shí)間均為0,檢查描述字后立即返回,輪詢 ????????????????---> 如果這個(gè)參數(shù)填NULL,則說明這個(gè)函數(shù)是阻塞(無限等待,直到有文件描述符的狀 態(tài)發(fā)生變化) struct timeval { 2 long tv_sec; /* seconds 秒*/ 3 long tv_usec; /* microseconds 微秒*/ 4 }; 5 void FD_CLR(int fd, fd_set *set); //把文件描述符集合里fd清0 6 int FD_ISSET(int fd, fd_set *set); //測(cè)試文件描述符集合里fd是否置1 7 void FD_SET(int fd, fd_set *set); //把文件描述符集合里fd位置1 8 void FD_ZERO(fd_set *set); //把文件描述符集合里所有位清0 返回值: 成功 有數(shù)據(jù)到達(dá) ---->狀態(tài)發(fā)生變化的文件描述符的總數(shù) 沒有數(shù)據(jù)到達(dá),超時(shí)-->0 失敗 -1 注意: 1、select能監(jiān)聽的文件描述符個(gè)數(shù)受限于FD_SETSIZE,一般為1024,單純改變進(jìn)程打開的文件描述符 個(gè)數(shù)并不能改變select監(jiān)聽文件個(gè)數(shù) 2、解決1024以下客戶端時(shí)使用select是很合適的,但如果鏈接客戶端過多,select采用的是輪詢模 型,會(huì)大大降低服務(wù)器響應(yīng)效率 3、可以進(jìn)行跨平臺(tái) /*4_accept非阻塞IO輪訓(xùn)服務(wù)器*/ #include <stdio.h> #include <unistd.h> #include <string.h> /*socket*/ #include <sys/types.h> /* See NOTES */ //man 2 socket /*inet_addr*/ #include <sys/socket.h> //man 3 inet_addr #include <netinet/in.h> #include <arpa/inet.h> /*fcntl*/ #include <fcntl.h> //man 2 fcntl#define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//創(chuàng)建套接字int socket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}//所以設(shè)置端口號(hào)可以復(fù)用,這兩條語句放在 綁定bind 之前int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//綁定struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));//監(jiān)聽listen(socket_fd,20);//添加非阻塞屬性到socket_fd套接字中int status = fcntl(socket_fd, F_GETFL); //得到文件描述符的狀態(tài)-->F_GETFLstatus |= O_NONBLOCK ;//在原來的基礎(chǔ)上新增非阻塞屬性fcntl(socket_fd, F_SETFL,status); //把變量status的狀態(tài)設(shè)置到文件描述符中//接收int socket_client;int client_cnt[20],cnt=0,j=0;//初始化客戶端的套接字for(j=0;j<20;j++){client_cnt[j] = -1;} struct sockaddr_in client_addr;socklen_t addrlen = sizeof(client_addr);int ret;char buf[1024] = {0};//在不開進(jìn)程和線程的情況,同時(shí)能夠接受多個(gè)客戶端while(1){//由于socket_fd套接字帶有阻塞屬性,所以導(dǎo)致accept函數(shù)阻塞socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client != -1){char *ip = inet_ntoa(client_addr.sin_addr);int port = ntohs(client_addr.sin_port);printf("有新的客戶端上線 [ip:%s port:%d]\n",ip,port);//將socket_client設(shè)置為非阻塞屬性存放在數(shù)組中int status = fcntl(socket_client, F_GETFL); //得到文件描述符的狀態(tài)-->F_GETFLstatus |= O_NONBLOCK ;//在原來的基礎(chǔ)上新增非阻塞屬性fcntl(socket_client, F_SETFL,status); //把變量status的狀態(tài)設(shè)置到文件描述符中//將所有的客戶端套接字存放在數(shù)組中client_cnt[cnt++] = socket_client; //cnt表示的是數(shù)組里面有效的客戶端套接字//printf("cnt = %d\n",cnt);}#if 0 //查看數(shù)組里面所有的客戶端套接字(調(diào)試用)for(j=0;j<20;j++){printf("%d ",client_cnt[j]); }printf("\n");printf("等待連接中....\n");sleep(1); #endif//遍歷數(shù)組里面所有的套接字(循環(huán)接受每一個(gè)客戶端發(fā)過來的數(shù)據(jù))for(j=0;j<cnt;j++) //cnt是數(shù)組里面有效的套接字{memset(buf,0,sizeof(buf));//默認(rèn)狀態(tài)下recv這個(gè)函數(shù)是阻塞屬性,但是現(xiàn)在的recv是非阻塞的ret = recv(client_cnt[j],buf,sizeof(buf),0);if(ret > 0) //只打印有效的數(shù)據(jù)printf("buf:%s ret:%d\n",buf,ret);}}//關(guān)閉套接字close(socket_fd); }/*5_accept非阻塞IO輪訓(xùn)服務(wù)器ip與端口*/ #include <stdio.h> #include <unistd.h> #include <string.h> /*socket*/ #include <sys/types.h> /* See NOTES */ //man 2 socket /*inet_addr*/ #include <sys/socket.h> //man 3 inet_addr #include <netinet/in.h> #include <arpa/inet.h> /*fcntl*/ #include <fcntl.h> //man 2 fcntl#define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000struct client_info {int cli_socket;struct sockaddr_in cli_addr; };int main(int argc,char **argv) {//創(chuàng)建套接字int socket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}//所以設(shè)置端口號(hào)可以復(fù)用,這兩條語句放在 綁定bind 之前int optval = 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//綁定struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));//監(jiān)聽listen(socket_fd,20);//添加非阻塞屬性到socket_fd套接字中int status = fcntl(socket_fd, F_GETFL); //得到文件描述符的狀態(tài)-->F_GETFLstatus |= O_NONBLOCK ;//在原來的基礎(chǔ)上新增非阻塞屬性fcntl(socket_fd, F_SETFL,status); //把變量status的狀態(tài)設(shè)置到文件描述符中//初始化客戶端的套接字int cnt = 0,j=0;struct client_info info[100];for(j=0;j<100;j++)info[j].cli_socket = -1;struct sockaddr_in client_addr;socklen_t addrlen = sizeof(client_addr);//接收int socket_client; int ret;char buf[1024] = {0};//在不開進(jìn)程和線程的情況,同時(shí)能夠接受多個(gè)客戶端while(1){//由于socket_fd套接字帶有阻塞屬性,所以導(dǎo)致accept函數(shù)阻塞socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);if(socket_client != -1){char *ip = inet_ntoa(client_addr.sin_addr);int port = ntohs(client_addr.sin_port);printf("有新的客戶端上線 [ip:%s port:%d]\n",ip,port);//將socket_client設(shè)置為非阻塞屬性存放在數(shù)組中int status = fcntl(socket_client, F_GETFL); //得到文件描述符的狀態(tài)-->F_GETFLstatus |= O_NONBLOCK ;//在原來的基礎(chǔ)上新增非阻塞屬性fcntl(socket_client, F_SETFL,status); //把變量status的狀態(tài)設(shè)置到文件描述符中//將所有的客戶端套接字存放在數(shù)組中//cnt表示的是數(shù)組里面有效的客戶端套接字info[cnt].cli_socket = socket_client;info[cnt].cli_addr = client_addr;cnt++;}#if 0 //查看數(shù)組里面所有的客戶端套接字(調(diào)試用)for(j=0;j<20;j++){printf("%d ",info[j].cli_socket); }printf("\n");printf("等待連接中....\n");sleep(1); #endif//遍歷數(shù)組里面所有的套接字(循環(huán)接受每一個(gè)客戶端發(fā)過來的數(shù)據(jù))for(j=0;j<cnt;j++) //cnt是數(shù)組里面有效的套接字{memset(buf,0,sizeof(buf));//默認(rèn)狀態(tài)下recv這個(gè)函數(shù)是阻塞屬性,但是現(xiàn)在的recv是非阻塞的 /* ret = recv(client_cnt[j],buf,sizeof(buf),0);if(ret > 0) //只打印有效的數(shù)據(jù)printf("buf:%s ret:%d\n",buf,ret); */ret = recv(info[j].cli_socket,buf,sizeof(buf),0); if(ret > 0) //只打印有效的數(shù)據(jù)printf("[ip:%s port:%d]buf:%s ret:%d\n",inet_ntoa(info[j].cli_addr.sin_addr),ntohs(info[j].cli_addr.sin_port),buf,ret); }}//關(guān)閉套接字close(socket_fd); }/*6_使用多路復(fù)用select實(shí)現(xiàn)TCP服務(wù)器*/ #include<stdio.h> #include<stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include<stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>#define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000void sys_err(const char*err) {fprintf(stderr,"%s\n",strerror(errno));exit(0); }int main() {int ret,max_fd,i=0,imax=0;int client[FD_SETSIZE]; //存儲(chǔ)所有客戶端的文件描述符//該宏系統(tǒng)已經(jīng)定義好了 文件描述符的總數(shù) 1024struct sockaddr_in clientAddr;//存儲(chǔ)連接上來的客戶端的IP地址和端口號(hào)int len = sizeof(struct sockaddr_in);printf("服務(wù)器 Port:%d PID:%d \n",SERVER_PORT,getpid());//1、建立套接字(監(jiān)聽套接字)int socket_fd = socket(AF_INET,SOCK_STREAM, 0);if(socket_fd == -1){sys_err("socket error");//perror("bind"); //兩種錯(cuò)誤條釋放作用一樣}//端口復(fù)用int optval = 1;setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&optval, sizeof(optval));//2、綁定自己的IP地址和端口號(hào)struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);//short//server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //表示本機(jī)任意IP地址server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);ret = bind(socket_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in));if(ret == -1){sys_err("bind error");}//3、設(shè)置監(jiān)聽listen(socket_fd,128);//定義文件描述符集合fd_set rset,allset;//清空集合FD_ZERO(&allset);FD_SET(socket_fd, &allset); //把監(jiān)聽套接字 加入到集合中//最大的文件描述符,在沒有客戶端進(jìn)來之前,最大的文件描述符最大值就是socket_fdmax_fd = socket_fd;//初始化為 -1 給存放客戶端套接字的數(shù)組初始化for(int k=0; k<FD_SETSIZE; k++){client[k] = -1;}//設(shè)置阻塞的時(shí)間struct timeval t;t.tv_sec = 5;t.tv_usec = 0;while(1){rset = allset; //每次循環(huán)的時(shí)候都需要重新設(shè)置select的集合//多路復(fù)用 ,同時(shí)監(jiān)聽 多個(gè)套接字文件描述符的狀態(tài) --阻塞監(jiān)聽//nready表示的是狀態(tài)發(fā)生變化的文件描述符的總數(shù),(不是里面存放了多少個(gè)描述符)int nready = select(max_fd+1,&rset, NULL,NULL, NULL);if(nready == -1){perror("select error");break;}//程序走到這里,說明集合中的文件描述符的狀態(tài)一定發(fā)生變化了//如果是監(jiān)聽套接字文件描述符發(fā)生變化了,說明一定是有新客戶端連接上來了if(FD_ISSET(socket_fd, &rset)){//接收新的客戶端int new_clientfd = accept(socket_fd, (struct sockaddr*)&clientAddr,&len);printf("有新的客戶端連接上來 IP:%s Port:%hu new_clientfd:%d\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port),new_clientfd);//把新的客戶端文件描述符加入到集合中 FD_SET(new_clientfd, &allset);//更新文件描述符最大值if(new_clientfd > max_fd)max_fd = new_clientfd;//將連接上來的客戶端文件描述符 加入到 數(shù)組中for(i=0; i<FD_SETSIZE; i++){if(client[i] <0 ){client[i] = new_clientfd;break;}}//imax = i;//存放客戶端套接字?jǐn)?shù)組里面的最大的下標(biāo)值(可以通過這個(gè)值來記錄你客戶端數(shù)目)//說明該集合中只有監(jiān)聽套接字文件描述符發(fā)生變化if(--nready == 0)continue;} //程序走到這里,說明有客戶端發(fā)送數(shù)據(jù)過來for(i=0; i<FD_SETSIZE; i++){if(client[i] <0)continue;//說明該客戶端發(fā)送數(shù)據(jù)過來了,//客戶端套接字的狀態(tài)就發(fā)生變化這句話if(FD_ISSET(client[i], &rset))就成立if(FD_ISSET(client[i], &rset)){char buf[1024] = {0};int ret = read(client[i],buf,sizeof(buf));if(ret == 0) //該客戶端斷開了{(lán)printf("有客戶端斷開了.....\n");close(client[i]);//關(guān)閉文件描述符FD_CLR(client[i],&allset);//將該客戶端從 文件集合中刪除client[i] = -1; //該文件描述符對(duì)應(yīng)的數(shù)組位置置 -1}printf("recv:%s\n",buf);//說明所有發(fā)生變化的文件描述符 已經(jīng)被我們處理完了,則退出if(--nready == 0)break;} } }//關(guān)閉close(socket_fd); return 0; }Client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /*socket*/ #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> /*ip*/ #include <netinet/in.h> //man 3 inet_addr #include <arpa/inet.h>//服務(wù)器的IP地址 #define SERVER_IP "192.168.11.2" #define SERVER_PORT 60000int main(int argc,char **argv) {//建立套接字--socketint socket_fd;//AF_INET-->ipv4 SOCK_STREAM-->tcpsocket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd < 0){perror("socket fail");return -1;}printf("socket_fd = %d\n",socket_fd);//填充IP地址(服務(wù)器)--新結(jié)構(gòu)體struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; //ipv4server_addr.sin_port = htons(SERVER_PORT);//host to net(本機(jī)端口號(hào)轉(zhuǎn)網(wǎng)絡(luò)端口號(hào))server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//將本機(jī)IP轉(zhuǎn)換為網(wǎng)絡(luò)IP//連接服務(wù)器int ret;ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));if(ret < 0){printf("connect fail\n");return -1;}//給服務(wù)器發(fā)送數(shù)據(jù)char buf[1024] = {0};while(1){ memset(buf,0,sizeof(buf));scanf("%s",buf);ret = send(socket_fd,buf,strlen(buf),0);printf("send success ret:%d\n",ret);}//關(guān)閉套接字close(socket_fd);return 0; }總結(jié)
以上是生活随笔為你收集整理的网络编程06-服务器编程非阻塞IO、多路复用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电信L2-python考题1-完美字符串
- 下一篇: 物流英语与计算机操作,物流英语与计算机模