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(&current);  //将集合置零
214     FD_SET(0,&current); //把标准输入描述符添加到集合当中
215     FD_SET(listen_fd,&current); //把监听描述符
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,&current);
231                 }else{  //新的连接描述符可读
232                     ret = client_handler(i);
233                     if(ret <= 0){
234                         close(i);
235                         FD_CLR(i,&current);
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(&current);  //将集合置零
110     FD_SET(0,&current); //把标准输入描述符添加到集合当中
111     FD_SET(client_fd,&current); //把监听描述符
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 }

 

posted @ 2021-11-21 15:55  yangrourou  阅读(285)  评论(0编辑  收藏  举报