聊天室程序(c 代码)
author: hjjdebugdate: 2026年 01月 23日 星期五 17:25:48 CSTdescrp: 聊天室程序(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. Makefile5. 执行效果聊天室程序, 就是微信中的群聊功能.微信相当于客户端程序, 而服务器程序是远程腾讯服务器,在默默的工作.这里演示的是命令行程序,没有窗口界面. 但原理是一样的.之所以写它,是因为server.c 和client.c 都是经过我调试的,是可用的程序.算一个小应用程序1 client.c 程序client.c实现了聊天室客户端具有独立的接收线程和发送功能支持实时消息显示一个client, 只能与server 想连接.1.1 client.c 源代码$cat client.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includearpa/inet.h#includepthread.h#includesys/time.h#defineBUFFER_SIZE1024#defineSERVER_IP127.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_receivedrecv(ctx-socket,buffer,BUFFER_SIZE-1,0);if(bytes_received0){buffer[bytes_received]0;printf(%s\n,buffer);//printf, 不加\n 会被缓存.不立即显示到屏幕上,而是直到遇到\n才刷新}elseif(bytes_received0){printf(服务器已断开连接\n);ctx-running0;break;}else{if(ctx-running){perror(接收数据失败\n);}ctx-running0;break;}}returnNULL;}intmain(){intsock0;structsockaddr_inserv_addr;pthread_t recv_thread;client_context_t ctx;// 创建套接字if((socksocket(AF_INET,SOCK_STREAM,0))0){perror(套接字创建失败);return-1;}// 设置服务器地址serv_addr.sin_familyAF_INET;serv_addr.sin_porthtons(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.socketsock;ctx.running1;if(pthread_create(recv_thread,NULL,receive_handler,ctx)!0){perror(创建接收线程失败);close(sock);return-1;}// 主循环发送消息charmessage[BUFFER_SIZE];//select 参数intres;// int stdin_fdfileno(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_sec1;//1秒超时timeout.tv_usec0;resselect(STDIN_FILENO1,readfds,NULL,NULL,timeout);if(res0FD_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(res0)//超时{// static int count0;// 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.running0;break;}if(strcmp(message,quit)0){printf(正在断开连接...\n);ctx.running0;break;}}// 清理资源ctx.running0;pthread_join(recv_thread,NULL);//等待子线程退出,完美闭环.close(sock);printf(客户端已退出\n);return0;}2 server.c 程序server.c实现了支持多客户端连接的聊天室服务器使用线程处理每个客户端连接并支持消息广播功能服务器支持最多10个并发客户端连接具备完整的连接管理和消息转发机制,很容易扩展为更多的客户端连接2.1 server.c 源代码$cat server.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includearpa/inet.h#includepthread.h#definePORT8080#defineBUFFER_SIZE1024#defineMAX_CLIENTS10intclient_sockets[MAX_CLIENTS];intclient_count0;pthread_mutex_t clients_mutexPTHREAD_MUTEX_INITIALIZER;voidbroadcast_message(char*message,intsender_sock){pthread_mutex_lock(clients_mutex);for(inti0;iclient_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_readread(client_sock,buffer,BUFFER_SIZE-1))0){buffer[bytes_read]\0;// 广播消息给其他客户端charbroadcast[BUFFER_SIZE50];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(inti0;iclient_count;i){if(client_sockets[i]client_sock){for(intji;jclient_count-1;j){client_sockets[j]client_sockets[j1];}client_count--;break;}}pthread_mutex_unlock(clients_mutex);close(client_sock);printf(客户端[%d]已断开连接\n,client_sock);returnNULL;}intmain(){intserver_fd,new_socket;structsockaddr_inaddress;intaddrlensizeof(address);// 创建服务端套接字if((server_fdsocket(AF_INET,SOCK_STREAM,0))0){perror(创建套接字失败);exit(EXIT_FAILURE);}// 设置套接字选项intopt1;if(setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt))){perror(设置套接字选项失败);exit(EXIT_FAILURE);}// 配置服务器地址address.sin_familyAF_INET;address.sin_addr.s_addrINADDR_ANY;address.sin_porthtons(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_socketaccept(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_countMAX_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 缺点:bitmap图的大小一般是1024个, 所以fd也就要求1024了, 个数受到了限制.醒来后需要查询哪个fd是活跃的,查找方法是枚举,一个一个去测试,询问,效率不高.针对select 缺点, linux 又提供了高效的epoll接口, 它不需要逐个查询注册的fd, 而是把激活的fd直接构成一个池子,类似于事件驱动方法, 这样由激活的fd直接找到对应的执行方法. 省略了查找激活fd的过程, 提高了执行效率.3.3 何时使用select 函数到底用不用多线程要根据实际环境来决定, 如果连接数不多或者被监测的fd不多,才几个或十几个, 那就用多线程好了.如果fd 个数 几十个上百个, 那就用select 函数, 因为多线程快承不起了. 几个fd能不能用select呢, 能.如果fd 个数 成千上万, 那只能用epoll. 因为select 最多支持1024个4. Makefile$cat Makefile CCgcc CFLAGS-g-Wall-Wextra-stdc99-pthread CLIENT_SRCclient.c SERVER_SRCserver.c CLIENT_TARGETclient SERVER_TARGETserver 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 clean5. 执行效果下面演示一下2个客户端登录, 服务器和客户端聊天的信息$ ./server聊天室服务器启动监听端口 8080新客户端连接: 127.0.0.1:54464server broad message:客户端[4]: 你好,我是客户1新客户端连接: 127.0.0.1:60958server broad message:客户端[5]: hello, every bodyserver 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 发来的信息运行良好!

相关新闻

select 函数详解

select 函数详解

2026/7/3 21:20:17 阅读更多 →
风险评估准备(上)

风险评估准备(上)

2026/7/3 21:20:25 阅读更多 →
JavaWeb企业级开发---用户登录认证

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

2026/7/3 21:20:26 阅读更多 →

最新新闻

Umi-OCR终极指南:免费离线文字识别软件的完整配置与优化教程

Umi-OCR终极指南:免费离线文字识别软件的完整配置与优化教程

Umi-OCR终极指南:免费离线文字识别软件的完整配置与优化教程 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多…

2026/7/4 22:12:22 阅读更多 →
postcss-write-svg:革命性CSS SVG编写工具,让图形开发效率提升10倍!

postcss-write-svg:革命性CSS SVG编写工具,让图形开发效率提升10倍!

postcss-write-svg:革命性CSS SVG编写工具,让图形开发效率提升10倍! 【免费下载链接】postcss-write-svg Write SVGs directly in CSS 项目地址: https://gitcode.com/gh_mirrors/po/postcss-write-svg 你是否厌倦了在CSS和SVG文件之间…

2026/7/4 22:12:21 阅读更多 →
3大架构优化策略:如何构建高可用AI网关服务

3大架构优化策略:如何构建高可用AI网关服务

3大架构优化策略:如何构建高可用AI网关服务 【免费下载链接】new-api A unified AI model hub for aggregation & distribution. It supports cross-converting various LLMs into OpenAI-compatible, Claude-compatible, or Gemini-compatible formats. A cent…

2026/7/4 22:12:21 阅读更多 →
Agent Skills技能发现机制:如何让AI助手智能匹配任务与技能

Agent Skills技能发现机制:如何让AI助手智能匹配任务与技能

Agent Skills技能发现机制:如何让AI助手智能匹配任务与技能 【免费下载链接】agentskills Specification and documentation for Agent Skills 项目地址: https://gitcode.com/GitHub_Trending/ag/agentskills Agent Skills是GitHub推荐项目精选(…

2026/7/4 22:10:20 阅读更多 →
RestFB实战教程:10个常见Facebook API操作示例

RestFB实战教程:10个常见Facebook API操作示例

RestFB实战教程:10个常见Facebook API操作示例 【免费下载链接】restfb RestFB is a simple and flexible Facebook Graph API client written in Java. 项目地址: https://gitcode.com/gh_mirrors/re/restfb 想要在Java应用中快速集成Facebook功能&#xff…

2026/7/4 22:10:20 阅读更多 →
如何搭建Leela Chess Zero环境?5分钟快速启动你的AI象棋之旅

如何搭建Leela Chess Zero环境?5分钟快速启动你的AI象棋之旅

如何搭建Leela Chess Zero环境?5分钟快速启动你的AI象棋之旅 【免费下载链接】leela-chess **MOVED TO https://github.com/LeelaChessZero/leela-chess ** A chess adaption of GCPs Leela Zero 项目地址: https://gitcode.com/gh_mirrors/le/leela-chess L…

2026/7/4 22:08:18 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻