基于UDP的网络聊天室
一.项目需求:
- 如果有用户登录,其他用户可以收到这个人的登录信息
- 如果有人发送信息,其他用户可以收到这个人的群聊信息
- 如果有人下线,其他用户可以收到这个人的下线信息
- 服务器可以发送系统信息
二.服务器端
#include <myhead.h> //服务器
#define SER_IP "192.168.125.48"
#define SER_PORT 8888
#define MAX_CLIENTS 100
#define BUFFER_SIZE 1024typedef struct Node {struct sockaddr_in addr;char username[20];struct Node *next;
} Node, *List;typedef struct Msg {char type; // 'L' 登陆模式, 'M' 消息模式, 'Q' 下线模式, 'S' 系统消息char username[20];char message[BUFFER_SIZE];
} Message;List client_list = NULL;// 添加客户端
void add_client(struct sockaddr_in *addr, const char *username) {Node *node = malloc(sizeof(Node));memcpy(&(node->addr), addr, sizeof(struct sockaddr_in));strncpy(node->username, username, 19);node->next = client_list;client_list = node;
}// 移除客户端
void remove_client(Node *node) {Node **prev = &client_list;while (*prev && *prev != node) {prev = &(*prev)->next;}if (*prev) {Node *temp = *prev;*prev = node->next;free(temp);}
}// 广播消息
void broadcast_message(int sfd, const Message *msg,struct sockaddr_in *except_addr) {Node *current = client_list;Message to_send = *msg;to_send.message[sizeof(to_send.message) - 1] ='\0'; // 确保消息字符串以null终止while (current) {if (memcmp(&(current->addr), except_addr, sizeof(struct sockaddr_in)) !=0) {sendto(sfd, (const char *)&to_send, sizeof(Message), 0,(struct sockaddr *)&(current->addr),sizeof(struct sockaddr_in));}current = current->next;}
}// 查找客户端
Node *find_client_by_addr(struct sockaddr_in *addr) {Node *current = client_list;while (current) {if (memcmp(&(current->addr), addr, sizeof(struct sockaddr_in)) == 0) {return current;}current = current->next;}return NULL;
}int main() {int sfd = socket(AF_INET, SOCK_DGRAM, 0);if (sfd < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}int reuse = 1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ==-1) {perror("setsockopt error");return -1;}struct sockaddr_in server_addr, client_addr;server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(SER_IP);server_addr.sin_port = htons(SER_PORT);if (bind(sfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) <0) {perror("bind failed");exit(EXIT_FAILURE);}printf("************服务器**************\n");printf("**********等待客户端**************\n");char buffer[BUFFER_SIZE] = {0};struct timeval tv;fd_set readfds;int max_fd = sfd;tv.tv_sec = 5;tv.tv_usec = 0;while (1) {FD_ZERO(&readfds);FD_SET(sfd, &readfds);// 检查是否有新的客户端连接或消息int res = select(max_fd + 1, &readfds, NULL, NULL, &tv);if (res < 0) {perror("select error");break;} else if (res > 0) {if (FD_ISSET(sfd, &readfds)) {socklen_t client_len = sizeof(client_addr);int n = recvfrom(sfd, buffer, BUFFER_SIZE, 0,(struct sockaddr *)&client_addr, &client_len);if (n < 0) {perror("recvfrom error");continue;}buffer[n] = '\0'; // 确保字符串以null终止Message msg;memcpy(&msg, buffer,(n < sizeof(Message))? n: sizeof(Message)); // 防止缓冲区溢出switch (msg.type) {case 'L': // 客户端登录add_client(&client_addr, msg.username);printf("%s 已登录\n", msg.username);// 广播登录信息给所有客户端(除了发送者)Message login_msg;strncpy(login_msg.username, msg.username, 19);login_msg.type = 'L';login_msg.message[0] = '\0'; // 清空消息字段,因为是登录消息broadcast_message(sfd, &login_msg, &client_addr);break;case 'M': // 客户端发送消息printf("%s: %s\n", msg.username, msg.message);// 广播消息给所有客户端(除了发送者)broadcast_message(sfd, &msg, &client_addr);break;case 'Q': // 客户端退出{Node *node = find_client_by_addr(&client_addr);if (node) {printf("%s 已下线\n", node->username);Message quit_msg;strncpy(quit_msg.username, node->username, 19);quit_msg.type = 'Q';quit_msg.message[0] ='\0'; // 清空消息字段,因为是下线消息remove_client(node);broadcast_message(sfd, &quit_msg, &client_addr);}} break;case 'S': // 服务器系统消息printf("系统消息: %s\n", msg.message);// 广播系统消息给所有客户端broadcast_message(sfd, &msg, NULL);break;default:printf("未知的消息类型\n");break;}}}}close(sfd);return 0;
}
三.客户端
#include <myhead.h>
#define SER_IP "192.168.125.48"
#define SER_PORT 8888
#define BUFFER_SIZE 1024typedef struct Msg {char type; // 'L' 登陆模式, 'M' 消息模式, 'Q' 下线模式, 'S' 系统消息char username[20];char message[BUFFER_SIZE];
} Message;int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}struct sockaddr_in sin;memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_addr.s_addr = inet_addr(SER_IP);sin.sin_port = htons(SER_PORT);char username[20];printf("输入您的名字: ");scanf("%19s", username); // 限制输入长度为19个字符,为null终止符留空间// 发送登录消息Message login_msg;login_msg.type = 'L';strncpy(login_msg.username, username, sizeof(login_msg.username) - 1);login_msg.message[0] = '\0'; // 清空消息字段,因为是登录消息sendto(sockfd, &login_msg, sizeof(login_msg), 0, (struct sockaddr *)&sin,sizeof(sin));char message[BUFFER_SIZE];char recv_buffer[BUFFER_SIZE];struct sockaddr_in from_addr;socklen_t len = sizeof(from_addr);while (1) {printf("输入消息 (or 'exit' to quit): ");if (scanf("%1023s", message) == EOF || strcmp(message, "exit") == 0) {// 发送退出消息Message quit_msg;quit_msg.type = 'Q';strncpy(quit_msg.username, username, sizeof(quit_msg.username) - 1);quit_msg.message[0] = '\0'; // 清空消息字段,因为是退出消息sendto(sockfd, &quit_msg, sizeof(quit_msg), 0,(struct sockaddr *)&sin, sizeof(sin));break;}while (getchar() != '\n'); // 清除输入缓冲区中的换行符// 发送消息到服务器Message send_msg;send_msg.type = 'M';strncpy(send_msg.username, username, sizeof(send_msg.username) - 1);strncpy(send_msg.message, message, sizeof(send_msg.message) - 1);send_msg.message[sizeof(send_msg.message) - 1] ='\0'; // 确保消息字符串以null终止sendto(sockfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&sin,sizeof(sin));// 接收服务器的广播消息(可能是其他客户端的消息或系统消息)int n = recvfrom(sockfd, recv_buffer, BUFFER_SIZE, 0,(struct sockaddr *)&from_addr, &len);if (n > 0) {Message *msg = (Message *)recv_buffer;recv_buffer[n] = '\0'; // 确保字符串以null终止switch (msg->type) {case 'L': // 登录通知printf("%s 已登录\n", msg->username);break;case 'M': // 群聊消息printf("%s: %s\n", msg->username, msg->message);break;case 'Q': // 下线通知printf("%s 已下线\n", msg->username);break;case 'S': // 系统消息printf("系统消息: %s\n", msg->message);break;default:printf("未知的消息类型\n");break;}}}close(sockfd);return 0;
}
不完善仅供参考
相关文章:

基于UDP的网络聊天室
一.项目需求: 如果有用户登录,其他用户可以收到这个人的登录信息如果有人发送信息,其他用户可以收到这个人的群聊信息如果有人下线,其他用户可以收到这个人的下线信息服务器可以发送系统信息 二.服务器端 #include <myhead.h&…...
数组-两个升序数组中位数
一、题目描述 二、解题思路 (一).基本思想: 如果列表总长度allsize( arr1.size()arr2.size() ) 为奇数时,中位数位置应该在两个列表排序后的第 allsize/2 位置处,如果allsize为偶数,中位数应该取 (allsize/2)-1 和 allsize/2 的…...

每日一题《leetcode--116.填充每个结点的下一个右侧结点》
https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/ 题目要求给每个结点的next指针进行填充,使每个结点的next指针指向其下一个右侧结点。如果右侧没有结点,则将next指针设置为空。 struct Node* connect(struct Node* root) {…...
【MySQL精通之路】InnoDB(6)-磁盘结构(5)-Redolog
主博客: 【MySQL精通之路】InnoDB(6)-磁盘上的InnoDB结构-CSDN博客 上一篇: 【MySQL精通之路】InnoDB-双写缓冲区-CSDN博客 下一篇: 目录 1.配置Redo Log容量(MySQL 8.0.30或更高版本) 2.配置重做日志容量(MySQL…...
【探索自然语言处理:构建一个简单的文本分类器】
文章目录 前言文本预处理特征提取模型训练文本分类结论 前言 在信息时代,文本数据无处不在,从社交媒体帖子到客户反馈,文本是沟通和信息交流的主要媒介。自然语言处理(NLP)是人工智能的一个分支,它使计算机…...

概率论统计——大数定律
大数定律 弱大数定律(辛钦大数定律) 利用切比雪夫不等式,证明弱大数定律 应用 伯努利大数定理,(辛钦大数定理的推论) 证明伯努利大数定理 注意:这里将二项分布转化成0,1分布来表示,…...
vscode终端命令行前面出现两个conda环境名的问题决解方法
已经安装了conda,打开vscode的terminal时,命令行前面有两个虚拟环境名。 进入vscode的setting 找到Python->Python:Default Interpreter Path,把这个值复位,就可以解决。 如果不想前面带(base),可以运行 conda co…...

“AI黏土人”一夜爆火,图像生成类应用应该如何长期留住用户?
文章目录 最近大火的“AI黏土人”,一股浓浓的《小羊肖恩》风。 凭借这这种搞怪的风格,“AI黏土人”等图像生成类应用凭借其创新技术和市场需求迅速崛起并获得巨大关注。然而,要保持用户黏性并确保长期发展,这些应用需要采取一系列…...
【MySQL精通之路】SQL优化(1)-查询优化(12)-块嵌套循环和批处理Key访问联接
在MySQL中,可以使用批处理Key访问(BKA)联接算法,该算法使用对联接表的索引访问和联接缓冲区。 BKA算法支持内联接、外联接和半联接操作,包括嵌套的外部联接。 BKA的优点包括由于更高效的表扫描而提高了联接性能。 此…...

SQL使用函数给多个分表添加同一字段
数据库中分表时,往往需要向多个分表中添加同一个字段,可以定义一个函数,每次调用这个函数向多个份表中添加同意字段。 1、创建函数示例: 在PostgreSQL中创建一个简单的函数 以下是一个在PostgreSQL中创建函数的简单示例&#x…...

OpenAI 再次刷新认知边界:GPT-4 颠覆语音助手市场,流畅度直逼真人互动?
前言 近日,美国人工智能研究公司 OpenAI 发布了其最新旗舰模型 GPT-4o,这一革命性的进展不仅标志着人工智能领域的新突破,更预示着即将步入一个全新的交互时代?GPT-4o 的发布,对于我们来说,意味着人工智能…...

UE5 使用外置摄像头进行拍照并保存到本地
连接外置摄像头功能:https://docs.unrealengine.com/4.27/zh-CN/WorkingWithMedia/IntegratingMedia/MediaFramework/HowTo/UsingWebCams/ 核心功能:UE4 相机拍照功能(图片保存)_ue 移动端保存图片-CSDN博客 思路是: …...

【C++】从零开始map与set的封装
送给大家一句话: 今日的事情,尽心、尽意、尽力去做了,无论成绩如何,都应该高高兴兴地上床恬睡。 – 三毛 《亲爱的三毛》 🌃🌃🌃🌃🌃🌃🌃&#x…...
Python可以声明并赋值一个hash类型变量吗?
在Python中,不能直接声明一个变量为hash类型,因为Python是一种动态类型语言,不需要(也不能)在声明变量时指定其类型。变量的类型是根据赋给它的值自动推断的。 将一个哈希值(即一个整数)赋值给…...

苗情灾情监控系统—提高农业生产效率
TH-MQ2苗情灾情监控系统是一种用于监测农作物生长状况和灾情的设备,通过实时监测和数据分析,帮助农民及时了解作物生长情况,采取相应的管理措施,提高农业生产效率和降低生产成本。 该系统通常由多种传感器、摄像头、数据传输模块等…...
wpf自定义按钮样式
在WPF中,自定义按钮样式可以通过创建一个ControlTemplate来实现。以下是一个简单的自定义按钮样式的例子: 首先,在你的WPF项目资源字典中定义按钮的ControlTemplate。 <Window.Resources><ControlTemplate x:Key"CustomButto…...

Meme币总市值突破630亿美元 以太坊ETF获批意味着代币化资产“完全安全”
近日,数字货币市场再次掀起轩然大波。一方面,Meme币总市值突破了630亿美元,令人瞠目结舌;另一方面,以太坊ETF的获批也引发了市场的广泛关注,被视为代币化资产的“完全安全”标志。 Meme币总市值飙升 Meme币…...

MySQL数据库语法(二)
一、数据库的创建 创建数据库CRATE DATABASE语法:CREATE DATABASE [IF NOT EXISTS]数据库名;功能:用给定的名字创建一个数据库如果数据库已经存在,发生一个错误。查看创建数据库:SHOW CREATE DATABASE <数据库名>ÿ…...

Linux makefile
Linux makefile 用makefile去自动编译和删除静态库和动态库 在实际开发中,项目的源代码文件比较多,按类型、功能、模块分别存放在不同的目录和文件中,哪些文件需要先编译,那些文件后编译,那些文件需要重新编译…...
信息安全基础知识
信息安全基础知识 安全策略表达模型是一种对安全需求与安全策略的抽象概念表达,一般分为自主访问控制模型(HRU)和强制访问控制模型(BLP、Biba)IDS基本原理是通过分析网络行为(访问方式、访问量、与历史访问…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...