最近学习了网络编程后, 写了一个基于TCP、UDP协议的群聊系统。

技术栈

1、TCP、UDP通信
      2、生产、消费模型, 支持并发
      3、自定义协议封装数据 & json封装数据
      4、STL中无序map、vector容器使用
      5、日志管理模块 - 在线显示后台情况
      6、ncurses库绘制聊天界面

支持功能

1、支持用户注册、登录。 注册成功后台会返回一个账号ID, 然后用户通过账号和密码进行登录, 进入聊天系统进行聊天。
      2、支持群发功能。当一方发送一次消息, 其他在线用户都可以接收到。
      3、支持在线用户显示。 当用户发送一次消息后,其他用户的在线列表就会显示该用户。

原理剖析

一、注册、登录模块

1、注册、登录模块采用TCP协议实现。因为注册、登录模块的通信非常重要, 因此采用TCP协议是为了保证的数据传输的可靠性。 但由于是TCP,存在粘包问题, 我们采用自定义协议(自定义实现的类)来解决粘包问题。

1、自定义注册信息协议

//注册信息
struct RegInfo{
       char NickName[15]; //用户注册的姓名
       char School[20]; //学校
       char Passwd[20]; //密码
};

我们可以看出这是一个普通的类, 客户端在发送数据的时候按照RegInfo类的大小填充, 服务器端接收数据按照RegInfo类大小接收并解析。 这样就解决了粘包问题。 (当然这样也有局限性,在于我们发送和接收数据大小是有限的)。 在设计的时候,对于注册用户的个人信息和密码很重要。

2、自定义登录协议

//登录信息
       struct LoginInfo{
       uint32_t UserId; //返回用户使用的用户名
       char Passwd[20]; //密码
};

同样解决粘包问题, 用户登录时 用户ID 和 密码 非常关键。 后台需要识别用户ID是否存在且存在是否密码正确的问题等

3、自定义应答

//应答信息
struct ReplyInfo{
       int Status;
       uint32_t UserId;
};

这个协议主要时服务器给我们客户端返回的数据包信息。 同样解决粘包问题。
这个是客户端在发送注册数据包或者登录数据包后, 它需要知道服务器处理的结果如何? 是否注册成功、注册失败,登录成功,登录失败等现象,并进行下一步的决策。

以上是为了解决TCP粘包问题并且为了解决双方协商的问题。
下面我们重点说注册、登录的细节流程。

1、客户端发送注册、登录数据包时, 它先会发现1字节大小的数据包。它的目的为了让服务器端知道客户端是想注册还是登录。 (服务器端后台会根据1字节进行选择, 是用户注册协议接收下一个数据包还是登录协议接收)

#define REGISTER 0 //用户注册标识
#define LOGIN 1 //用户登录标识

如果1字节数据包发送的是0, 服务器端会进入注册处理。
如果为1, 服务器端会进入登录处理。

2、客户端发送注册请求。

3、客户端发送登录请求

二、用户管理模块

用户管理模块的功能:
1、当用户注册时,存储用户的个人信息,返回用户一个新的ID。
2、登录时判断该用户是否为合格。
3、进行聊天界面还需要判断黑户进入

我设计的用户管理模块包含的功能:1、用户注册;2、用户登录; 3、判断用户是否为黑户等接口。

其中userinfo是一个用户的类, 包含用户的个人信息, ID, 密码, 用户的地址信息,用户的状态信息。

后台采用了unordered_map容器以<ID, userInfo>为键值对构建存储用户信息,
保证可以通过ID键可以查找,修改到对应的userinfo信息。

当用户注册完毕后, 会在unordered_map中添加一个信息,并且将对应的userinfo类中的status置为已注册状态。

用户登录完毕后, 会将对应的unordered_map中对应的userinfo类中status置为已登录状态。

为什么要这样做呢?
答:标志这些状态非常关键, 我在后续会为大家演示, 我在这里大体说一下功能。
防止黑户进入聊天界面。(按照正常的逻辑来讲,只有已登录的用户才可以进入聊天界面, 然后聊天界面是UDP通信, 如果别人知道我服务器的IP地址和UDP绑定的PORT端口,那是不是可以跨过注册、登录模块,直接进入聊天模块呢??)

(聊天模块)当用户在聊天界面第一次消息后, 我们会先检查该用户信息, 然后判断该用户的状态status是否为已登录的状态, 如果是,我们会在userinfo类中填充用户当前的地址信息, 然后把用户的状态status置为已在线状态, 并且将该用户userinfo添加到在线用户列表onlinelist中。(是一个vector<userinfo容器, 它专门存储当前在线用户, 功能在于转发所有在线用户,形成群发的特点。后续介绍)
       如果用户不止一次发送过消息, 二次或多次给我们发送消息时,我们会先检查,然后判断用户userinfo的状态为已在线状态。 就不用管了。 这种用户也是合格的。

三、生产、消费模型

生产线程 + 消费线程 + 线程安全的队列。
(实现需要互斥锁 + 条件变量 + STL中queue)

生产、消费模型的处理逻辑:

一开始线程安全队列为空, 生产线程和消费线程一开始都会创建, 由于一开始队列为空, 因此消费线程会处于阻塞当中,当生产线程把需要处理的数据添加到线程安全队列中后,会唤醒消费线程去取数据处理。 当消费线程处理完毕之后会唤醒生产线程去添加数据。 这样形成一种良性的循环。 对于队列的操作需要保证同步和互斥。

这是线程安全处理的方式之一。 它的优点可以解决多线程带来的高并发问题。
(高并发:一个处理器处理,当存在大量的线程就绪准备使用CPU时, 这种的情况就是高并发,高并发存在的问题线程切换,线程过多,频繁的切换,切换的时候会加载相应的信息, 最终使得整个业务的处理缓慢)
生产、消费模型当队列满了时候生产线程会阻塞(阻塞的线程不会竞争CPU, 它会在阻塞的队列中,等待唤醒),消费线程也同理。 可以有效解决并发问题。

这种模型在CS模型中经常使用。

在这个项目中同样也是用到了它, 我们生产线程负责把用户发来的数据添加到线程安全队列中,去唤醒消费线程去完成对所有在线用户的转发, 从而形成群发的效果。

这个时候我们会用到上述的用户管理模块的 用户在线列表vector<userinfo。
它里面保存的时在线的用户userinfo类。

这个时候只需要循环调用sendto接口发送即可,即实现群发。

四、聊天模块

服务器端: 聊天模块就是UDP通信 + 生产消费模型

当客户端的聊天模块UDPsocket收到了数据包, 这时候会收到json格式的string对象recv_string, 我们需要进行json反序列化将用户发送的数据内容解析出来。

取出来的ID用来用户管理模块的用户检查 (已在上面解释过)。

如果检查用户ID不合格, 我们会忽略处理这条消息。

如果合适我们会将处理, 将json格式的 recv_string 对象添加到生产、消费模型中的线程安全队列, 从而唤醒消费线程去处理。(已在上面解释过)

客户端:当用户登录成功之后会进入聊天界面, 聊天界面分为3块, 接收消息区; 发送消息区;显示在线用户列表区;当然还有顶部的介绍区(虽然有点尴尬!)

我们聊天模块采用UDP通信, UDP通信的好处: 数据报传输, 速度快。

对于发送的数据我们采用 json 封装 和 对应的解析。
需要json封装的数据有{ 用户的姓名; ID; 发送的内容 }
(用户的姓名在后续的在线列表显示有用, ID用于检查用户, 发送内容,聊天嘛,不能不说啥吧)

当用户收到服务器端的数据包时, 它也是接收的json格式的数据。
对其解析后会到 发送用户的姓名; 发送用户的消息内容。
将发送用户的姓名在 在线用户显示区显示。
将发送用户的数据在 接收消息区显示。

从而在客户端会形成聊天的简单样子。 但是核心就是这样实现的。

五、日志系统

在项目开发中, 日志系统必不可少, 我是第一次用到它, 用过就说好。

它会标志我们服务器后台每一次的操作流程。
如果有问题可以查看日志系统进行分析, 准备定位到错误。 尤其是在大型开发中。

通过日志系统我们看出我们程序执行到那块, 执行的怎么样, 那些操作没做等问题。

六、演示:

注册登录界面:

交互界面

后台日志显示

源码链接:

群聊系统项目(基于TCP、UDP实现)相关推荐

  1. Linux小项目-群聊系统

    项目名称:chat_room群聊系统 背景知识与主要技术: 熟悉Linux基本指令的使用(ls,cd,make,mkdir,top,basename,pwd,cp,mv,rm,touch) 熟悉lin ...

  2. 4.基于NIO的群聊系统

    [README] 1.本文总结自B站<netty-尚硅谷>,很不错: 2.文末有错误及解决方法: [1]群聊需求 1)编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非 ...

  3. C++可视化-----QQ群聊系统

    目  录 第二章       概要设计 第三章       详细设计 第四章       测试报告 第五章       安装及使用 第六章       项目总结 需求分析 写这个是看着视频写出来的,项 ...

  4. 分享我用Qt写的游戏组队群聊系统

    #ifndef GETSERVERINFO_H #define GETSERVERINFO_H#include <QObject> #include <QtNetwork/QTcpS ...

  5. Java NIO 应用案例:实现一个简单的群聊系统

    1 案例要求 编写一个 NIO 多人群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞): 服务器端功能: 监测用户上线,离线: 实现客户端消息的转发功能(将该客户端的消息转发给其它客户端): ...

  6. 基于TCP/UDP的P2P网络通信协议研究与实现

    此章节是理论知识,下个章节会奉献源码 摘    要 对等式网络(peer-to-peer,简称P2P),又称点对点技术,是一种实现网络中不同主机直接通信的技术.在物联网的应用中,大量的设备需要能进行点 ...

  7. Netty:实现群聊系统+自定义名称+空闲检测+Protobuf

    作为一个Java小白,持续学习是不可避免的,近期学习了Netty的相关知识,使用Netty实现了一个简单的群聊系统,使用Protobuf进行传输,支持空闲心跳检测,并且可以自定义群聊名称,所以写了此篇 ...

  8. Netty之实现一个简单的群聊系统

    要求 编写一个 Netty 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞) 实现多人群聊 服务器端:可以监测用户上线,离线,并实现消息转发功能 客户端:通过channel可以无阻塞发送消息 ...

  9. socket通信 _ 一个简单的群聊系统

    群聊系统要用到通信socket协议,在java中要用到两个类java.net.ServerSocket和 Java.net.Socket.ServerSocket用于创建服务器,而Socket用于创建 ...

最新文章

  1. 【C#串口编程计划】串口编程简介
  2. java 中调用window系统中的文件,或者执行命令(shell、.CMD、.EXE)并获取返回值
  3. 开源 Web 应用最常见漏洞是 XSS 和 SQLI 漏洞
  4. 就业技术书文件表格_Word格式:工程预结算工作流程图及工作表单,附20余表格...
  5. 给 C# 代码动态着色的 JavaScript 脚本
  6. Bootstrap的lia
  7. SAP Connect对inbound邮件接收问题的处理和调试环境搭建
  8. 图谱问答-理解query
  9. ColorStateList 使用详解
  10. 漫画:数据结构之最短路径 Dijkstra 算法的优化 | 技术头条
  11. 亚马逊股价继续大涨 首度突破每股800美元
  12. 51单片机之程序下载不进单片机
  13. 机器语言、汇编语言(低级语言)、高级语言
  14. 中小创势如破竹未来投资机会在哪
  15. java 四边形_Java 实例 – 打印平行四边形
  16. 电脑连不上网络, 并且宽带连接是灰色的
  17. LeetCode单词规律解法
  18. 小程序转码机器人-微信小程序转二维码
  19. 计算机考试成绩有疑惑,计算机考研疑惑 真的好难受
  20. 简历中使用STAR法则

热门文章

  1. 如何利用亚马逊关键词进行引流?权威关键词设置技巧来了
  2. sql prompt自动分号关闭
  3. NoSql 数据库简介
  4. 白泽四足机器人ROS+rviz仿真(二)整体行走步态
  5. VS 2015激活密钥
  6. 通俗理解TF-IDF文本分析算法
  7. “美国饿了么”DoorDash完成6亿美元融资 估值升至126亿美元
  8. python 超好用的迭代兵器库itertools,十八般兵器哪18般?
  9. 怎样获取iPhone/ipad的设备ID(UUID)号?
  10. VS/Xamarin Android入门(安装配置与基础控件)一