当前位置: 首页 > article >正文

用C语言手搓一个聊天室服务器:从socket到多线程的保姆级踩坑实录

用C语言手搓一个聊天室服务器从socket到多线程的保姆级踩坑实录深夜两点屏幕的蓝光映在脸上第17次编译失败后我盯着gcc报出的segmentation fault陷入了沉思。这就是用C语言实现网络服务的魅力所在——没有现成的框架兜底每个字节的生死存亡都掌握在你手中。本文将带你经历从零构建聊天室服务器的完整过程重点不是展示完美成品而是记录那些教科书不会告诉你的实战细节。1. 环境准备与基础框架1.1 开发环境配置推荐使用Ubuntu 18.04 LTS作为开发环境其稳定的glibc版本能避免许多兼容性问题。以下是必须的依赖项sudo apt update sudo apt install gcc-7 build-essential gdbVSCode配置关键点在于.vscode/tasks.json的编写{ version: 2.0.0, tasks: [ { label: build, type: shell, command: gcc, args: [ ${file}, -o, ${fileBasenameNoExtension}, -lpthread, -Wall, -Wextra, -Werror ] } ] }注意必须添加-lpthread链接参数否则多线程相关函数会报未定义错误。-Wall -Wextra -Werror这三个参数能帮你捕获大多数潜在问题。1.2 基础网络框架搭建TCP服务器的经典四步曲int main() { int listenfd socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv_addr; memset(serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_addr.s_addr htonl(INADDR_ANY); serv_addr.sin_port htons(8888); bind(listenfd, (struct sockaddr*)serv_addr, sizeof(serv_addr)); listen(listenfd, 20); // 第二个参数是backlog队列长度 // 后续处理... }第一个坑bind()失败常见原因有两个端口被占用可通过netstat -tulnp | grep 8888确认没有sudo权限1024以下端口需要root权限2. 用户管理与数据持久化2.1 用户数据结构设计采用双结构体设计分离用户基础信息与在线状态#define MAX_USERS 100 #define USERNAME_LEN 20 #define PASSWORD_LEN 20 // 持久化存储 struct persistent_user { char username[USERNAME_LEN]; char password[PASSWORD_LEN]; }; // 运行时状态 struct online_user { char username[USERNAME_LEN]; int sockfd; pthread_t thread_id; time_t last_active; };2.2 文件持久化的坑用户数据存储最易出问题的环节是文件读写。常见错误做法// 错误示例直接写入结构体 fwrite(user, sizeof(struct persistent_user), 1, fp);正确做法应该是文本模式存储// 正确做法文本格式存储 fprintf(fp, %s\n%s\n, user.username, user.password);踩坑记录二进制存储在不同平台可能因内存对齐问题导致读取失败且不利于调试查看。3. 多线程并发处理3.1 连接管理模型采用每个连接一个线程的经典模型while(1) { int connfd accept(listenfd, NULL, NULL); pthread_t thread; pthread_create(thread, NULL, handle_client, (void*)(intptr_t)connfd); pthread_detach(thread); // 避免需要join }线程安全要点所有全局变量访问必须加锁使用pthread_mutex_timedlock避免死锁为每个线程设置取消点3.2 消息队列实现为避免多线程同时写socket导致数据混乱需要实现消息队列struct message { int target_fd; char content[256]; struct message *next; }; pthread_mutex_t queue_lock PTHREAD_MUTEX_INITIALIZER; struct message *msg_queue NULL; void enqueue_message(int fd, const char *text) { struct message *msg malloc(sizeof(struct message)); msg-target_fd fd; strncpy(msg-content, text, 255); msg-content[255] \0; pthread_mutex_lock(queue_lock); msg-next msg_queue; msg_queue msg; pthread_mutex_unlock(queue_lock); }4. 核心功能实现细节4.1 命令解析技巧使用strtok_r实现安全的命令解析void parse_command(int sockfd, const char *cmd) { char *saveptr; char *cmd_copy strdup(cmd); char *verb strtok_r(cmd_copy, , saveptr); char *target strtok_r(NULL, , saveptr); char *message strtok_r(NULL, \n, saveptr); if(strcmp(verb, SEND) 0) { if(target message) { handle_send(sockfd, target, message); } } // 其他命令处理... free(cmd_copy); }4.2 心跳检测机制防止僵尸连接占用资源需要实现心跳void *heartbeat_checker(void *arg) { while(1) { sleep(60); // 每分钟检查一次 time_t now time(NULL); pthread_mutex_lock(users_lock); for(int i0; iMAX_USERS; i) { if(online_users[i].sockfd ! -1 now - online_users[i].last_active 300) { close(online_users[i].sockfd); online_users[i].sockfd -1; } } pthread_mutex_unlock(users_lock); } return NULL; }5. 调试与性能优化5.1 GDB调试技巧多线程调试需要特殊命令gdb -p pid # 附加到运行中的进程 (gdb) info threads # 查看所有线程 (gdb) thread 2 # 切换到线程2 (gdb) bt # 查看当前线程调用栈5.2 Valgrind内存检查必须检查内存泄漏valgrind --leak-checkfull --show-leak-kindsall ./server典型输出解读definitely lost确认内存泄漏possibly lost可能泄漏需要检查still reachable程序结束时未释放但仍有指针指向6. 安全防护措施6.1 输入验证所有用户输入必须验证int is_valid_username(const char *name) { if(strlen(name) 3 || strlen(name) USERNAME_LEN) return 0; for(const char *p name; *p; p) { if(!isalnum(*p) *p ! _) return 0; } return 1; }6.2 防DoS策略限制连接频率struct ip_counter { char ip[16]; time_t last_connect; int count; }; #define MAX_ATTEMPTS 5 #define BAN_TIME 3600 int check_connect_rate(const char *ip) { time_t now time(NULL); // 查找该IP的记录 // 如果尝试次数超过MAX_ATTEMPTS且时间在BAN_TIME内返回0 // 否则更新记录并返回1 }7. 生产环境部署建议7.1 系统调优调整Linux系统参数# 增加最大文件描述符数 ulimit -n 65535 # 调整TCP参数 echo 30 /proc/sys/net/ipv4/tcp_fin_timeout echo 1 /proc/sys/net/ipv4/tcp_tw_reuse7.2 日志管理实现分级日志系统#define LOG_LEVEL_DEBUG 0 #define LOG_LEVEL_INFO 1 #define LOG_LEVEL_ERROR 2 void log_message(int level, const char *fmt, ...) { if(level current_log_level) return; va_list args; va_start(args, fmt); time_t now time(NULL); char timestr[20]; strftime(timestr, 20, %Y-%m-%d %H:%M:%S, localtime(now)); fprintf(log_file, [%s] , timestr); vfprintf(log_file, fmt, args); fputc(\n, log_file); va_end(args); }当我把这个服务器运行在树莓派上连续7天无崩溃时终于确信那些深夜调试是值得的。最意外的收获是在accept()循环里加个5ms的usleep()CPU占用率从90%降到了3%——有时候最简单的优化反而最有效。

相关文章:

用C语言手搓一个聊天室服务器:从socket到多线程的保姆级踩坑实录

用C语言手搓一个聊天室服务器:从socket到多线程的保姆级踩坑实录 深夜两点,屏幕的蓝光映在脸上,第17次编译失败后,我盯着gcc报出的"segmentation fault"陷入了沉思。这就是用C语言实现网络服务的魅力所在——没有现成的…...

从入门到精通:摄影测量学核心概念与应用全景解析

1. 摄影测量学入门指南:从零开始理解核心概念 第一次接触摄影测量学时,我被那些专业术语搞得晕头转向。直到有一次在公园用手机拍摄了一组树木照片,尝试用免费软件生成3D模型后,才真正理解了这门技术的魅力。摄影测量学本质上就是…...

5分钟掌握ExplorerPatcher:Windows界面定制终极指南

5分钟掌握ExplorerPatcher:Windows界面定制终极指南 【免费下载链接】ExplorerPatcher This project aims to enhance the working environment on Windows 项目地址: https://gitcode.com/GitHub_Trending/ex/ExplorerPatcher 还在为Windows 11的新界面感到…...

VMware解锁macOS完整指南:3步免费运行苹果系统

VMware解锁macOS完整指南:3步免费运行苹果系统 【免费下载链接】unlocker VMware Workstation macOS 项目地址: https://gitcode.com/gh_mirrors/unloc/unlocker 你是否渴望在Windows或Linux电脑上体验macOS的魅力?无论你是开发者需要测试iOS应用…...

PyTorch数据集加载进阶:除了CIFAR10,你的自定义数据该怎么准备?

PyTorch数据集加载进阶:从CIFAR10到自定义数据的深度实践 在深度学习项目中,数据准备往往比模型构建更耗时。许多开发者能熟练使用torchvision.datasets加载标准数据集,却对自定义数据束手无策。本文将带你深入PyTorch数据加载机制&#xff…...

WarcraftHelper:魔兽争霸3兼容性修复终极解决方案

WarcraftHelper:魔兽争霸3兼容性修复终极解决方案 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为经典游戏魔兽争霸3在现代Windows系…...

实战:用Python的scipy和numpy搞定分数阶灰色模型(FGM),附完整代码和避坑指南

实战:用Python的scipy和numpy搞定分数阶灰色模型(FGM),附完整代码和避坑指南 灰色预测模型在数据分析领域一直占有一席之地,特别是当面对小样本、贫信息的数据预测问题时。传统灰色模型通过一阶累加生成指数规律明显的…...

边缘计算实战:基于 Linux Netns 与标准海事网关抵御局域网横向攻击的物理隔离架构

摘要:扁平化局域网极易遭受 ARP 欺骗与黑客横向攻击。本文记录了在标准工业级海事网关上基于 Linux netns 构建网络物理与逻辑隔离防线的实操复盘。 导语:在实操一个远洋船载网络的安全重构项目时,我们面临一个极其严峻的威胁模型&#xff1…...

逆向工程师的视角:如何用Windbg双机调试分析一个未知Windows驱动(实战案例解析)

逆向工程师的视角:如何用Windbg双机调试分析未知Windows驱动 在安全研究和恶意代码分析领域,逆向工程师常常需要面对未知的Windows驱动程序。这些驱动可能是第三方闭源组件,也可能是潜在的恶意软件载体。与传统的驱动开发调试不同&#xff0c…...

别再乱接电阻了!手把手教你为DDR4/DDR5内存信号选对端接方案(附仿真对比)

别再乱接电阻了!手把手教你为DDR4/DDR5内存信号选对端接方案(附仿真对比) 第一次调试DDR5内存接口时,我盯着示波器上扭曲的信号波形整整三天没合眼。当我把串联端接电阻从22Ω换成39Ω的瞬间,眼图突然像被施了魔法一样…...

Excel+ChatGPT函数实战:零代码实现语义理解与智能数据处理

1. 为什么说“在Excel里直接调用ChatGPT”不是噱头,而是真正在改写数据处理的工作流 你有没有过这样的时刻:盯着Excel表格里一列杂乱的客户反馈,想快速标出哪些是投诉、哪些是表扬,却卡在手动翻查、复制粘贴、反复试错公式上&…...

开源工具phantom-secrets:轻量级秘密管理方案,助力安全开发与CI/CD

1. 项目概述:一个用于秘密管理的开源工具 最近在整理自己的开发环境时,发现各种API密钥、数据库密码、配置文件里的敏感信息散落在各个角落,管理起来非常头疼。用文本文件记不安全,用密码管理器又觉得和开发流程有点脱节。直到我发…...

Wechatsync(文章同步助手)自动发布神器

下载地址:https://www.chajianxw.com/product-tool/16773.html 安装教程:https://www.chajianxw.com/tutorial/how-to-install-chrome-plugin.html AI-Skills 技能包一键调用:https://ai-skills.ai/?inviteCode=S2JV3NCK 目录 一、引言 二、系统整体架构设计 核心技术栈…...

AI Agent(智能体)的输出格式应该从 Markdown 转向 HTML吗?

在近期(2026年5月)的技术圈和AI社区引发了非常热烈的讨论。提出这个观点的是 Anthropic(Claude背后的公司)负责 Claude Code 团队的工程师 Thariq Shihipar,他最近发表了一篇题为《使用 Claude Code:HTML 极…...

JSON数据同步利器:深度解析ogre-software/json-synchronizer的核心原理与应用

1. 项目概述:一个被低估的JSON数据同步利器如果你经常和JSON数据打交道,尤其是在前后端分离、微服务架构或者多数据源集成的场景下,你肯定遇到过这样的烦恼:手头有两份甚至多份JSON数据,它们结构相似,但内容…...

紫光同创Logos系列FPGA实战:BGA封装PCB布局与Fanout布线避坑指南(附示意图)

紫光同创Logos系列FPGA实战:BGA封装PCB布局与Fanout布线避坑指南 第一次拿到紫光同创Logos系列FPGA的BGA封装芯片时,那种密密麻麻的焊盘阵列确实会让人头皮发麻。特别是FBG256和FBG484这类高密度封装,如何在有限的空间内完成高质量的Fanout布…...

Win11 22H2 打不开 IE?亲测有效!一行代码直接调出独立 IE 窗口

很多升级到 Windows 11 22H2 的用户都遇到过这样的困扰:明明银行、政务、企业内网等旧系统明确要求用 IE 浏览器登录,可系统里找不到 IE 入口,Edge 的 IE 兼容模式又频繁失效,直接打开 IE 还会强制跳转到 Edge,折腾半天…...

FPGA新手避坑指南:用SPWM驱动电机时,你的死区时间加对了吗?

FPGA电机驱动实战:SPWM死区时间设计的核心要点与避坑策略 在数字电源和电机控制领域,FPGA因其并行处理能力和精确时序控制而备受青睐。许多工程师在成功实现SPWM信号生成后,往往忽略了驱动电路中最致命的一环——死区时间设置。我曾亲眼见证过…...

nlux框架:快速构建可定制AI对话界面的JavaScript解决方案

1. 项目概述:一个面向未来的对话式AI集成框架如果你最近在关注AI应用开发,尤其是想在自己的产品里快速集成一个类似ChatGPT那样的智能对话界面,那你很可能已经听说过或者搜索过“nlux”或“nlkitai/nlux”这个项目。简单来说,nlux…...

5分钟免费解锁iPhone激活锁:applera1n终极使用指南

5分钟免费解锁iPhone激活锁:applera1n终极使用指南 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 你是否刚入手了一部二手iPhone,却发现自己被困在激活锁界面无法前进&#xf…...

拆解LCD12864串行时序:用STM32的GPIO模拟,一步步带你读懂那张时序图

深入解析LCD12864串行通信:用STM32 GPIO模拟时序的实战指南 在嵌入式开发中,液晶显示模块(LCD)是常见的人机交互界面,而LCD12864因其价格适中、显示内容丰富等特点被广泛使用。不同于简单的复制粘贴代码,真正理解其底层通信协议才…...

Xilinx MIG核读写DDR3时,这个时序细节没处理好,数据就全乱了(附Vivado 2020.1调试实录)

Xilinx MIG核DDR3读写时序陷阱:命令与数据通道异步处理实战解析 当你在Vivado中完成MIG核配置,看着DDR3初始化校准成功的指示灯亮起时,可能不会想到真正的挑战才刚刚开始。我曾在多个高速数据采集项目中,反复栽在同一个坑里——命…...

Claude规则引擎:结构化提示词管理与Prompt Engineering实战

1. 项目概述:一个规则引擎的诞生与价值 最近在社区里看到不少朋友在讨论如何更好地管理和复用与Claude这类大型语言模型交互时的提示词(Prompt)和规则集。我自己在长期使用过程中也深有体会:每次开启一个新对话,要么得…...

【技术解析】方差分析:从统计表解读到业务决策的实战指南

1. 方差分析:从统计表到业务决策的实战指南 第一次接触方差分析时,我也被那些统计术语和公式搞得晕头转向。直到有一次,产品经理拿着A/B测试数据问我:"新版页面真的比旧版好吗?好多少?"我才意识到…...

用LangChain Tools打造会自主查资料的GPT模型

1. 项目概述:为什么你需要一个“会自己查资料”的GPT模型?我第一次在ChatGPT里输入“2024年巴黎奥运会新增了哪些比赛项目?”时,得到的回复是:“我的训练数据截止于2021年9月,无法提供2024年的最新信息。”…...

DIY红外热像仪进阶:手把手教你用C语言实现7种伪彩色编码(附完整代码)

DIY红外热像仪进阶:手把手教你用C语言实现7种伪彩色编码(附完整代码) 当32x24的温度矩阵在屏幕上呈现为单调的灰度图像时,你是否想过如何让它焕发生机?伪彩色编码技术正是打开这扇门的钥匙。本文将带你深入探索七种经…...

FPGA调试实录:我的SPI Master模块为什么读不到数据?常见问题排查指南

FPGA调试实录:SPI Master模块数据读取失败的深度排查指南 当你的SPI Master模块在调试过程中突然"罢工",示波器上的波形看似正常却始终无法读取数据时,那种挫败感每个硬件工程师都深有体会。本文将从实战角度出发,分享一…...

哪个降AI软件好?2026年4款主流降AI工具按场景对位横评!

哪个降AI软件好?2026年4款主流降AI工具按场景对位横评! 「哪个降 AI 软件好」没有标准答案。学生最常踩的坑是把这个问题简化成「哪款最便宜」或者「哪款最有效」——其实好不好用看你的场景。学校送知网严标准、送维普重灾区、自媒体被判 AI、本科双重问…...

如何在无GPU群晖设备上开启完整AI相册功能:Synology Photos面部识别终极指南

如何在无GPU群晖设备上开启完整AI相册功能:Synology Photos面部识别终极指南 【免费下载链接】Synology_Photos_Face_Patch Synology Photos Facial Recognition Patch 项目地址: https://gitcode.com/gh_mirrors/sy/Synology_Photos_Face_Patch 还在为DS918…...

降AI提示词大全!10个prompt让AI输出人类味+嘎嘎降AI兜底!

降AI提示词大全!10个prompt让AI输出人类味嘎嘎降AI兜底! 用 ChatGPT、DeepSeek、Kimi、豆包写论文最大的痛是:写得快但被检测判 AI、改起来比自己写还累。其实在写作环节就能预防一部分 AI 痕迹,靠的是会写降 AI 提示词。 这篇先给…...