基于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基本原理是通过分析网络行为(访问方式、访问量、与历史访问…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
