聊天室程序(c 代码)


author: hjjdebug
date: 2026年 01月 23日 星期五 17:25:48 CST
descrp: 聊天室程序(c 代码)


文章目录

  • 1 client.c 程序
    • 1.1 client.c 源代码
  • 2 server.c 程序
    • 2.1 server.c 源代码
  • 3. select 函数原理
    • 3.1 select 优点:
    • 3.2 select 缺点:
    • 3.3 何时使用select 函数
  • 4. Makefile
  • 5. 执行效果

聊天室程序, 就是微信中的群聊功能.
微信相当于客户端程序, 而服务器程序是远程腾讯服务器,在默默的工作.
这里演示的是命令行程序,没有窗口界面. 但原理是一样的.
之所以写它,是因为server.c 和client.c 都是经过我调试的,是可用的程序.算一个小应用程序

1 client.c 程序

client.c实现了聊天室客户端,具有独立的接收线程和发送功能,支持实时消息显示
一个client, 只能与server 想连接.

1.1 client.c 源代码

$cat client.c#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<arpa/inet.h>#include<pthread.h>#include<sys/time.h>#defineBUFFER_SIZE1024#defineSERVER_IP"127.0.0.1"#definePORT8080typedefstruct{intsocket;intrunning;}client_context_t;//定义该结构,是为了主线程退出时,子线程要先退出.void*receive_handler(void*arg){client_context_t*ctx=(client_context_t*)arg;charbuffer[BUFFER_SIZE];while(ctx->running){memset(buffer,0,BUFFER_SIZE);intbytes_received=recv(ctx->socket,buffer,BUFFER_SIZE-1,0);if(bytes_received>0){buffer[bytes_received]=0;printf("%s\n",buffer);//printf, 不加\n 会被缓存.不立即显示到屏幕上,而是直到遇到\n才刷新}elseif(bytes_received==0){printf("服务器已断开连接\n");ctx->running=0;break;}else{if(ctx->running){perror("接收数据失败\n");}ctx->running=0;break;}}returnNULL;}intmain(){intsock=0;structsockaddr_inserv_addr;pthread_t recv_thread;client_context_t ctx;// 创建套接字if((sock=socket(AF_INET,SOCK_STREAM,0))<0){perror("套接字创建失败");return-1;}// 设置服务器地址serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(PORT);if(inet_pton(AF_INET,SERVER_IP,&serv_addr.sin_addr)<=0){perror("无效的服务器地址");return-1;}// 连接到服务器if(connect(sock,(structsockaddr*)&serv_addr,sizeof(serv_addr))<0){perror("连接服务器失败");return-1;}printf("已连接到聊天室服务器 %s:%d\n",SERVER_IP,PORT);// 初始化上下文并启动接收线程ctx.socket=sock;ctx.running=1;if(pthread_create(&recv_thread,NULL,receive_handler,&ctx)!=0){perror("创建接收线程失败");close(sock);return-1;}// 主循环发送消息charmessage[BUFFER_SIZE];//select 参数intres;// int stdin_fd=fileno(stdin); 可直接使用STDIN_FILENO 宏=0structtimevaltimeout;fd_set readfds;printf("输入消息发送到聊天室 ('quit' 退出):\n");while(ctx.running){// fgets(message, BUFFER_SIZE, stdin); // fgets 会阻塞,服务器先退出则客户端不能退出,所以改用selectFD_ZERO(&readfds);FD_SET(STDIN_FILENO,&readfds);//每次都要初始化,因为select会修改readfds,timeouttimeout.tv_sec=1;//1秒超时timeout.tv_usec=0;res=select(STDIN_FILENO+1,&readfds,NULL,NULL,&timeout);if(res>0&&FD_ISSET(STDIN_FILENO,&readfds)){fgets(message,BUFFER_SIZE,stdin);// 已查询过了,有数据,所以不会在这里阻塞message[strcspn(message,"\n")]=0;// 移除换行符if(strlen(message)==0)continue;printf("receive from stdin:%s\n",message);}elseif(res==0)//超时{// static int count=0;// printf("timeout: %d\n",count++);continue;}else//出现错误{printf("error ocurred! res:%d\n",res);break;}if(send(sock,message,strlen(message),0)<0){perror("发送消息失败");ctx.running=0;break;}if(strcmp(message,"quit")==0){printf("正在断开连接...\n");ctx.running=0;break;}}// 清理资源ctx.running=0;pthread_join(recv_thread,NULL);//等待子线程退出,完美闭环.close(sock);printf("客户端已退出\n");return0;}

2 server.c 程序

server.c实现了支持多客户端连接的聊天室服务器,使用线程处理每个客户端连接,并支持消息广播功能
服务器支持最多10个并发客户端连接,具备完整的连接管理和消息转发机制,很容易扩展为更多的客户端连接

2.1 server.c 源代码

$cat server.c#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<arpa/inet.h>#include<pthread.h>#definePORT8080#defineBUFFER_SIZE1024#defineMAX_CLIENTS10intclient_sockets[MAX_CLIENTS];intclient_count=0;pthread_mutex_t clients_mutex=PTHREAD_MUTEX_INITIALIZER;voidbroadcast_message(char*message,intsender_sock){pthread_mutex_lock(&clients_mutex);for(inti=0;i<client_count;i++){if(client_sockets[i]!=sender_sock){if(send(client_sockets[i],message,strlen(message),0)<0){perror("广播消息失败");}}}printf("server broad message:%s\n",message);pthread_mutex_unlock(&clients_mutex);}void*handle_client(void*sock_addr){intclient_sock=*(int*)sock_addr;charbuffer[BUFFER_SIZE];intbytes_read;// 发送欢迎消息char*welcome_msg="欢迎加入聊天室!\n";send(client_sock,welcome_msg,strlen(welcome_msg),0);while((bytes_read=read(client_sock,buffer,BUFFER_SIZE-1))>0){buffer[bytes_read]='\0';// 广播消息给其他客户端charbroadcast[BUFFER_SIZE+50];snprintf(broadcast,sizeof(broadcast),"客户端[%d]: %s",client_sock,buffer);broadcast_message(broadcast,client_sock);if(strncmp(buffer,"quit",4)==0){break;}}// 断开客户端连接pthread_mutex_lock(&clients_mutex);for(inti=0;i<client_count;i++){if(client_sockets[i]==client_sock){for(intj=i;j<client_count-1;j++){client_sockets[j]=client_sockets[j+1];}client_count--;break;}}pthread_mutex_unlock(&clients_mutex);close(client_sock);printf("客户端[%d]已断开连接\n",client_sock);returnNULL;}intmain(){intserver_fd,new_socket;structsockaddr_inaddress;intaddrlen=sizeof(address);// 创建服务端套接字if((server_fd=socket(AF_INET,SOCK_STREAM,0))==0){perror("创建套接字失败");exit(EXIT_FAILURE);}// 设置套接字选项intopt=1;if(setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))){perror("设置套接字选项失败");exit(EXIT_FAILURE);}// 配置服务器地址address.sin_family=AF_INET;address.sin_addr.s_addr=INADDR_ANY;address.sin_port=htons(PORT);// 绑定套接字if(bind(server_fd,(structsockaddr*)&address,sizeof(address))<0){perror("绑定失败");exit(EXIT_FAILURE);}// 开始监听if(listen(server_fd,3)<0){perror("监听失败");exit(EXIT_FAILURE);}printf("聊天室服务器启动,监听端口 %d\n",PORT);while(1){// 接受新连接if((new_socket=accept(server_fd,(structsockaddr*)&address,(socklen_t*)&addrlen))<0){perror("接受连接失败");continue;}printf("新客户端连接: %s:%d\n",inet_ntoa(address.sin_addr),ntohs(address.sin_port));// 添加到客户端列表pthread_mutex_lock(&clients_mutex);if(client_count<MAX_CLIENTS){client_sockets[client_count++]=new_socket;}else{printf("客户端数量已达上限\n");close(new_socket);pthread_mutex_unlock(&clients_mutex);continue;}pthread_mutex_unlock(&clients_mutex);// 为客户端创建处理线程, 也可以改为多路I/O复用模型,select 模型pthread_t thread_id;if(pthread_create(&thread_id,NULL,handle_client,&new_socket)!=0){perror("创建线程失败");close(new_socket);}// 分离线程以便自动回收资源pthread_detach(thread_id);}// 清理资源close(server_fd);pthread_mutex_destroy(&clients_mutex);return0;}

server 程序,属于多路I/O ,如果不想用多线程, 而想用select 模型, 也容易实现,算了,不给代码了,说说原理吧.
如果要代码可以参考链接中的代码:

select 函数详解

3. select 函数原理

想用单线程来实现多路IO读写, 于是就有了把多个读描述符合并到一个读bitmap图,
同理还要构建一个写bitmap图, 一个异常bitmap图, 构建了这三个描述符集.这是调用select的前提条件.
调用select, 当前线程就会被阻塞, 当再醒来的时候, 那是因为三个描述符中一定有那么一个fd是激活的.
当然也可能是超时引起的,如果你设置了超时条件.这里先不考虑这种情况.
然后你去从三个描述符集中去查找激活的fd, 然后从该fd中去读取或写入数据.
实际的操作是比描述要简单的, 因为它预先定义了一些宏帮你简化一些操作. 不过原理就是这样了.

3.1 select 优点:

用一个线程监测多个I/O, 没有启用多线程

3.2 select 缺点:

  1. bitmap图的大小一般是1024个, 所以fd也就要求<1024了, 个数受到了限制.
  2. 醒来后需要查询哪个fd是活跃的,查找方法是枚举,一个一个去测试,询问,效率不高.

针对select 缺点, linux 又提供了高效的epoll接口, 它不需要逐个查询注册的fd, 而是把激活的fd直接构成一个池子,
类似于事件驱动方法, 这样由激活的fd直接找到对应的执行方法. 省略了查找激活fd的过程, 提高了执行效率.

3.3 何时使用select 函数

到底用不用多线程要根据实际环境来决定, 如果连接数不多或者被监测的fd不多,才几个或十几个, 那就用多线程好了.
如果fd 个数 几十个上百个, 那就用select 函数, 因为多线程快承不起了. 几个fd能不能用select呢, 能.
如果fd 个数 成千上万, 那只能用epoll. 因为select 最多支持1024个

4. Makefile

$cat Makefile CC=gcc CFLAGS=-g-Wall-Wextra-std=c99-pthread CLIENT_SRC=client.c SERVER_SRC=server.c CLIENT_TARGET=client SERVER_TARGET=server all:$(CLIENT_TARGET)$(SERVER_TARGET)$(CLIENT_TARGET):$(CLIENT_SRC)$(CC)$(CFLAGS)-o $@ $<$(SERVER_TARGET):$(SERVER_SRC)$(CC)$(CFLAGS)-o $@ $<clean:rm-f $(CLIENT_TARGET)$(SERVER_TARGET).PHONY:all clean

5. 执行效果

下面演示一下2个客户端登录, 服务器和客户端聊天的信息
$ ./server
聊天室服务器启动,监听端口 8080
新客户端连接: 127.0.0.1:54464
server broad message:客户端[4]: 你好,我是客户1
新客户端连接: 127.0.0.1:60958
server broad message:客户端[5]: hello, every body
server broad message:客户端[4]: 欢迎你,客户端5

$ ./client // 这是客户端4
已连接到聊天室服务器 127.0.0.1:8080
输入消息发送到聊天室 (‘quit’ 退出):
欢迎加入聊天室! //欢迎信息

你好,我是客户1 // 客户端4 发出的信息
receive from stdin:你好,我是客户1
客户端[5]: hello, every body // 客户端5 发来的信息
欢迎你,客户端5 // 客户端4 发出的信息
receive from stdin:欢迎你,客户端5

$ ./client // 这是客户端5
已连接到聊天室服务器 127.0.0.1:8080
输入消息发送到聊天室 (‘quit’ 退出):
欢迎加入聊天室! //欢迎信息

hello, every body //客户端5 发出的信息
receive from stdin:hello, every body
客户端[4]: 欢迎你,客户端5 //客户端4 发来的信息

运行良好!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1215229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

select 函数详解

author: hjjdebug date: 2026年 01月 25日 星期日 15:08:37 CST descrip: select 函数详解. 文章目录0: I/O 多路复用是什么意思 ??1. select 函数可以同时支持多少路I/O ?1.1. server_fd 是一个整数1.2 read_fds 是什么?1.3 read_fds 赋值.2. 使用select 的注意事项.2.1 t…

2026年市场头部的大牌保健食品供应链口碑推荐,保健食品集合店/保健食品/大牌热销品,大牌保健食品加盟代理口碑排行

行业趋势:政策红利与技术驱动下的供应链升级 随着粤港澳大湾区与海南自贸港政策的深度落地,大牌保健食品供应链正经历结构性变革。政策赋能下,企业通过整合保税仓储、多式联运及跨境通关优势,构建起“全球直采+区域…

聚焦2026:深度解析防火涂料国标要求与工程选型平衡技巧排行,饰面型防火涂料/电缆防火涂料,防火涂料企业口碑排行

近年来,随着建筑行业对消防安全的重视程度持续升级,防火涂料作为关键被动防火材料,其市场需求呈现爆发式增长。然而,面对国标GB14907-2018《钢结构防火涂料》、GB28375-2012《饰面型防火涂料》等强制性标准对耐火极…

风险评估准备(上)

一、网络安全风险评估 前期准备全指南 网络安全风险评估的前期准备是评估工作落地的核心前提&#xff0c;直接决定评估过程的效率、评估结果的精准度和可落地性&#xff0c;核心目标是明确评估边界、统一评估标准、整合资源与信息、规避评估实施中的各类障碍&#xff0c;为后续…

Deepin25用户安装教程

深度系统25安装教程准备文件: deepin-desktop-community-25.0.1-amd64.iso ventoy-1.1.10-windows.zip系统启动盘制作 网盘下载: https://pan.quark.cn/s/a804ae8dd78f 准备8G以上U盘,会清空u盘数据,尽量备份数据 …

Cursor 2.4 重磅发布:Subagents 多智能体协作与 Skills 技能系统上线

随着 AI 在代码库中处理的任务日益复杂和持久&#xff0c;Cursor 团队正式发布了 2.4 版本。 此次更新带来了全新的 Agent 架构改进&#xff0c;重点引入了 Subagents&#xff08;子智能体&#xff09;、Skills&#xff08;技能&#xff09; 以及 图像生成 功能&#xff0c;旨…

Java后端开发者的AGI时代学习与职业路径策略

资深Java后端工程师在通用人工智能(AGI)兴起的时代,既面临挑战也拥有机遇。本报告将基于2026年达沃斯论坛上Anthropic CEO达里奥阿莫代(Dario Amodei)和Google DeepMind CEO德米斯哈萨比斯(Demis Hassabis)对AG…

JavaWeb企业级开发---用户登录认证

记录在听黑马课的时候的笔记以及课堂上练习的代码&#xff0c;文章图源于我在听课的时候所截的屏&#xff0c;所以有些不清晰&#xff0c;请见谅。下面是课程链接&#xff0c;可点击自行跳转。 【黑马程序员JavaWeb开发教程&#xff0c;实现javaweb企业开发全流程&#xff08;…

能否用自然语言控制音色?CosyVoice2-0.5B指令调优实战指南

能否用自然语言控制音色&#xff1f;CosyVoice2-0.5B指令调优实战指南 1. 为什么“用四川话说”真的能生效&#xff1f; 你有没有试过&#xff0c;在语音合成工具里输入一句“今天真热啊”&#xff0c;然后加个括号备注“请用东北口音”——结果系统完全无视&#xff1f;或者…

最值得推荐的5家跨境营销服务商

一、趋势与价值随着跨境电商竞争加剧&#xff0c;企业不再满足于依赖第三方平台获取流量&#xff0c;而是希望通过自建独立站、搭建营销中台来掌握自主流量和数据资产。独立站市场正在迅速增长&#xff0c;预计到2025年中国独立站规模将达5.5万亿元并占跨境电商B2C市场的41%–5…

死了么打卡一键报平安H5抖音快手微信小程序看广告流量主开源

"死了么"项目功能介绍 一、项目定位 "死了么"是一款专注于个人安全状态监控与紧急通知的H5应用&#xff0c;通过简单的"一键打卡"操作&#xff0c;让用户向家人传递平安信息。当用户超过设定阈值未打卡时&#xff0c;系统会自动向紧急联系人发送…

2026年缠膜机工厂精选:这些品牌值得一试!缠绕机/穿箭打包机/缠绕打包机/全自动打包机/自动打包机,缠膜机产品排名

随着工业4.0浪潮的推进,包装环节的智能化升级成为制造业降本增效的关键。缠膜机作为物流包装的核心设备,其技术迭代速度与场景适配能力直接影响企业的供应链效率。当前市场呈现两极分化:头部企业通过模块化设计、AI…

揭秘2025年办公隔断市场:口碑与实力兼具的厂家排行,百叶隔断/办公室隔断墙/雾化玻璃隔断/电动门/自由组合隔断办公隔断设计推荐排行

随着企业对办公空间灵活性、功能性与美学要求的不断提升,办公隔断市场正经历着一轮深刻的变革。从传统的固定隔墙到如今集隔音、防火、智能调光于一体的模块化系统,市场对供应商的综合服务能力提出了更高要求。本文基…

2026年主流安检门品牌盘点与选购建议,安检设备/安检仪/安检机/智能安检/金属探测门/安检门,安检门源头厂家怎么找

随着公共安全需求持续升级,安检门作为关键安防设备,已从机场、车站等传统场景延伸至医院、学校、工厂等多元化领域。据行业统计,2025年国内安检门市场规模突破45亿元,年复合增长率达12%,但市场集中度不足30%,品牌…

一次可连续走k步的bfs的处理方法

做在二维地图上移动的模拟题时,绝大多数情况都需要使用 \(bfs\),其中 \(99\%\) 的情况都是只走一步(也就是上下左右四个方向选一个,并移动一格)。那么如果每一次可以连续走 \(k\) 步,我们应当如何处理呢? M - N…

SCI论文,能引用中文参考文献吗?

很多写SCI论文的同学经常会遇到一个问题&#xff1a;自己写的英文论文&#xff0c;不仅涉及到外文文献&#xff0c;同时也涉及到中文论文&#xff0c;是否可以引用中文参考文献呢&#xff1f; 答案是可以的。 但是在引用过程中也要注意以下几点: 第一、确认投递期刊是否可以…

Spring 6.0基于JDB手写定制自己的ROM框架

ORM 用面向对象的方式操作关系型数据库 开发者操作的是 对象&#xff08;Object&#xff09; ORM 框架负责把对象 自动映射 为&#xff1a; SQL 表&#xff08;Table&#xff09; 行&#xff08;Row&#xff09; 列&#xff08;Column&#xff09; 目标&#xff1a; 减…

一个英语听力的神器——获取transcripts

一个英语听力的神器——获取transcripts

基于SpringBoot完成的垃圾分类管理系统

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2026年国内评价高的调节阀厂家哪家强,半球阀/截止阀/闸阀/不锈钢阀门/电动盲板阀/消声止回阀,调节阀生产厂家排行榜

行业背景:调节阀市场进入技术驱动与场景深耕阶段 随着工业自动化进程加速,调节阀作为流程工业的核心控制设备,其性能直接影响生产效率与安全性。当前市场呈现两大趋势:一是智能化需求激增,电动/气动调节阀需集成远…