Linux系统下运用select函数实现tcp群聊(c语言)
服务端
1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <netinet/ip.h> 9 #include <arpa/inet.h> 10 11 struct link{ 12 int fd; 13 char *addr; 14 uint16_t port; 15 struct link *next; 16 }; 17 struct link *head = NULL; 18 19 struct link * add_node(struct link *head,int fd,char *addr,uint16_t port); //添加聊天人 20 struct link * move_node(struct link *head,int fd); //删除聊天人 21 void show_node(struct link *head); //显示所有联系人 22 int init_tcp_server(unsigned short port); //初始化TCP服务器,返回socket描述符 23 int input_handler(int fd); //标准输出处理 24 int listen_handler(int listen_fd); //监听处理 25 int client_handler(int new_fd); //客户端处理 26 void main_loop(int listen_fd); //主函数循环 27 28 29 int main(){ 30 int listen_fd; 31 listen_fd = init_tcp_server(6666); //开启的端口号,可任意改变 32 if(listen_fd < 0){ 33 fprintf(stderr,"init tcp server failed!\n"); 34 return -1; 35 } 36 printf("listening...\n"); 37 main_loop(listen_fd); 38 close(listen_fd); 39 return 0; 40 } 41 42 struct link * add_node(struct link *head, int fd, char *addr, uint16_t port){ 43 struct link *p = NULL; 44 struct link *pr = head; 45 p = (struct link *) malloc(sizeof (struct link)); 46 p->fd = fd; 47 p->addr = addr; 48 p->port = port; 49 p->next = NULL; 50 51 if(head == NULL){ 52 head = p; 53 }else{ 54 while(pr->next != NULL){ 55 pr = pr->next; 56 } 57 pr->next = p; 58 } 59 // printf("add success!\n"); 60 return head; 61 } 62 63 struct link * move_node(struct link *head,int fd){ 64 struct link *h,*p; 65 h = head; 66 if(h->fd == fd){ 67 p = head; 68 free(head); 69 head = p->next; 70 }else{ 71 while(h->next != NULL){ 72 if(h->next->fd == fd){ 73 p = h->next->next; 74 free(h->next); 75 h->next = p; 76 break; 77 } 78 } 79 } 80 81 printf("move success!\n"); 82 show_node(head); 83 return head; 84 } 85 86 void show_node(struct link *head){ 87 struct link *p; 88 p = head; 89 printf("members:\n"); 90 while(p != NULL){ 91 printf("%d %s : %d\n",p->fd,p->addr,p->port); 92 p = p->next; 93 } 94 // printf("show success!\n"); 95 return; 96 97 } 98 99 int init_tcp_server(unsigned short port){ 100 int ret; 101 int opt; 102 int listen_fd; 103 struct sockaddr_in self; 104 105 listen_fd = socket(AF_INET,SOCK_STREAM,0); 106 if(listen_fd < 0){ 107 perror("socket"); 108 return -1; 109 } 110 111 //配置监听描述符地址复用属性 112 opt = 1; 113 ret = setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&opt, sizeof(opt)); 114 if(ret < 0){ 115 perror("set socket opt"); 116 return -1; 117 } 118 119 // 填充服务器开放接口和端口号信息 120 memset(&self,0,sizeof (self)); 121 self.sin_family = AF_INET; 122 self.sin_port = htons(port); 123 self.sin_addr.s_addr = htonl(INADDR_ANY); 124 ret = bind(listen_fd,(struct sockaddr *)&self,sizeof (self)); 125 if(ret == -1){ 126 perror("bind"); 127 return -1; 128 } 129 130 listen(listen_fd,5); 131 132 return listen_fd; 133 } 134 135 int input_handler(int fd){ 136 char buf[1024]; 137 fgets(buf, sizeof(buf),stdin); 138 buf[strlen(buf)-1] = 0; 139 printf("input:%s\n",buf); 140 return 0; 141 } 142 143 int listen_handler(int listen_fd){ 144 int new_fd; 145 struct sockaddr_in remote; 146 int len; 147 148 memset(&remote,0,sizeof(remote)); 149 len = sizeof(remote); 150 new_fd = accept(listen_fd,(struct sockaddr *)&remote,&len); 151 if(new_fd < 0){ 152 perror("accept"); 153 return -1; 154 } 155 printf("--------------------------\n"); 156 printf("have a new communication!(%s:%d)\n", 157 inet_ntoa(remote.sin_addr),ntohs(remote.sin_port)); 158 head = add_node(head,new_fd,inet_ntoa(remote.sin_addr),ntohs(remote.sin_port)); 159 show_node(head); 160 printf("--------------------------\n"); 161 return new_fd; 162 } 163 164 int client_handler(int new_fd){ 165 char buf[1024],bufid[1024]; 166 struct link *p; 167 int ret,ret1; 168 169 ret = recv(new_fd,buf, sizeof(buf)-1,0); 170 if(ret < 0){ 171 perror("recv"); 172 return -1; 173 }else if(ret == 0){ 174 printf("--------------------------\n"); 175 printf("connection is unlink!\n"); 176 head = move_node(head,new_fd); 177 printf("--------------------------\n"); 178 return 0; 179 }else{ 180 buf[ret] = 0; 181 printf("%s\n",buf); 182 p = head; 183 while(p != NULL){ 184 if(p->fd == new_fd){ 185 sprintf(bufid,"%s : %d ",p->addr,p->port); 186 strcat(bufid,buf); 187 break; 188 } 189 p = p->next; 190 } 191 p = head; 192 while(p != NULL){ 193 if(p->fd == new_fd){ 194 p = p->next; 195 continue; 196 } 197 ret1 = send(p->fd,bufid, strlen(bufid),0); 198 if(ret1 < 0) 199 { 200 perror("send"); 201 } 202 p = p->next; 203 } 204 } 205 return ret; 206 } 207 208 void main_loop(int listen_fd){ 209 fd_set current,bak_fds; 210 int new_fd; 211 int ret; 212 213 FD_ZERO(¤t); //将集合置零 214 FD_SET(0,¤t); //把标准输入描述符添加到集合当中 215 FD_SET(listen_fd,¤t); //把监听描述符 216 217 while(1){ 218 bak_fds = current; 219 ret = select(1024,&bak_fds,NULL,NULL,NULL); 220 if(ret < 0){ 221 perror("select"); 222 break; 223 } 224 for (int i = 0; i <= 1024 ; ++i) { 225 if(FD_ISSET(i,&bak_fds)){ 226 if(i == 0){ //标准输入可读 227 input_handler(i); 228 }else if(i == listen_fd){ //监听描述符可读 229 new_fd = listen_handler(i); 230 FD_SET(new_fd,¤t); 231 }else{ //新的连接描述符可读 232 ret = client_handler(i); 233 if(ret <= 0){ 234 close(i); 235 FD_CLR(i,¤t); 236 } 237 } 238 } 239 } 240 } 241 }
客户端
1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <netinet/ip.h> 9 #include <arpa/inet.h> 10 #include <time.h> 11 12 int init_tcp_client(const char *host,short port); //初始化TCP客户端,返回socket描述符 13 void readtime(char *buf); //时间处理 14 int input_handler(int fd); //标准输入处理 15 int recv_handler(int client_fd); //接收处理 16 void main_loop(int client_fd); //主函数循环 17 18 19 int main(){ 20 int client_fd; 21 client_fd = init_tcp_client("***.***.***.***",6666); //填写服务端的IP地址,端口号要与服务端一致 22 if(client_fd < 0){ 23 fprintf(stderr,"init tcp server failed!\n"); 24 return -1; 25 } 26 main_loop(client_fd); 27 close(client_fd); 28 return 0; 29 } 30 31 int init_tcp_client(const char *host,short port){ 32 int tcp_socket; 33 int ret; 34 struct sockaddr_in dest; 35 36 tcp_socket = socket(AF_INET,SOCK_STREAM,0); 37 if(tcp_socket == -1){ 38 perror("socket"); 39 return -1; 40 } 41 42 memset(&dest,0,sizeof(dest)); 43 dest.sin_family = AF_INET; 44 dest.sin_port = htons(port); 45 dest.sin_addr.s_addr = inet_addr(host); 46 ret = connect(tcp_socket,(struct sockaddr *)&dest, sizeof(dest)); 47 if(ret < 0){ 48 perror("connect"); 49 return -1; 50 } 51 printf("connect %s success!\n",host); 52 return tcp_socket; 53 } 54 55 void readtime(char *buf){ 56 time_t t; 57 struct tm *info; 58 time(&t);//获取秒数 59 info = localtime(&t);//格式化日期 60 sprintf(buf,"%d-%d-%d %02d:%02d:%02d\n", 61 info->tm_year+1900,info->tm_mon+1,info->tm_mday, 62 info->tm_hour,info->tm_min,info->tm_sec); 63 } 64 65 int input_handler(int client_fd){ 66 char buf[1024]; 67 char time_buf[1024]; 68 int ret; 69 70 fgets(buf, sizeof(buf),stdin); 71 buf[strlen(buf)-1] = 0; 72 if(strncmp(buf,"bye",3) == 0){ //客户端输入bey退出聊天 73 return -1; 74 } 75 readtime(time_buf); 76 strcat(time_buf,buf); 77 ret = send(client_fd,time_buf, strlen(time_buf),0); 78 if(ret < 0) 79 { 80 perror("send"); 81 return -1; 82 } 83 // printf("send %d bytes success!\n",ret); 84 return ret; 85 } 86 87 int recv_handler(int client_fd){ 88 char buf[1024]; 89 int ret; 90 91 ret = recv(client_fd,buf,sizeof(buf),0); 92 if(ret < 0){ 93 perror("recv"); 94 return -1; 95 }else if(ret == 0){ 96 printf("server is off!\n"); 97 return -1; 98 }else{ 99 buf[ret] = 0; 100 printf("%s\n",buf); 101 } 102 return ret; 103 } 104 105 void main_loop(int client_fd){ 106 fd_set current,bak_fds; 107 int ret,ret1,ret2; 108 109 FD_ZERO(¤t); //将集合置零 110 FD_SET(0,¤t); //把标准输入描述符添加到集合当中 111 FD_SET(client_fd,¤t); //把监听描述符 112 113 while(1){ 114 bak_fds = current; 115 ret = select(10,&bak_fds,NULL,NULL,NULL); 116 if(ret < 0){ 117 perror("select"); 118 break; 119 } 120 for (int i = 0; i <= 10 ; ++i) { 121 if(FD_ISSET(i,&bak_fds)){ 122 if(i == 0){ //标准输入可读 123 ret1 = input_handler(client_fd); 124 if(ret1 < 0){ 125 return; 126 } 127 }else{ //描述符可读 128 ret2 = recv_handler(client_fd); 129 if(ret2 < 0){ 130 return; 131 } 132 } 133 } 134 } 135 } 136 }