C++学习项目 仿微信Socket即时通讯项目

📊 迅聊(ChatForge) 轻量即时通讯系统

🚀 基于 C++17 + Qt6 的即时通讯系统,14天从 0 到 1 完整实现 TCP 服务端+QT客户端全链路。

● 可视化成果:不像算法题只能看控制台输出,搭配Qt图形客户端,可直观操作好友、收发消息、查看聊天气泡,成就感拉满
● 面试高频:原生Socket、TCP通信、多线程并发、SQLite数据库、Qt界面开发是C++后端/桌面端开发的核心考点
● 知识覆盖广:一个项目涵盖Linux Socket、多线程、自定义TCP协议、SQLite、JSON解析、Qt GUI、网络安全等20+知识点
● 难度递进:每天都有新挑战,但每天都能跑起来,不会卡住劝退

项目亮点

  • 🔥 纯原生 API:服务端不依赖 Boost/Asio,直接使用 Linux Socket + pthread
  • 🔥 完整链路:从 socket 到 UI,覆盖 IM 系统全技术栈
  • 🔥 可扩展设计:分层架构 + 枚举指令集,新增功能只需加枚举和 case

项目展示

alt

🛠️ 技术栈

层级 技术 星级
服务端语言 C++17 ⭐⭐⭐⭐
网络层 Linux Socket API(纯原生,无框架) ⭐⭐⭐⭐⭐
并发模型 pthread 多线程 ⭐⭐⭐⭐
数据库 SQLite3 + SQLiteCpp ⭐⭐⭐⭐
序列化 nlohmann/json(单头文件库) ⭐⭐⭐⭐
客户端 GUI Qt6 Widgets ⭐⭐⭐⭐⭐
客户端网络 QTcpSocket ⭐⭐⭐⭐⭐
UI 设计 Qt Designer + QSS ⭐⭐⭐⭐
构建系统 CMake(服务端)/ qmake(客户端) ⭐⭐⭐⭐

环境要求

组件 最低要求
编译器 g++ 9+ / MSVC 2022+
C++ 标准 C++17
CMake 3.10+
Qt 6.x(仅客户端需要)
SQLite 3.x 开发库
OS Linux(服务端)/ Windows(客户端)

一、14 天学习计划汇总

Day 1–7:服务端核心(C++17 · Linux Socket · SQLite)

天数 主题 核心知识点 创建/修改文件 实战输出
1 环境搭建 & Hello World socket 4步(create/bind/listen/accept)、TCP基础、g++编译、nc测试、字节序(htonl/htons)、SO_REUSEADDR server/main.cpp(Day1版) ✅ 服务端打印"欢迎来到迅聊服务器"
2 服务端架构 & 多线程 多线程accept、JSON协议设计(4字节头+JSON体)、枚举/宏/typedef、pthread_create/pthread_setname_npmap/vectorMSG_WAITALLgetopt参数解析 common.h/cppinfos.hDeThread.hsession.h/cppchatTask.h/cppmain.cpp(升级版)、CMakeLists.txt ✅ 多线程回显服务器,支持 -p 端口参数
3 数据库 & 注册登录 SQLite 4表(user/friend/group_table/member)、SQLiteCpp API、CommandHandler模式、extern全局变量、try-catch/stoi、用户在线映射 userMap<fd,account>PRIMARY KEY/AUTOINCREMENT CommandHandler.h/cpp(Regist/Login)、修改main.cpp(数据库初始化)、修改session.cpp(handleMsg) ✅ 可注册/登录的服务器,数据持久化
4 好友系统 & 消息转发 UNION双向好友查询、函数重载(sendMsg 3重载)、消息转发(查userMap→转发)、std::find()算法、usleep微秒延迟、系统助手(10000) 扩展CommandHandler.cpp(Search/AddFriend/FriendList/Chat)、扩展session.cpp(getFriendList/getFriendFd) ✅ 支持添加好友、私聊、在线状态查询
5 群聊系统 群聊广播(查member表→查userMap→循环send→排除发送者)、JOIN多表查询、vector增删改查、群主权限、SQLite事务(BEGIN/COMMIT/ROLLBACK)、datetime('now') 扩展CommandHandler.cpp(CreateGroup/GroupChat/GroupList)、扩展session.cpp(getGroupMember/getGroupOwnerFd) ✅ 支持创建群聊、群发消息、ACID事务保证
6 Qt 客户端工程搭建 QTcpSocket/信号槽(sync vs async)、QJsonDocument/QJsonObject/QJsonArray/QByteArray、缓冲区粘包处理(while+append+mid+remove)、Q_OBJECT宏、emit信号分发、父子对象机制 main.cpp(Qt版)、client.h/cpplogging.h/cpptcpclient.h/cpp、各.ui文件、Client.pro ✅ 可登录/注册的Qt界面,信号槽解耦架构
7 登录注册界面 Qt Designer可视化设计、QSS样式表(background/border/radius/hover/pressed)、QStackedWidget页面切换、QLineEdit(密码模式/placeholder)、QPropertyAnimation淡入/抖动动画、QGraphicsOpacityEffect logging.ui(登录界面)、logging.h/cpp(业务逻辑)、main.cpp(窗口初始化) ✅ 带QSS美化+动画的登录/注册界面

Day 8–14:客户端完善 & 全栈集成

天数 主题 核心知识点 创建/修改文件 实战输出
8 主窗口 & 好友列表 QSplitter可拖拽分割、QListWidget+自定义Item(FriendItem)、QVBoxLayout/QHBoxLayout嵌套布局、setItemWidgetpaintEvent重写、TcpClient信号分发(CallLogging/CallClient/CallAddFriend) client.h/cpp(主窗口)、frienditem.h/cpp(自定义列表项)、Rewriting/目录 ✅ 可拖拽面板+好友/群聊列表+消息分发架构
9 聊天窗口 & 消息发送 QTextBrowser+HTML气泡(div/span/inline-block/background)、append() vs setHtml()QMap<int,ChatWidget*>窗口管理、QToolButtonQTextCursor光标插入、R"(...)"原始字符串 chatwindow.h/cpp(聊天窗口)、sendtextedit.h/cpp(发送输入框)、Chatting/目录 ✅ 左右对齐气泡聊天+多窗口管理+Enter发送
10 客户端群聊功能 群聊消息分发(cmd_group_chat区分cmd_friend_chat)、QJsonArray遍历、ChatWidget复用(isGroupChat标志)、群成员列表刷新、onRefreshGroupList 扩展client.cpp(群聊case)、扩展chatwindow.cpp(群消息显示)、扩展frienditem.cpp(群组Item) ✅ 私聊/群聊共用同一ChatWidget+群成员列表
11 添加好友 & 搜索 QDialog模态对话框(exec/show)、QLineEdit::textChanged实时搜索/returnPressed回车搜索、accept()/reject()返回值、setPlaceholderText、验证消息机制、QDialog::Accepted/Rejected addfriend.h/cpp/ui(搜索对话框)、systemmessage.h/cpp/ui(系统消息)、verificationitem.h/cpp/ui(验证消息项) ✅ 实时搜索用户+验证消息+同意/拒绝好友
12 表情/图片/头像/创建群聊 Unicode Emoji(U+1F60A)、QGridLayout网格按钮、Qt::Popup/Qt::FramelessWindowHint窗口标志、QPixmap缩放(scaled/KeepAspectRatio/SmoothTransformation)、QPainter圆形头像绘制、QDataStream/QFile文件读写、群聊创建事务 emojiselector.h/cpp(表情选择器)、iconselect.h/cpp/ui(头像选择)、imagepreview.h/cpp(图片预览)、group_create相关 ✅ Emoji选择器+头像裁剪+图片预览+创建群聊
13 完善 & 调试 QStandardPaths跨平台路径、QDir/QFile/QFileInfo文件操作、QMessageBox(info/warning/critical/question)、qDebug调试输出、QTimer心跳包(保活+断线检测)、QAbstractSocket::SocketError错误处理 所有文件的最终集成、tcpclient.cpp(心跳逻辑)、mainwindow.cpp(连接错误处理) ✅ 完整IM客户端+心跳保活+错误处理+文件缓存
14 项目总结 & 扩展 架构回顾(C/S全链路)、20+协议指令完整列表、nlohmann/json::at() vs operator[]epoll/io_uring扩展方案、TLS加密(QSslSocket)、离线消息(message_history表)、哈希密码(SHA-256+盐值)、设计模式(单例/观察者/策略/工厂) 无新增文件,回顾所有代码 ✅ 完整项目总结+扩展方向+面试准备

二、从本项目能学到什么

2.1 核心技术栈掌握程度

技术领域 具体技能 掌握程度 项目代码量
C++编程 类/继承、STL(map/vector)、函数重载、externtypedef/usingenum、异常处理、lambda、RAII、new[]/delete[] ⭐⭐⭐⭐ ~3000行(服务端)
Linux网络编程 socket 4步、字节序(htonl/htons)、SO_REUSEADDRMSG_WAITALLinet_ntoa/inet_ntopSOCK_STREAM vs SOCK_DGRAMgetoptbacklog ⭐⭐⭐⭐⭐ ~200行(网络层)
多线程编程 pthread_create、线程属性(joinable/detached)、pthread_setname_npstd::thread、互斥锁、死锁避免、std::atomic、异常安全 ⭐⭐⭐⭐ ~100行(线程封装)
网络协议设计 自定义应用层协议(4字节头+JSON体)、粘包/分包处理、缓冲区拼接算法、枚举驱动指令集、协议可扩展性 ⭐⭐⭐⭐⭐ ~100行(协议层)
JSON序列化 nlohmann/json:创建/解析/序列化、at()安全访问 vs operator[]、try-catch解析异常、dump()序列化 ⭐⭐⭐⭐ ~50行(JSON操作)
SQLite数据库 4表设计(PRIMARY KEY/AUTOINCREMENT/NOT NULL/DEFAULT)、CRUD、参数化查询防注入、UNION/JOIN/子查询、事务ACID、datetime('now')PRAGMA busy_timeout ⭐⭐⭐⭐ ~300行(数据库操作)
Qt6 GUI开发 信号槽机制(sync)、Q_OBJECT宏、emit、Widgets/布局管理器、QSplitterQStackedWidgetQListWidget+自定义Item、QSS样式表 ⭐⭐⭐⭐⭐ ~2000行(客户端)
Qt网络编程 QTcpSocket(readyRead)、QJsonDocument/QJsonObject/QJsonArrayQByteArray缓冲区、粘包处理、信号分发架构 ⭐⭐⭐⭐⭐ ~300行(TcpClient)
UI交互设计 聊天气泡(HTML+CSS)、Emoji选择器(Qt::Popup)、头像绘制(QPainter圆形)、图片预览(QPixmap缩放)、QDialog模态框 ⭐⭐⭐⭐ ~500行(UI相关)
调试与优化 qDebugQMessageBoxQTimer心跳、QStandardPaths、GDB、netstat/tcpdump、Valgrind ⭐⭐⭐ ~100行(调试相关)
构建系统 CMake(服务端)、qmake(客户端)、thirdparty集成(git submodule)、Makefile ⭐⭐⭐ ~50行(构建配置)

2.2 工程能力提升

能力维度 项目中的具体体现
分层架构设计 客户端: TcpClient(网络层) → 3信号分发 → UI层; 服务端: Session(会话层) → CommandHandler(业务层) → SQLite(数据层)
模块解耦 TcpClient不知道谁在处理消息(信号槽解耦); CommandHandler不知道网络细节(仅操作json对象)
代码复用 ChatWidget通过isGroupChat标志同时服务私聊和群聊; sendMsg 3重载(发给自己/指定fd/fd列表)
错误处理 try-catch捕获JSON解析异常、stoi转换异常、数据库异常; taskThread外层catch确保close(fd)资源释放
资源管理 RAII(Session析构close socket)、new[]/delete[]配对、Qt父子对象自动释放、std::lock_guard自动解锁
协议可扩展 枚举驱动指令集+switch-case分发,新增命令只需+枚举值+case分支,不改动框架
线程安全 互斥锁保护userMap共享资源; 每个Session对象线程内隔离; SQLite busy_timeout处理并发
跨平台设计 服务端Linux(Socket/pthread) + 客户端Windows(Qt6),统一4字节头+JSON协议对接
面试准备 配套70道面试题+12个简历模板,覆盖C++/网络/多线程/数据库/Qt/架构六大领域

2.3 面试高频考点对照

面试题 本项目中的体现 相关代码位置
TCP粘包/分包如何处理? 4字节定长头 + JSON可变体 + 缓冲区while循环拼接 session.cpp:recvMsg()tcpclient.cpp:onReadyRead()
多线程服务器如何设计? accept主线程 + 每客户端一个taskThread + detached自动回收 main.cpp:accept循环chatTask.cpp
聊天消息转发流程? A→服务器查userMap→B的fd→服务器send转发 session.cpp:handleMsg() case cmd_friend_chat
私聊和群聊的区别? 私聊:1次send; 群聊:查member表→查userMap→循环send CommandHandler.cppsession.cpp:getGroupMember()
select/poll/epoll区别? 可扩展:将线程模型改造为epoll+线程池 见docs/项目面试题库-70题.md Q21
数据库事务如何保证? 创建群聊: BEGIN→INSERT group_table→INSERT member×N→COMMIT/ROLLBACK CommandHandler.cpp:CreateGroup()
信号槽同步还是异步? 默认同步(emit立即调用槽函数,返回后再继续) tcpclient.h 信号定义
自定义协议如何设计? 枚举定义20+指令、JSON承载数据、4字节头解决粘包 common.h enum commands
Qt如何管理多聊天窗口? QMap<int,ChatWidget*>按账号/群号索引,存在即激活,不存在则创建 client.cpp:openChatWindow()
std::mapunordered_map选择? userMap用map:并发<200、O(log n)可接受、代码简单、无哈希冲突 common.h:userMap
函数重载匹配规则? sendMsg 3重载:精确匹配→提升匹配→标准转换→用户定义转换 session.h:sendMsg() × 3
new[]/delete[]为什么要配对? recvMsg中new char[len+1]必须delete[] msg,否则UB session.cpp:recvMsg()
RAII是什么? Session构造函数获取fd、析构函数自动close(fd),异常安全 session.h/cpp
SQL参数化查询防注入? Statement query(db, "SELECT * FROM user WHERE account=?"); query.bind(1, account); CommandHandler.cpp 全部SQL操作
如何检测客户端断线? recv()返回0→优雅关闭; recv()返回-1→异常断开(ECONNRESET等); 心跳超时 session.cpp:recvMsg()tcpclient.cpp:QTimer心跳

三、项目整体架构分析

3.1 系统分层架构

层次 组件 核心类/文件 技术栈 核心职责
客户端UI 界面层 client.cpplogging.cppchatwindow.cppfrienditem.cppemojiselector.cpp Qt6 Widgets · QSS · Qt Designer 登录/注册/主窗口/聊天气泡/表情选择/头像设置
客户端网络 通信层 tcpclient.h/cpp QTcpSocket · QJsonDocument · QJsonObject TCP连接管理、4字节头编解码、3信号分发(CallLogging/CallClient/CallAddFriend)
网络传输 传输层 TCP · 自定义应用层协议 4字节长度头(网络字节序) + JSON消息体(UTF-8)
服务端接入 连接层 main.cppchatTask.cpp POSIX socket · pthread socket→bind→listen→accept循环、每客户端一线程
服务端会话 会话层 session.h/cpp C++17 · json · socket recvMsg/handleMsg/sendMsg、userMap管理、粘包处理
服务端业务 业务层 CommandHandler.h/cpp C++17 · SQLiteCpp 注册/登录/好友/群聊/搜索/验证消息等全部业务逻辑
数据持久化 数据层 SQLite3 · SQLiteCpp 4表设计(user/friend/group_table/member)、参数化查询
在线管理 状态层 userMapcommon.h std::map<int,int> fd↔account映射、上下线追踪、消息路由

3.2 通信协议明细

字段 长度 格式 说明
长度头 4字节 uint32_t 网络字节序 标识后续JSON体长度,解决TCP粘包
JSON体 可变 UTF-8 JSON 承载业务数据,含cmd/account/msg等字段
最大消息 16MB 由4字节长度头上限决定

四、学习路径建议

学习阶段 目标 建议行动 参考文档 预计时间
基础预热 掌握必要前置知识 复习C++类/继承/STL; 了解TCP三次握手; 安装Linux+Qt6开发环境 预备知识清单 3天
Day 1–2 理解socket编程+多线程 手打代码:从单线程server到多线程server; 学会g++/CMake编译; 熟悉nc测试 Day01.md, Day02.md 2天
Day 3–5 掌握数据库+业务逻辑 重点理解CommandHandler模式; 手动sqlite3操作验证; 画消息转发流程图 Day03~05.md, 知识点精讲 3天
Day 6–7 Qt客户端入门 理解信号槽机制(核心!); 区分emit/connect/Q_OBJECT; 熟悉Qt Designer布局 Day06~07.md, 知识点精讲 2天
Day 8–10 客户端完整功能 掌握QListWidget自定义Item; 理解HTML气泡原理; 私聊/群聊复用设计 Day08~10.md, 知识点精讲 2天
Day 11–13 功能完善+调试 学习QDialog模态框; QTimer心跳; QPixmap图片处理; qDebug调试技巧 Day11~13.md, 知识点精讲 2天
Day 14 总结+扩展 复习架构图; 尝试加新功能(离线消息/文件传输); 用面试题库自测 Day14.md, 面试题库 1天
面试冲刺 用项目打动面试官 对照70道面试题逐一自答; 选1-2个简历模板微调; 准备5分钟项目演示 面试题库, 简历模板 3天

部分技术点讲解

一、Socket 网络编程核心

1.1 什么是 Socket?

Socket(套接字)是操作系统提供的一个通信端点抽象,用于实现不同主机之间或同一主机不同进程之间的网络通信。它隐藏了底层网络协议(TCP/UDP)的复杂性,提供了类似文件读写的接口。

本质:Socket 是一个文件描述符(file descriptor),可以用 read()/write()recv()/send() 操作。

1.2 TCP Socket 编程的 C/S 模型

服务端标准流程(本项目 server/main.cpp

socket()  →  bind()  →  listen()  →  accept()  →  recv()/send()  →  close()
  创建套接字   绑定端口    监听连接    接受连接     收发数据      关闭

客户端标准流程(本项目 Client/Tools/tcpclient.cpp

socket()  →  connect()  →  send()/recv()  →  close()
  创建套接字   建立连接     收发数据       关闭

1.3 各 API 深入解析

socket(AF_INET, SOCK_STREAM, 0)

int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
参数 含义
domain AF_INET IPv4 地址族
type SOCK_STREAM TCP 流式套接字
protocol 0 自动选择 TCP 协议

三种常用 socket 类型

  • SOCK_STREAM → TCP,可靠有序,面向连接
  • SOCK_DGRAM → UDP,不可靠,无连接
  • SOCK_RAW → 原始套接字,直接操作 IP 层

struct sockaddr_in 结构体

struct sockaddr_in {
    sa_family_t    sin_family;   // 地址族: AF_INET
    in_port_t      sin_port;     // 端口号(网络字节序)
    struct in_addr sin_addr;     // IP 地址(网络字节序)
    char           sin_zero[8];  // 填充字节
};

// 本项目中的使用:
struct sockaddr_in server_addr, client_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡
server_addr.sin_port = htons(default_port);       // 端口 8888

bind() — 绑定套接字和端口

bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr))

将 socket 与 IP 地址和端口号关联。(struct sockaddr *) 是 C 风格的多态转换,因为 bind() 接受通用的 sockaddr 指针,而实际传入的是 sockaddr_in

listen() — 进入监听状态

listen(listen_fd, 4)
  • 第一个参数:监听套接字
  • 第二个参数:backlog — 未完成连接队列的最大长度(同时等待 accept() 的连接数)
  • 调用后 socket 变为被动(passive),可以接受客户端的连接请求

部分面试题展示

一、C++ 基础与进阶(12题)

Q1 ⭐ extern 关键字的作用是什么?

真实场景: 项目中 common.hextern SQLite::Database db 声明全局数据库对象

问题: 解释 extern 关键字的作用。项目中为什么要用 extern 声明 userMapdb

点击展开答案

参考答案:

  • extern 告诉编译器"这个变量在其他文件中定义",用于声明但不定义全局变量
  • 项目中 userMapdbmain.cpp 中定义(分配内存),通过 externsession.cppCommandHandler.cpp 等其他文件能访问同一个实例
  • 没有 extern 会导致"未定义符号"链接错误
// main.cpp — 定义
SQLite::Database db("user.db", ...);
map<int, int> userMap;

// common.h — 声明
extern SQLite::Database db;
extern map<int, int> userMap;

// session.cpp — 使用(只需 #include "common.h")
userMap[socket] = account;  // 访问的是 main.cpp 里的那个

Q2 ⭐ typedefusing 的区别?

真实场景: 项目中用 using json = nlohmann::json;typedef struct _user_info{...} UserInfo;

问题: C++11 的 using 和传统的 typedef 有什么区别?你更推荐哪个?为什么?

点击展开答案

参考答案:

  • typedef 是 C 语言遗留语法,using 是 C++11 引入的
  • using 更直观(等号赋值风格),且支持模板别名(typedef 不支持)
// typedef — 传统风格
typedef struct _user_info { int account; } UserInfo;
typedef void (*FuncPtr)(int);

// using — C++11 风格
using UserInfo = struct _user_info { int account; };
using FuncPtr = void(*)(int);
using JsonDict = std::map<std::string, nlohmann::json>;  // 模板别名

推荐 using:可读性更好,功能更强大(支持模板别名)。

Q3 ⭐ enumenum class 的区别?

真实场景: 项目中用 enum commands { cmd_regist=0, cmd_login, ... } 定义 20+ 命令

问题: C++11 的 enum class 和传统 enum 有什么区别?你的项目为什么用传统 enum

点击展开答案

参考答案:

对比 传统 enum enum class
作用域泄露 ❌ 枚举值暴露到外层作用域 ✅ 枚举值限定在类内
隐式转换 ✅ 可隐式转 int ❌ 必须强制转换
前向声明 ❌ 不支持 ✅ 支持
enum commands { cmd_regist = 0, cmd_login = 1 };
int x = cmd_regist;  // OK,隐式转换

enum class Colors { Red, Green };
int y = Colors::Red;  // ❌ 编译错误!必须 (int)Colors::Red

项目中用传统 enum 是因为命令值需要在 JSON 中直接作为整数收发(msg["cmd"] = cmd_login),隐式转换很方便。如果用 enum class,每次赋值都要 (int) 强转。

部分简历展示

模板1:C++后端开发(校招/应届生)

💼 项目经历:迅聊(ChatForge)即时通讯系统

项目属性 说明
项目周期 14天
技术栈 C++17 · Linux Socket · pthread · SQLite3 · nlohmann/json · CMake
项目规模 服务端15+源文件,~3000行C++代码(全栈的话可以把客户端也加上)
源码地址 xxxx

项目描述: 基于 C++17 开发的轻量即时通讯系统,采用 C/S 架构,支持用户注册登录、好友管理、私聊、群聊等功能。服务端运行在 Linux 平台,客户端基于 Qt6 开发。

主要职责与成果:

  1. 服务端核心开发: 基于 Linux Socket API(socket/bind/listen/accept)实现 TCP 服务器,支持多客户端并发连接
  2. 多线程并发处理: 采用「主线程 accept + 每客户端一线程」模型,通过 pthread_create 创建独立 taskThread 处理客户端会话,支撑 100+ 并发连接
  3. 自定义应用层协议: 设计并实现「4字节长度头 + JSON 消息体」协议格式,解决 TCP 粘包/分包问题,支持 20+ 种业务指令
  4. 数据库设计: 使用 SQLite3 + SQLiteCpp 封装,设计 4 张业务表(user/friend/group_table/member),实现注册登录、好友关系、群组成员的数据持久化
  5. 命令分发架构: 通过 CommandHandler 模式解耦网络收发与业务逻辑,支持可扩展的指令路由

项目亮点:

  • 完整实现了从 TCP 连接建立到业务逻辑处理的全链路通信系统
  • 通过消息转发机制(userMap 在线映射 + 查表路由)实现私聊与群聊
  • 自定义枚举指令集,系统可灵活扩展新功能

🛠️ 技能清单

技能类别 具体技能
编程语言 C/C++(熟练)、Python(了解)
网络编程 TCP/IP协议、Socket API、select/epoll(了解)
数据库 SQLite3、MySQL基础
工具链 Git、CMake、GDB、Valgrind
操作系统 Linux(Ubuntu/CentOS)、Shell基础
其他 多线程编程、JSON序列化、设计模式

详细情况可看:https://mp.weixin.qq.com/s/8ehk9QGQKfnigX4oHlZGzg

#简历中的项目经历要怎么写##计算机#
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务