1.聊天室程序——客戶端
客戶端我也用了select進(jìn)行I/O復(fù)用乙濒,同時監(jiān)控是否有來自socket的消息和標(biāo)準(zhǔn)輸入陕赃,近似可以完成對鍵盤的中斷使用。
其中select的監(jiān)控里颁股,STDOUT和STDIN是已有規(guī)定的值了么库。
Socket_setup函數(shù)負(fù)責(zé)進(jìn)行對socket進(jìn)行初始化完成connect 的過程,然后在主函數(shù)里無限循環(huán)檢查sockfd和STDIN的緩沖區(qū)是否有新的消息
客戶端程序較簡單:
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.聊天室程序——服務(wù)器端
我的想法是甘有,只要建立一個數(shù)組來存放客戶端信息诉儒,每次有客戶端發(fā)送信息或者在服務(wù)器端有消息需要發(fā)出去,直接遍歷每一個元素給每個客戶端發(fā)一個就好亏掀,客戶端只需要完成以下幾件事:
1.初始化忱反,因為select的性質(zhì),每次檢測完后會清空fdset滤愕,所以需要每一次都把所有連接上了客戶端都重新加入進(jìn)fdset温算,涉及函數(shù)void init_clients(void),int main(int argc,const char *argv[]
2.檢測有沒有新的信息發(fā)上來了间影,如果有注竿,并且可讀,那就廣播出去,涉及函數(shù):void chat(fd_set fdset)巩割,void broadcast(char *msg)
3.把所有發(fā)上來的信息按照時間格式化輸出裙顽,這里學(xué)到了幾個新函數(shù),一個是int sprintf( char *buffer, const char *format, [ argument] … );可以格式化輸出字符串和數(shù)字喂分,一個是對世間的格式化锦庸,size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr );這里涉及的函數(shù):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 // 緩沖區(qū)大小 12 #define CLIENT_SIZE 8 // 允許的客戶端數(shù)量 13 #define BACKLOG CLIENT_SIZE // listen 隊列長度,等于允許的客戶端數(shù)量 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進(jìn)行操作 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 }