1.聊天室程序——客户端
客户端我也用了select进行I/O复用,同时监控是否有来自socket的消息和标准输入,近似可以完成对键盘的中断使用。
其中select的监控里,STDOUT和STDIN是已有规定的值了。
Socket_setup函数负责进行对socket进行初始化完成connect 的过程,然后在主函数里无限循环检查sockfd和STDIN的缓冲区是否有新的消息
客户端程序较简单:
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.聊天室程序——服务器端
我的想法是,只要建立一个数组来存放客户端信息,每次有客户端发送信息或者在服务器端有消息需要发出去,直接遍历每一个元素给每个客户端发一个就好,客户端只需要完成以下几件事:
1.初始化,因为select的性质,每次检测完后会清空fdset,所以需要每一次都把所有连接上了客户端都重新加入进fdset,涉及函数void init_clients(void),int main(int argc,const char *argv[]
2.检测有没有新的信息发上来了,如果有,并且可读,那就广播出去,涉及函数:void chat(fd_set fdset),void broadcast(char *msg)
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 }