1.聊天室程序——客戶端數組
客戶端我也用了select進行I/O複用,同時監控是否有來自socket的消息和標準輸入,近似能夠完成對鍵盤的中斷使用。服務器
其中select的監控裏,STDOUT和STDIN是已有規定的值了。socket
Socket_setup函數負責進行對socket進行初始化完成connect 的過程,而後在主函數裏無限循環檢查sockfd和STDIN的緩衝區是否有新的消息函數
客戶端程序較簡單:spa
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdbool.h> 5 #include <unistd.h> 6 #include <sys/socket.h> 7 #include <arpa/inet.h> 8 9 #define BUF_SIZE 256 10 #define STDIN 0 11 #define STDOUT 1 12 #define INVALID -1 13 14 int 15 socket_setup(const char *serv_ip, int serv_port) 16 { 17 int rtn,sockfd; 18 struct sockaddr_in sockaddr; 19 20 sockfd = socket(AF_INET, SOCK_STREAM, 0); 21 bzero(&sockaddr,sizeof(sockaddr)); 22 sockaddr.sin_family = AF_INET; 23 sockaddr.sin_port = htons(serv_port); 24 inet_pton(AF_INET, serv_ip, &sockaddr.sin_addr); 25 26 rtn = connect(sockfd,(struct sockaddr *)&sockaddr, sizeof(sockaddr)); 27 28 if (rtn == INVALID) 29 { 30 puts("connection failure\n"); 31 exit(1); 32 } 33 else 34 { 35 puts("connection successful\n"); 36 return sockfd; 37 } 38 } 39 40 int 41 main(int argc, const char *argv[]) 42 { 43 int i,read_size, sockfd = socket_setup(argv[1],argv[2]); 44 char buffer[BUF_SIZE]; 45 fd_set fdset; 46 47 while (1) 48 { 49 FD_ZERO(&fdset); 50 FD_SET(STDIN, &fdset); 51 FD_SET(sockfd, &fdset); 52 select(sockfd + 1, &fdset, NULL, NULL, 0); 53 54 if( FD_ISSET( sockfd, &fdset ) ) 55 { 56 readsize = read(sockfd, buffer, BUF_SIZE); 57 write(STOUT, buffer, read_size); 58 59 if(read_size == 0) 60 { 61 puts("server close"); 62 exit(1); 63 } 64 } 65 66 if(FD_ISSET(STDIN, &fdset)) 67 { 68 read_size = read(STDIN, buffer, BUF_SIZE); 69 write(sockfd, buffer, read_size); 70 } 71 } 72 73 return 0; 74 }
2.聊天室程序——服務器端code
個人想法是,只要創建一個數組來存放客戶端信息,每次有客戶端發送信息或者在服務器端有消息須要發出去,直接遍歷每個元素給每一個客戶端發一個就好,客戶端只須要完成如下幾件事:orm
1.初始化,由於select的性質,每次檢測完後會清空fdset,因此須要每一次都把全部鏈接上了客戶端都從新加入進fdset,涉及函數void init_clients(void),int main(int argc,const char *argv[]server
2.檢測有沒有新的信息發上來了,若是有,而且可讀,那就廣播出去,涉及函數:void chat(fd_set fdset),void broadcast(char *msg)blog
3.把全部發上來的信息按照時間格式化輸出,這裏學到了幾個新函數,一個是int sprintf( char *buffer, const char *format, [ argument] … );能夠格式化輸出字符串和數字,一個是對世間的格式化,size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr );這裏涉及的函數:void stdmsg(int i, char *buffer, const char *msg)隊列
源程序是:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <time.h> 6 #include <sys/socket.h> 7 #include <arpa/inet.h> 8 9 #define TIME_SIZE 16 // 表示時間的字符串長度 10 #define IP_SIZE 16 // IP 字符串長度 11 #define BUF_SIZE 256 // 緩衝區大小 12 #define CLIENT_SIZE 8 // 容許的客戶端數量 13 #define BACKLOG CLIENT_SIZE // listen 隊列長度,等於容許的客戶端數量 14 #define INVALID -1 15 16 struct CLIENT{ 17 int clienfd; 18 struct sockaddr_in sockaddr; 19 char ip[IP_SIZE]; 20 int port; 21 }clients[CLIENT_SIZE]; 22 23 void init_clients(void) 24 { 25 int i; 26 27 for( i = 0; i < CLIENT_SIZE; i++ ) 28 { 29 clients[i].clientfd = INVALID; 30 } 31 } 32 33 void broadcast(char *msg) 34 { 35 int i; 36 37 for(i = 0; i<CLIENT_SIZE; i++) 38 { 39 if( clients[i].clienfd != INVALID ) 40 { 41 write(clients[i].clientfd, msg, sterlen(msg)); 42 } 43 } 44 } 45 46 void stdmsg(int i, char *buffer, const char *msg) 47 { 48 char curtime[TIME_SIZE]; 49 time_t curtime_t; 50 struct tm *timeinfo; 51 52 curtime_t = time(NULL); 53 timeinfo = localtime(&curtime_t); 54 strftime(curtime, TIME_SIZE, "%X", timeinfo); 55 sprintf(buffer,"<%s %s:%d> %s",curtime,clients[i].ip,clients[i].port,msg); 56 } 57 58 void accept_connect(int listenfd) 59 { 60 int connectfd,i; 61 char buffer[BUF_SIZE]; 62 struct sockaddr_in clientaddr; 63 socklen_t connectlen = sizeof(struct sockaddr_in); 64 65 connectfd = accept( listenfd, (struct sockaddr_in *)&clientaddr, &connectlen); 66 67 68 for( i = 0; i < CLIENT_SIZE, i++ ) 69 { 70 if(clients[i].clienfd == INVALID) 71 { 72 clients[i].clienfd == connectfd; 73 memcpy(&clients[i].sockaddr); 74 clients[i].port = ntohs(clients[i].sockaddr.sin_port); 75 inet_ntop(AF_INET, &clients[i].sockaddr.sin_addr, clients[i].ip, IP_SIZE); 76 stdmsg(i,buffer,"login\n"); 77 printf("%s",buffer); 78 broadcast(buffer); 79 break; 80 } 81 } 82 83 if (i == CLIENT_SIZE ) 84 { 85 strcpy(buffer, "out of number\n"); 86 write(connectfd, buffer, strlen(buffer)); 87 close(connectfd);//全部操做利用buffer進行操做 88 } 89 } 90 91 92 void chat(fd_set fdset) 93 { 94 int sockfd, read_size, i; 95 char read_buf[BUF_SIZE], send_buf[BUF_SIZE]; 96 97 for( i = 0; i < CLIENT_SIZE; i++ ) 98 { 99 sockfd = clients[i].clienfd; 100 101 if(sockfd != INVALID && FD_ISSET(sockfd,&fdset)) 102 { 103 read_size = read(sockfd, read_buf, BUF_SIZE - 1); 104 105 if(read_size == 0) 106 { 107 //connection lost 108 close(sockfd); 109 clients[i].clienfd = INVALID; 110 stdmsg(i, send_buf, "logout\n"); 111 printf("%s\n",send_buf); 112 broadcast(send_buf); 113 114 continue; 115 } 116 else 117 { 118 read_buf[read_size] = '\0'; 119 stdmsg(i, send_buf, read_buf); 120 printf("%s",send_buf); 121 broadcast(send_buf); 122 } 123 } 124 } 125 } 126 127 int socket_setup(int port) 128 { 129 int rtn, listenfd = socket(AF_INET, SOCK_STREAM, 0); 130 struct sockaddr_in sockaddr; 131 132 bzero(&sockaddr, sizeof(sockaddr)); 133 sockaddr.sin_family = AF_INET; 134 sockaddr.sin_port = htons(port); 135 sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 136 137 rtn = bind(listenfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); 138 if (rtn == INVALID) 139 { 140 puts("bind error\n"); 141 exit(1); 142 } 143 144 if(listen(listenfd,BACKLOG) == INVALID) 145 { 146 puts("listen error\n") 147 exit(1); 148 } 149 150 puts("service setup\n"); 151 return listenfd; 152 } 153 154 int main(int argc,const char *argv[]) 155 { 156 int maxfd, i, listenfd = socket_setup(atoi(argv[1])); 157 fdset fdset; 158 159 init_clients(); 160 161 while(1) 162 { 163 FD_ZERO(&fdset); 164 FD_SET(listenfd, &fdset); 165 maxfd = listenfd; 166 167 for(i = 0; i < CLIENT_SIZE; i++) 168 { 169 if(clients[i].clienfd != INVALID) 170 { 171 FD_SET(clients[i].clienfd, &fdset); 172 173 if(clients[i].clienfd > maxfd) 174 { 175 maxfd = clients[i].clienfd; 176 } 177 } 178 } 179 180 select(maxfd + 1, &fdset, NULL, NULL, 0); 181 182 if(FD_ISSET(listenfd, &fdset)) 183 { 184 accept_connect(listenfd); 185 } 186 chat(fdset); 187 } 188 return 0; 189 }