【linux项目】多人聊天室

多人聊天室

搭建一个可以实现多个用户同时在线聊天的多人聊天室

在这里插入图片描述

客户端

1.注册用户信息

bool Register()
 89     {
 90       if(!connect_server())
 91       {
 92         return false;                                                                                               
 93       }
 94 
 95       //1.发送注册标识
 96       char type = REGISTER;
 97       ssize_t send_size = send(tcp_sock,&type,1,0);
 98 
 99       if(send_size < 0)
100       {
101         LOG(ERROR,"send Register type ")<<std::endl;
102         return false;
103       }
104       //2.发送注册内容
105       struct reg_info ri;
106       std::cout<<"please enter your name:";
107       std::cin>>ri.name_;
108 
109       std::cout<<"please enter your from:";
110       std::cin>>ri.from_;
111 
112       while(1)
113       {
114 
115         std::string p_a1;//第一次输入的密码
116         std::cout<<"please enter your passwd:";
117         std::cin>>p_a1;
118         std::cout<<"please enter your passwd again :";
119         std::string p_a2;//第二次输入的密码
120         std::cin>>p_a2;
121         if(p_a1 == p_a2)
122         {
123           strcpy(ri.passwd_,p_a1.c_str());
124           break;
125         }
126         else 
127         {
128           printf("The password is not the same\n");
129         }
130       }
131                                                                                                                     
132       send_size = send(tcp_sock,&ri,sizeof(ri),0);
133 
134       if(send_size < 0)
135       {
136         LOG(ERROR,"send Register type ")<<std::endl;
137         return false;
138       }
139       //3.解析应答状态和获取用户id
140       struct reply_info re_i;                
141       ssize_t re_i_size = recv(tcp_sock,&re_i,sizeof(re_i),0);
142       if(re_i_size < 0)
143       {
144         LOG(ERROR,"re_i_size ")<<std::endl;
145         return false;
146       }
147       else if(re_i_size == 0)
148       {
149         LOG(ERROR,"peer shutdown connect ")<<std::endl;
150         return false;
151       }
152 
153       if(re_i.status != REGISTER_T)
154       {
155         LOG(ERROR,"Register failed")<<std::endl;
156         printf("注册失败\n");
157         return false;
158       }
159       LOG(INFO,"Register success user_id = ")<<re_i.user_id<<std::endl;
160       //printf("注册成功,user_id = %lu\n",re_i.user_id);
161       //4.注册成功之后保存用户信息
162       me.Name_ = ri.name_;
163       me.From_ = ri.from_;
164       me.Passwd_ = ri.passwd_;
165       me.Uer_ID = re_i.user_id;
166       close(tcp_sock);
167       return true;
168     }        

2.登录用户

 bool Login()
170     {
171       if(!connect_server())
172       {
173         return false;
174       }
175 
176       //1.发送登录标识
177       char type = LOGIN;
178       ssize_t send_size = send(tcp_sock,&type,1,0);
179 
180       if(send_size < 0)
181       {
182         LOG(ERROR,"send login type ")<<std::endl;
183         return false;
184       }
185       //2.发送登录数据
186       struct login_info li;
187       li.user_id = me.Uer_ID;
188       strcpy(li.passwd_,me.Passwd_.c_str()); 
189       send_size = send(tcp_sock,&li,sizeof(li),0);                                                                  
190 
191       if(send_size < 0)
192       {
193         LOG(ERROR,"send login type ")<<std::endl;
194         return false;
195       }
196       //3.解析登录状态
197       struct reply_info re_i;
198       ssize_t re_i_size = recv(tcp_sock,&re_i,sizeof(re_i),0);
199                                                                                                                     
200       if(re_i_size < 0)
201       {
202         LOG(ERROR,"re_i_size ")<<std::endl;
203         return false;
204       }
205       else if(re_i_size == 0)
206       {
207         LOG(ERROR,"peer shutdown connect ")<<std::endl;
208         return false;
209       }
210       if(re_i.status != LOGIN_T)
211       {
212         LOG(ERROR,"login failed status = ")<<re_i.status<<std::endl;
213         //rintf("登录失败\n");
214         //printf("login status = %d\n",re_i.status);
215         return false;
216       }
217       //printf("登录成功\n");
218       //printf("login status = %d\n",re_i.status);
219       LOG(INFO,"login success status = ")<<re_i.status<<std::endl;
220       return true;
221     }

3.发送消息至服务端

   //udp数据收发
   bool send_msg(const std::string& msg)
226     {
227       struct sockaddr_in p_addr;                                                                                    
228       p_addr.sin_family = AF_INET;
229       p_addr.sin_port = htons(udp_port);
230       p_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());
231       ssize_t send_size = sendto(udp_sock,msg.c_str(),msg.size(),0,(struct sockaddr*)&p_addr,sizeof(p_addr));
232 
233       if(send_size < 0)
234       {
235        // LOG(ERROR,"send msg to server failed\n");
236         return false;
237       }
238 
239       //LOG(INFO,"send_msg to server success\n");
240       return true;
241     }

4.接收服务端消息

 bool recv_msg(std::string* msg)
244     {
245       //LOG(INFO,"--------------\n");
246       char buf[MES_MAX_SIZE];
247       memset(buf,'\0',sizeof(buf));                                                                                 
248       struct sockaddr_in svr_addr;
249       //struct sockaddr svr_addr;
250       socklen_t svr_addr_len = sizeof(svr_addr);
251       // LOG(INFO,"**************\n");
252       //LOG(INFO,"22222222222222\n");
253       //ssize_t recv_size = recvfrom(udp_sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&svr_addr,&svr_addr_len);
254       ssize_t recv_size = recvfrom(udp_sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&svr_addr,&svr_addr_len);
255      
256       
257       //LOG(INFO,"000000000000\n");
258      //LOG(INFO,"ssssssssssss\n");
259       if(recv_size < 0)
260      {
261       // LOG(ERROR,"recv msg from sevser failed\n");
262        return false;
263      }
264       //LOG(INFO,"11111111111111\n");
265      (*msg).assign(buf,recv_size);
266      //LOG(INFO,"recv_msg from server success\n");
267      return true;
268     }

5.维护客户端窗口

调用了ncurses库,主要参考ncurses库常见用法

   static void* Draw_window(void* arg)
269     {
270       pthread_num* pn = (pthread_num*) arg;
271       chat_window* cw = pn->win_p;
272       int num = pn->p_n;
273       switch (num)
274       {
275         case 0:
276           //即可以绘制窗口,也可以打出欢迎语
277           run_Draw_head(cw);
278           break;
279         case 1:
280           run_out_put(cw,pn->c_c);//聊天显示区域
281           break;
282         case 2:
283           run_user_list(cw,pn->c_c);//显示在线的用户
284           break;
285         case 3:
286           run_in_put(cw,pn->c_c);//显示输入区域
287           break;
288         default:                                
289           break;
290       }
291       //printf("i am %d  pthread\n",pn->p_n);
292       delete pn;
294     }

服务端

1.处理客户端的注册、登录请求

static void* Login_Reg_Start(void* arg)
  198     {
  199       pthread_detach(pthread_self());
  200       login_connect* lc = (login_connect*)arg;
  201       chat_server* cs = (chat_server*)lc->get_server();                                                           
  202       //注册,请求登录
  203       //  请求从客户端来,recv(sock,buf,size,0)
  204       char rues_type;
  205       ssize_t recv_size = recv(lc->get_tcp_sock(),&rues_type,1,0);
  206       if(recv_size < 0)
  207       {
  208         LOG(ERROR,"recv rues_type failed")<<std::endl;
  209         return NULL;
  210       }
  211       else if(recv_size == 0)
  212       {
  213         LOG(ERROR,"client shutdowan connect")<<std::endl;
  214         return NULL;
  215       }
  216 
  217       uint32_t userId = -1;
  218       int userStatus = -1;
  219       //正常接收到一个请求标识
  220       switch(rues_type)
  221       {
 case REGISTER:
  223             //用户管理模块--注册
  224             userStatus = cs->deal_register(lc->get_tcp_sock(),&userId);
  225             break;
  226           
  227           case LOGIN:
  228             //用户管理模块--登录
  229             userStatus = cs->deal_login(lc->get_tcp_sock());
  230             break;
  231 
  232           case LOGINOUT:
  233             //用户管理模块--退出登录
  234             userStatus = cs->deal_login_out();
  235             break;
  236           
  237           default:
  238             LOG(ERROR,"recv request typt default")<<std::endl;
  239             break;
  240       }
  241                                                                                                                   
  242       //  响应send(sock,buf,size,0)
  243       reply_info ri;
  244       ri.status = userStatus;
  245       ri.user_id = userId; 
  246       ssize_t send_size = send(lc->get_tcp_sock(),&ri,sizeof(ri),0);
  247       if(send_size < 0)
  248       {
  249         //如果发送数据失败,是否考虑应用层重新发送
  250         LOG(ERROR,"send F")<<std::endl;
  251       }
  252       LOG(INFO,"send success")<<std::endl;
  253 
  254       //将tcp连接释放掉                                                                                           
  255       close(lc->get_tcp_sock());
  256       delete lc;
  257       return NULL;
  258     }

2.创建生产线程,接收客户端的消息,并放入消息池

 pthread_t tid;
  121       for(int i = 0;i < PRODUCER_COUNT;i++)
  122       {                  
  123         int ret = pthread_create(&tid,NULL,product_msg_start,(void*)this);
  124           if(ret < 0)
  125           {
  126             LOG(FATAL,"pthread_create product_msg_start")<<std::endl;
  127             exit(4);
  128           }
  129           //printf("product_msg_start success\n");
  130         ret = pthread_create(&tid,NULL,consume_msg_start,(void*)this);
  131           if(ret < 0)
  132           {
  133             LOG(FATAL,"pthread_create consume_msg_start")<<std::endl;
  134             exit(4);
  135           }
  136           //printf("consume_msg_start success\n");
  137       }


 static void* product_msg_start(void* arg)
  171     {
  172       pthread_detach(pthread_self());
  173       chat_server* cs = (chat_server*)arg;
  174       while(1)
  175       {                                                                                                           
  176         //将接收到的消息缓存到数据池中
  177         cs->recv_msg();
  178        //printf("recv_msg\n");
  179       }
  180       return NULL;
  181     }

3.创建消费线程,从消息池获取消息,并群发给所有在线用户

 static void* consume_msg_start(void* arg)
  184     {
  185       pthread_detach(pthread_self());
  186       
  187       chat_server* cs  = (chat_server*)arg;
  188       while(1)
  189       {
  190         //从数据池中获取数据
  191        // printf("many_send_msg\n");
  192         cs->many_send_msg();
  193       }
  194       return NULL;
  195     }

为何会使用生产者-消费者模型以及数据池
可以解决多个用户同时发送消息,通过生产线程将消息放入数据池,消费线程从数据池取出,这样逐步处理,就可以将多个消息一一接收,一一存储,一一发送。
数据池:线程安全队列+2个接口(push pop)

线程安全:同步(条件变量)+互斥(互斥锁)
队列:STL:queue
push_msg_que();
pop_msg_que();

void push_msg_pool(std::string& msg)
 27    {
 28       pthread_mutex_lock(&msg_queue_lock);
 29       while(is_full())
 30       {                                                                                                             
 31         pthread_cond_wait(&product_queue,&msg_queue_lock);
 32       }
 33       msg_queue.push(msg);
 34       pthread_mutex_unlock(&msg_queue_lock);
 35       pthread_cond_signal(&consumer_queue);
 36     }
 37     void pop_msg_pool(std::string* msg)
 38     {
 39       pthread_mutex_lock(&msg_queue_lock);
 40       while(msg_queue.empty())
 41       {
 42         pthread_cond_wait(&consumer_queue,&msg_queue_lock);
 43       }
 44       *msg = msg_queue.front();
 45       msg_queue.pop();
 46       pthread_mutex_unlock(&msg_queue_lock);
 47       pthread_cond_signal(&product_queue);
 48     }

private:
 59     std::queue<std::string> msg_queue;
 60     //约束队列大小,防止队列无限扩容,导致内存过大,被操作系统强杀
 61     size_t capacity_;
 62     //互斥锁
 63     pthread_mutex_t msg_queue_lock;
 64     //生产者条件变量
 65     pthread_cond_t product_queue;
 66     //消费者条件变量
 67     pthread_cond_t consumer_queue;   
为了方便阅读使用json,提高用户体验

主要参考C++json详解C++中JSON的使用详解【转】

 public:
 15   //反序列化
 16   //将客户端发送来的json数据反序列化
 17   void deseruakuze(std::string mes)
 18   {
 19     Json::Reader r;
 20     Json::Value v;
 21     r.parse(mes,v,false);
 22 
 23     name_ = v["name_"].asString();
 24     from_ = v["from_"].asString();
 25     msg_ = v["msg_"].asString();
 26     user_id = v["user_id"].asInt();
 27   }
 28 
 29   void serialize(std::string* msg)
 30   {
 31     Json::Value v;
 32     v["name_"] = name_;
 33     v["from_"] = from_;
 34     v["msg_"] = msg_;            
 35     v["user_id"] = user_id;
 36 
 37     Json::FastWriter w;
 38     *msg = w.write(v);
 39   }

以上代码都一些简略代码,具体代码详见




  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值