//網絡編程服務端 /* * 備注:因為客戶端代碼、輔助方法代碼和epoll相同,所以select只展示服務器端代碼 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>//htons()函數頭文件 #include <netinet/in.h>//inet_addr()頭文件 #include <fcntl.h> #include <sys/select.h> #include <time.h> #include "pub.h" #define MAXSOCKET 1024 int main(int arg, char *args[]) { if (arg < 2) { printf("please print one param!\n"); return -1; } //create server socket int listen_st = server_socket(atoi(args[1])); if (listen_st < 0) { return -1; } //設置非阻塞文件描述符 setnonblock(listen_st); int i = 0; int maxfd = 0; //最大的socket,select函數第一個參數使用 /* *建立客戶端連接池 */ int client[MAXSOCKET]; //select最大支持1024個socket連接 //將所有的客戶端連接池初始化,將每個成員都設置為-1,表示無效 for (; i < MAXSOCKET; i++) { client[i] = -1; } maxfd = listen_st; //程序剛開始執行時,只有服務端socket,所以服務端socket最大 //定義一個事件數組結構 fd_set allset; while (1) { //初始化一個fd_set對象 FD_ZERO(&allset); //將服務器端socket放入事件數組allset中(服務端socket需要特殊處理,所以沒有放入socket池中) FD_SET(listen_st, &allset); //先假設最大的socket就是服務器端socket maxfd = listen_st; //遍歷socket池 找出值最大的socket for (i = 0; i < MAXSOCKET; i++) { if (client[i] != -1) { //將socket池中的所有socket都添加到事件數組allset中 FD_SET(client[i], &allset); if (client[i] > maxfd) { maxfd = client[i]; //maxfd永遠是值最大的socket } } } //開始等待socket發生讀事件 int rc = select(maxfd + 1, &allset, NULL, NULL, NULL); /* * select函數返回之后,allset數組的數據產生變化,現在allset數組里的數據是發生事件的socket * select和epoll不同,select每次返回后, * 會清空select池中的所有socket,所有的socket等select返回后就被清除了 * 所以必須由程序建立一個socket池,每次都將socket池中的socket加入到select池中 * select不會為程序保存socket信息,這與epoll最大的不同, * epoll添加到events中的socket,如果不是程序員清除,epoll永遠保留這些socket */ if (rc < 0) { //select函數出錯,跳出循環 printf("select failed ! error message:%s\n", strerror(errno)); break; } //判斷是否是服務器socket接收到數據,有客戶端連接 if (FD_ISSET(listen_st, &allset)) { //accept int client_st = server_accept(listen_st); if (client_st < 0) { //直接跳出select循環 break; } //客戶端連接成功 設置客戶端非阻塞 setnonblock(client_st); //將客戶端socket加入socket池中 for (i = 0; i < MAXSOCKET; i++) { if (client[i] == -1) { client[i] = client_st; break; } } if (i == MAXSOCKET) { //socket池已滿,關閉客戶端連接 close_socket(client_st); } } //處理客戶端的socket for (i = 0; i < MAXSOCKET; i++) { if (client[i] == -1) { //無效socket直接退出 continue; } //判斷是否是這個socket有事件發生 if (FD_ISSET(client[i], &allset)) { //接收消息 if (socket_recv(client[i]) < 0) { //如果接收消息出錯,關閉客戶端socket close_socket(client[i]); //從socket池中將這個socket清除 client[i] = -1; } rc--; } //說明所有消息的socket已經處理完成 if (rc < 0) { //備注:雙循環break只能跳出最近的一重循環 break; } } } //close server socket close_socket(listen_st); return 0; }