【多线程】聊天室的实现

【目标实现】

模拟一个聊天室,任意一个客户端窗口可以发送消息,同时也可以接收聊天室内所有人的消息。

【服务器端】

 1 #include <stdio.h>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <arpa/inet.h>
 5 #include <pthread.h>
 6 #include <unistd.h>
 7 using namespace std;
 8 int cli_socks[100], num = 0;
 9 pthread_mutex_t mutex;
10 void *t_main(void *arg)
11 {
12     char str[100];
13     int sock = *(int *)arg;
14     while(1)
15     {
16         int len = read(sock, str, 100);
17         if(!len) break;
18         str[len] = 0;
19         //puts(str);
20         pthread_mutex_lock(&mutex);
21         for(int i = 1; i <= num; i++)
22         {
23             write(cli_socks[i], str, sizeof(str));
24         }
25         pthread_mutex_unlock(&mutex);
26     }
27     int i;
28     pthread_mutex_lock(&mutex);
29     for(i = 1; i <= num; i++)
30     {
31         if(cli_socks[i] == sock)
32             break;
33     }
34     while(i < num) 
35     {
36         cli_socks[i] = cli_socks[i+1];
37         i++;
38     }
39     num--;
40     pthread_mutex_unlock(&mutex);
41     printf("close connect is %d\n", sock);
42     close(sock);
43 }
44 int main(int argc, char ** argv)
45 {
46     int ser_sock, cli_sock;
47     sockaddr_in ser_addr, cli_addr;
48     ser_sock = socket(PF_INET, SOCK_STREAM, 0);
49 
50     int opt = 1;
51     setsockopt(ser_sock, SOL_SOCKET, SO_REUSEADDR, &opt, 4);
52 
53     memset(&ser_addr, 0, sizeof(ser_addr));
54     ser_addr.sin_family = AF_INET;
55     ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
56     ser_addr.sin_port = htons(atoi(argv[1]));
57     bind(ser_sock, (sockaddr *)&ser_addr, sizeof(ser_addr));
58 
59     listen(ser_sock, 5);
60 
61     pthread_mutex_init(&mutex, NULL);//创建互斥量
62     pthread_t id;
63     while(1)
64     {
65         socklen_t cli_len = sizeof(cli_addr);
66         cli_sock = accept(ser_sock, (sockaddr *)&cli_addr, &cli_len);
67         //利用互斥量锁住临界区
68         pthread_mutex_lock(&mutex);
69         cli_socks[++num] = cli_sock;
70         pthread_mutex_unlock(&mutex);
71 
72         pthread_create(&id, NULL, t_main, (void *)&cli_sock);
73         pthread_detach(id);
74         printf("connect is %d\n", cli_sock);
75     }
76     pthread_mutex_destroy(&mutex);//销毁互斥量
77     close(ser_sock);
78     return 0;
79 }

【客户端】

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <arpa/inet.h>
 6 #include <pthread.h>
 7 using namespace std;
 8 char name[20];
 9 void *wr(void *arg)
10 {
11     char s[100], mes[100];
12     int sock = *(int *)arg;
13     while(1)
14     {
15         fgets(s, sizeof(s), stdin);
16         if(!strcmp(s, "q\n")) break;//fgets会读入最后的回车
17         sprintf(mes, "%s %s", name, s);
18         write(sock, mes, sizeof(mes));
19     }
20     close(sock);
21     exit(0);
22 }
23 void *rd(void *arg)
24 {
25     int sock = *(int *)arg;
26     char s[100];
27     while(1)
28     {
29         int len = read(sock, s, 100);
30         s[len] = 0;
31         puts(s);
32     }
33 }
34 int main(int argc, char **argv)
35 {
36     if(argc != 4)
37     {
38         puts("请输入4个参数!");
39         exit(1);
40     }
41     int sock = socket(PF_INET, SOCK_STREAM, 0);
42 
43     sockaddr_in addr;
44     memset(&addr, 0, sizeof(addr));
45     addr.sin_family = AF_INET;
46     addr.sin_addr.s_addr = inet_addr(argv[1]);
47     addr.sin_port = htons(atoi(argv[2]));
48     sprintf(name, "[%s]", argv[3]);
49     connect(sock, (sockaddr *)&addr, sizeof(addr));
50 
51 
52     pthread_t id_rd, id_wr;
53     pthread_create(&id_wr, NULL, wr, (void *)&sock);
54     pthread_create(&id_rd, NULL, rd, (void *)&sock);
55 
56     pthread_join(id_wr, NULL);
57     //pthread_join(id_rd, NULL);
58     close(sock);
59     return 0;
60 }

【效果截图】

【发现问题】

1.exit和return的区别:传送门

2.linux用gets会出现警告,由于他没有指定输入字符的大小,如果输入字符大于定义的数组长度的时候,那么就会发生内存越界问题。 而用fgets函数则可以根据定义数组的长度自动截断字符,而消除一些安全隐患。 但是fgets会读入最后的回车。

3.客户端代码的18行是把数据送到输出缓冲,29行是从输入缓冲读取,不用担心两个会同时进行,因为并不在一个通道上。

4.全局变量num和cli_socks所在的代码行构成临界区,因为如果不同的线程同时运行可能会引发错误。

猜你喜欢

转载自www.cnblogs.com/lesroad/p/9691103.html
今日推荐