tcp迷你聊天室

以http://www.cnblogs.com/Zzz-y/p/9107554.html里的 server 为雏形写了一个迷你型的多人聊天室。

client跟之前一样。主要对 server 做了一些改进:

1、聊天室要实现收到消息后,对所有的client广播,所以这边改成建立一个线程服务一个客户,于是clientfd共享就方便的多。

2、用两个数组tid和fd_array分别保存线程id和fd,两者用数组索引idx一一对应。一个客户进入聊天室,占用一个idx。当客户退出,释放idx。

3、聊天室同时可接纳人数上限为MAX_NUM。服务器在accept之后,对已经退出的tid作资源回收,并分配给新的客户端。如果没有tid,则提示房间已满并关闭连接。

4、服务器接受SIGINT信号作为结束,结束时回收所有线程资源,关闭fd。

 

运行结果:

服务器:

客户端:

如果房间满员:

 

 代码:

  1 #include <stdio.h>
  2 #include <sys/socket.h>
  3 #include <sys/types.h>
  4 #include <arpa/inet.h>
  5 #include <netinet/in.h>
  6 #include <unistd.h>
  7 #include <string.h>
  8 #include <stdlib.h>
  9 #include <pthread.h>
 10 #include <time.h>
 11 #include <signal.h>
 12 
 13 #define MAX_NUM 3
 14 
 15 struct tclient
 16 {
 17     int idx;
 18     struct sockaddr_in addr;
 19 };
 20 
 21 pthread_t tid[MAX_NUM];
 22 int fd_array[MAX_NUM];
 23 int serverfd;
 24 
 25 void sig_act(int signo) {
 26     for (int i = 0; i < MAX_NUM; ++i)
 27     {
 28         if (tid[i] != 0) {
 29             pthread_join(tid[i], nullptr);
 30         }
 31     }
 32     close(serverfd);
 33     printf("server closed!\n");
 34     exit(0);
 35 }
 36 
 37 void* foo(void *arg) {
 38     struct tclient tc = *static_cast<struct tclient*>(arg);
 39     char buff[1024];
 40     char msg[1024];
 41     int fd = fd_array[tc.idx];
 42     int len;
 43     time_t timep;
 44     inet_ntop(AF_INET, &tc.addr.sin_addr, buff, sizeof(buff));
 45     printf("connction from %s, port %d\n", buff, ntohs(tc.addr.sin_port));
 46     while(1) {
 47         len = read(fd, buff, 1024);
 48         time (&timep);
 49         if (len > 0) {
 50             printf("receive from %d: %s\n", ntohs(tc.addr.sin_port), buff);
 51             sprintf(msg, "%sreceive from %d: %s\n", ctime(&timep), ntohs(tc.addr.sin_port), buff);
 52             for (int i = 0; i < MAX_NUM; ++i)
 53             {
 54                 if (fd_array[i] != 0 && fd_array[i] != fd)
 55                 {
 56                     write(fd_array[i], msg, 1024);
 57                 }
 58             }
 59         }
 60         else {
 61             close(fd);
 62             fd_array[tc.idx] = 0;
 63             break;
 64         }
 65         usleep(1000);
 66     }
 67     return nullptr;
 68 }
 69 int main() {
 70     int clientfd;
 71     struct sockaddr_in serveraddr, clientaddr;
 72     socklen_t clientaddr_len;
 73     u_short port = 9999;
 74     char buff[1024];
 75     int i;
 76     serverfd = socket(AF_INET, SOCK_STREAM, 0);
 77     if (serverfd == -1) {
 78         printf("socket error\n");
 79         exit(0);
 80     }
 81     bzero(&serveraddr, sizeof(serveraddr));
 82     serveraddr.sin_family = AF_INET;
 83     serveraddr.sin_port = htons(port);
 84     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 85     if (bind(serverfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1) {
 86         printf("bind error\n");
 87         exit(0);
 88     }
 89     inet_ntop(AF_INET, &serveraddr.sin_addr, buff, sizeof(buff));
 90     printf("server ip is %s running on port %d\n", buff, ntohs(serveraddr.sin_port));
 91 
 92     if ( listen(serverfd, 1) == -1 ) {
 93         printf("listen error\n");
 94         exit(0);
 95     }
 96     signal(SIGINT, sig_act);
 97     while(1) {
 98         clientaddr_len = sizeof(clientaddr);
 99         clientfd = accept(serverfd, (struct sockaddr *)&clientaddr, &clientaddr_len);
100         for (i = 0; i < MAX_NUM; ++i)
101         {
102             if (tid[i] != 0 && fd_array[i] == 0) {
103                 pthread_join(tid[i], nullptr);
104                 tid[i] = 0;
105             }
106             if (tid[i] == 0)
107             {
108                 struct tclient tc;
109                 tc.idx = i;
110                 tc.addr = clientaddr;
111                 fd_array[i] = clientfd;
112                 pthread_create(&tid[i], nullptr, foo, static_cast<void*>(&tc));
113                 break;
114             }
115         }
116         if (i == MAX_NUM) {
117             char msg[1024] = "The room is full!";
118             write(clientfd, msg, 1024);
119             close(clientfd);
120         }
121     }
122 }

 

值得改进的地方:

1、这个server不是线程安全的:假如线程1在已经完成L54的验证,在L56发送msg之前,某一线程关闭了对应的fd,这样就造成了向未知fd发送数据。可以对fd加个锁。

 2、融合https://www.cnblogs.com/Zzz-y/p/9281037.html里的数据库,加入账号密码。

posted @ 2018-07-15 20:08  Zzz...y  阅读(1525)  评论(0编辑  收藏  举报