zoukankan      html  css  js  c++  java
  • 【多线程】聊天室的实现

    【目标实现】

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

    【服务器端】

     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, judge = 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         pthread_mutex_lock(&mutex);
    20         //向所有客户端发送消息
    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     //线程同步
    29     pthread_mutex_lock(&mutex);
    30     for(i = 1; i <= num; i++)
    31     {
    32         if(cli_socks[i] == sock)
    33             break;
    34     }
    35     while(i < num) 
    36     {
    37         cli_socks[i] = cli_socks[i+1];
    38         i++;
    39     }
    40     num--;
    41     pthread_mutex_unlock(&mutex);
    42     printf("close connect is %d
    ", sock);
    43     close(sock);
    44     if(!num && judge) exit(0);
    45 }
    46 int main(int argc, char ** argv)
    47 {
    48     int ser_sock, cli_sock;
    49     sockaddr_in ser_addr, cli_addr;
    50     ser_sock = socket(PF_INET, SOCK_STREAM, 0);//创建套接字
    51     if(ser_sock == -1) 
    52         puts("socket error");
    53 
    54     //地址再分配
    55     int opt = 1;
    56     setsockopt(ser_sock, SOL_SOCKET, SO_REUSEADDR, &opt, 4);
    57 
    58     memset(&ser_addr, 0, sizeof(ser_addr));
    59     ser_addr.sin_family = AF_INET;
    60     ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    61     ser_addr.sin_port = htons(atoi("9190"));
    62     //分配地址信息
    63     bind(ser_sock, (sockaddr *)&ser_addr, sizeof(ser_addr));
    64     //转化可接受连接状态
    65     listen(ser_sock, 5);
    66 
    67     pthread_mutex_init(&mutex, NULL);//创建互斥量
    68     pthread_t id;
    69     puts("等待进入...");
    70     while(1)
    71     {
    72         socklen_t cli_len = sizeof(cli_addr);
    73         //请求受理
    74         cli_sock = accept(ser_sock, (sockaddr *)&cli_addr, &cli_len);
    75         judge = 1;
    76         //利用互斥量锁住临界区
    77         pthread_mutex_lock(&mutex);
    78         cli_socks[++num] = cli_sock;
    79         pthread_mutex_unlock(&mutex);
    80 
    81         pthread_create(&id, NULL, t_main, (void *)&cli_sock);//创建线程
    82         pthread_detach(id);//线程运行结束自动释放所有资源
    83         printf("connect is %d
    ", cli_sock);
    84     }
    85     pthread_mutex_destroy(&mutex);//销毁互斥量
    86     close(ser_sock);
    87     return 0;
    88 }

    【客户端】

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <arpa/inet.h>
     6 #include <pthread.h>
     7 #include <iostream>
     8 using namespace std;
     9 char name[100], na[100];
    10 void *wr(void *arg)
    11 {
    12     char s[100], mes[100];
    13     int sock = *(int *)arg;
    14     int x = sizeof(s);
    15     while(1)
    16     {
    17         fgets(s, sizeof(s), stdin);
    18         s[strlen(s) - 1] = 0;
    19         if(!strcmp(s, "q")) break;
    20         sprintf(mes, "%s %s", name, s);
    21         write(sock, mes, strlen(mes));
    22     }
    23     sprintf(mes, "【系统消息】%s退出聊天室", na);
    24     write(sock, mes, strlen(mes));
    25     close(sock);
    26     exit(0);
    27 }
    28 void *rd(void *arg)
    29 {
    30     int sock = *(int *)arg;
    31     char s[100];
    32     while(1)
    33     {
    34         int len = read(sock, s, 100);
    35         s[len] = 0;
    36         puts(s);
    37     }
    38 }
    39 int main(int argc, char **argv)
    40 {
    41     puts("请输入用户名!");
    42     char mes[50];
    43     cin >> na;
    44     getchar();//防止fgets读回车
    45     int sock = socket(PF_INET, SOCK_STREAM, 0);//创建套接字
    46 
    47     sockaddr_in addr;
    48     memset(&addr, 0, sizeof(addr));
    49     addr.sin_family = AF_INET;
    50     addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    51     addr.sin_port = htons(atoi("9190"));
    52     sprintf(name, "[%s]", na);
    53     connect(sock, (sockaddr *)&addr, sizeof(addr));//请求连接
    54     sprintf(mes, "【系统消息】%s进入聊天室", na);
    55     write(sock, mes, strlen(mes));
    56 
    57     pthread_t id_rd, id_wr;
    58     //创建读写线程
    59     pthread_create(&id_wr, NULL, wr, (void *)&sock);
    60     pthread_create(&id_rd, NULL, rd, (void *)&sock);
    61 
    62     pthread_join(id_wr, NULL);//等待写线程终止
    63     //pthread_join(id_rd, NULL);
    64     close(sock);
    65     return 0;
    66 }

    【效果截图】

    【发现问题】

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

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

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

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

  • 相关阅读:
    [Linux] git send-email的使用
    Linux 下 grep 命令常用方法简介
    Scikit-learn的kmeans聚类
    python 基础知识
    Python
    xgboost 特征选择,筛选特征的正要性
    阿里菜鸟-算法(一面)
    基于Ranking-CNN的年龄识别(CVPR_2017)
    基于多输出有序回归的年龄识别(CVPR_2016)
    在caffe中用训练好的 caffemodel 来分类新的图片所遇到的问题
  • 原文地址:https://www.cnblogs.com/lesroad/p/9691103.html
Copyright © 2011-2022 走看看