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。聊天室同时可接纳人数上限为MAX_NUM。

运行结果:

服务器:

客户端:

如果房间满员:

 代码:

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

值得改进的地方:

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

如果对整个数组加锁,感觉多线程就没多大意思了,效果跟直接用select或poll轮询差不多。

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

猜你喜欢

转载自www.cnblogs.com/Zzz-y/p/9314684.html