#一个大型项目
起点聊天室的详细介绍
本项目仍在持续更新中。
介绍
起点聊天室项目包含了局域网和全局网络两大模块。
全局网模块
- 目的:以 QQ 为参照,熟悉大型即时通讯软件的开发流程。
- 服务端技术栈:
Linux,C++,libevent网络库, 线程池, 数据库连接池, 文件服务器,MySQL,Log4j风格的日志框架, 单例模式, 观察者模式,gdb调试工具。 - 客户端技术栈:
Qt, 信号与槽,RAII, 文件 IO, 网络编程, 多线程,Json解析。
主要功能
- 登录模块
- 通过文件 IO 保存登录过的用户头像、账号,并可选择性保存密码。
- 通过选择历史账号,自动填充头像和密码。
- 支持无账号密码直接登录局域网聊天。
- 实现自动登录、记住密码、找回密码、注册账号等基础功能。
- 个人信息模块
- 支持个人信息的修改与展示。
- 好友系统
- 支持添加好友。
- 聊天功能
- 实现一对一单人聊天。
- 实现多人在线群聊。
局域网模块
- 目的:开发一个在公司内部使用的轻量化聊天与文件传输工具,防止信息泄露,无需中心服务器。
- 技术栈:
Qt Widget,C++, 网络编程, 文件 IO,UDP广播,TCP文件传输。
主要功能
- 实现局域网内所有用户的自动发现、群聊和私聊。
- 支持选择性地加入或创建群聊。
- 基于
TCP的可靠文件传输。
开发日志
本日志仍在持续更新中。
登录模块
前端展示

2024年3月23日
开发了 login_dat 接口,用于通过字节流高效地存储和读取用户的头像、账号及密码。
接口设计
- 技术:利用
RAII思想管理user.dat文件的资源,在构造时获取资源并初始化,在析构时自动关闭和保存。 login_dat()- 构造函数:自动识别并创建
./user.dat文件。如果文件已存在,则自动读取数据到内存中的user_info_list。
- 构造函数:自动识别并创建
~login_dat()- 析构函数:将内存中
user_info_list的最新状态写回user.dat文件。
- 析构函数:将内存中
write_user_info(const QPixmap &avater, const QString &name, const QString &password="")- 将新的用户信息写入
user_info_list。如果用户已存在,则不进行任何操作。
- 将新的用户信息写入
get_user_info_list() ---> QVector<user_info>*- 返回
user_info_list的指针。
- 返回
is_has(const QString& name) ---> bool- 检查指定账号是否存在。
user.dat 字节流格式
1 | |
开发难点
- QPixmap 与 QByteArray 之间的转化
- 从
QByteArray加载QPixmap:QPixmap.loadFromData(QByteArray) - 将
QPixmap保存到QByteArray:需要一个QBuffer作为中介。1
2
3
4
5QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "PNG"); // format
buffer.close();为什么需要 QBuffer?
因为QPixmap::save()方法需要一个QIODevice设备作为写入目标,而QByteArray本身不是QIODevice。QBuffer可以将QByteArray包装成一个内存中的QIODevice设备,从而让save()方法可以将图片数据写入到这个字节数组中。
- 从
Log4j 日志框架
框架分析

2024年3月26日
断断续续分析了两天,大致框架终于搞懂了。日志系统主要分为四大模块:日志器 (Logger)、日志事件 (LogEvent)、格式化器 (Formatter) 和 **输出地 (Appender)**。
代码亮点:
使用宏定义简化字符串到枚举的转换,代码更简洁,但牺牲了一定的可读性。
1 | |
复习 C/C++ 的可变参数列表:
1 | |
va_list: 定义一个指向可变参数列表的指针。va_start: 初始化va_list指针,使其指向第一个可变参数。va_arg: 读取当前参数,并使指针后移。va_end: 清理可变参数列表。
2024年3月29日
通过 gdb 调试,终于把日志系统的核心业务逻辑彻底搞懂了。这是一个典型的责任链与多态结合的设计模式。
核心流程:
- **日志管理器 (LoggerManager)**:采用单例模式管理所有的日志器。默认会创建一个名为
root的根日志器。 - **日志器 (Logger)**:
root日志器在构造时,会默认创建一个标准输出StdOutAppender和一个默认格式DefaultFormatter。 - **事件 (LogEvent)**:当代码中产生一条日志时,会创建一个
LogEvent对象。该对象必须关联一个Logger。 - **事件包装器 (LogEventWrap)**:
LogEvent通常被LogEventWrap包装。在LogEventWrap的析构函数中,它会调用Logger的写入函数,将LogEvent自身作为参数传入。 - 写入过程:
Logger收到LogEvent后,会遍历其下所有可用的Appender列表。- 通过多态调用每个
Appender的写入函数。 - 在
Appender内部,再通过多态调用其关联的Formatter的解析函数。 Formatter将日志格式字符串(如 `%d