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