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

3.24作业

基于UDP的网络聊天室

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

服务器端代码

#include<myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.117.74"
#define PRINT_ERR(msg)                                      do                                                      {                                                       printf("%s,%d,%s\n", __FILE__, __LINE__, __func__); perror(msg);                                        exit(-1);                                           } while (0)typedef struct
{char code; //操作码char name[32];char txt[128];
} msg_t;
//链表结构体
typedef struct _NODE
{struct sockaddr_in c_addr;struct _NODE *next;
} node_t;void creat_link(node_t **head);
int do_register(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead);
int do_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead);
int quit_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead);int main(int argc, const char *argv[])
{//创建套接字int sockfd;if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){PRINT_ERR("socket error");}//创建服务器网络信息结构体struct sockaddr_in serviceaddr;memset(&serviceaddr, 0, sizeof(serviceaddr));serviceaddr.sin_family = AF_INET;serviceaddr.sin_addr.s_addr = inet_addr(argv[1]);serviceaddr.sin_port = htons(atoi(argv[2]));socklen_t serviceaddr_len = sizeof(serviceaddr);//将服务器网络信息结构体与套接字绑定if (bind(sockfd, (struct sockaddr *)&serviceaddr, serviceaddr_len) == -1){PRINT_ERR("bind error");}//创建客户端网络信息结构体struct sockaddr_in clientaddr;memset(&clientaddr, 0, sizeof(clientaddr));socklen_t clientaddr_len = sizeof(clientaddr);msg_t msg;//创建父子进程pid_t pid;pid = fork();if (pid == -1){PRINT_ERR("fork error");}else if (pid == 0){//子进程//接受数据并处理//定义链表头节点node_t *phead = NULL;creat_link(&phead);phead->next = NULL;while (1){memset(&msg, 0, sizeof(msg));memset(&clientaddr, 0, sizeof(clientaddr));if ((recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &clientaddr_len)) == -1){PRINT_ERR("recvfrom error");}printf("%8s : [%s]\n", msg.name, msg.txt);switch (msg.code){case 'L':do_register(sockfd, msg, clientaddr, phead);break;case 'C':do_group_chat(sockfd, msg, clientaddr, phead);break;case 'Q':quit_group_chat(sockfd, msg, clientaddr, phead);break;}}}else if (pid > 0){//父进程//发系统消息msg.code='C';strcpy(msg.name,"server");while(1){fgets(msg.txt,128,stdin);msg.txt[strlen(msg.txt)-1]='\0';if(sendto(sockfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&serviceaddr,serviceaddr_len)==-1){PRINT_ERR("sendto error");}}}close(sockfd);return 0;
}
//创建链表头节点函数
void creat_link(node_t **head)
{*head = (node_t *)malloc(sizeof(node_t));
}
//登录操作
int do_register(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{//遍历链表将登录信息发送给所以人node_t *p = phead;while (p->next != NULL){p = p->next;if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1){PRINT_ERR("recvfrom error");}}//将登录的客户端信息插入保存在链表//头插//定义一个新的指针保存客户端信息node_t *newp = NULL;creat_link(&newp);newp->c_addr = clientaddr;newp->next = phead->next;phead->next = newp;return 0;
}int do_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{//遍历链表,将消息发给除自己之外的所有人node_t *p = phead;while (p->next != NULL){p = p->next;//判断链表客户端信息是否是自己//是自己就不发送if (memcmp(&(p->c_addr), &clientaddr, sizeof(clientaddr))){if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1){PRINT_ERR("recvfrom error");}}}return 0;
}
//退出群聊操作
int quit_group_chat(int sockfd, msg_t msg, struct sockaddr_in clientaddr, node_t *phead)
{node_t *p = phead;while (p->next != NULL){//判断链表客户端信息是否是自己//是自己就不发送并且将自己的客户端信息在链表内删除if (memcmp(&(p->next->c_addr), &clientaddr, sizeof(clientaddr))){p = p->next;if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1){PRINT_ERR("recvfrom error");}}else{node_t *pnew;pnew = p->next;p->next = pnew->next;pnew->next = NULL;free(pnew);pnew = NULL;}}return 0;
}

客户端代码

#include<myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.117.74"
#define PRINT_ERR(msg)                                      do                                                      {                                                       printf("%s,%d,%s\n", __FILE__, __LINE__, __func__); perror(msg);                                        exit(-1);                                           } while (0)typedef struct
{char code; //操作码 char name[32];char txt[128];
} msg_t;int main(int argc, const char *argv[])
{//创建套接字int sockfd;if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){PRINT_ERR("socket error");}//创建服务器网络信息结构体struct sockaddr_in serviceaddr;memset(&serviceaddr, 0, sizeof(serviceaddr));serviceaddr.sin_family = AF_INET;serviceaddr.sin_addr.s_addr = inet_addr(argv[1]);serviceaddr.sin_port = htons(atoi(argv[2]));socklen_t serviceaddr_len = sizeof(serviceaddr);//给服务器发送登录数据包msg_t msg;memset(&msg, 0, sizeof(msg_t));msg.code = 'L';printf("请输入用户名:");fgets(msg.name, 32, stdin);msg.name[strlen(msg.name) - 1] = '\0';strcpy(msg.txt, "加入群聊");if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&serviceaddr, serviceaddr_len) == -1){PRINT_ERR("sendto error");}//创建父子进程pid_t pid;pid = fork();if (pid == -1){PRINT_ERR("fork error");}else if (pid == 0){//子进程//接受数据并处理while (1){//每次循环前将msg置零memset(&msg, 0, sizeof(msg));//接受服务器发过来的信息并打印到终端上if (recvfrom(sockfd, &msg, sizeof(msg_t), 0, NULL, NULL) == -1){PRINT_ERR("recvfrom error");}printf("%8s:[%s]\n", msg.name, msg.txt);}}else if (pid > 0){//父进程//发送消息while (1){   //memset会把name清除msg.code = 'C';fgets(msg.txt, 128, stdin);msg.txt[strlen(msg.txt) - 1] = '\0';if (strcmp(msg.txt, "quit") == 0){msg.code = 'Q';strcpy(msg.txt, "退出群聊");}if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&serviceaddr, serviceaddr_len) == -1){PRINT_ERR("sendto error");}if (strcmp(msg.txt, "退出群聊") == 0){break;}}kill(pid,SIGKILL);wait(NULL);close(sockfd);}return 0;
}

相关文章:

3.24作业

基于UDP的网络聊天室 项目需求&#xff1a; 如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息如果有人下线&#xff0c;其他用户可以收到这个人的下线信息服务器可以发送系统信息 服务器端代码 #in…...

Excel双击单元格后弹窗输入日期

Step1. 在VBE界面新建一个窗体(Userform1),在窗体的工具箱的空白处右键,选中添加附件,勾选Calendar control 8.0,即可完成日历的添加。 PS:遗憾的是, Office 64 位没有官方的日期选择器控件。唯一的解决方案是使用Excel 的第三方日历。 参考链接:How to insert calen…...

原生 HTML/CSS/JS 实现右键菜单和二级菜单

文章来源&#xff1a;www.huhailong.vip 站点 文章源地址&#xff1a;https://www.huhailong.vip/article/1764653112011841538 Demo效果演示地址 先看效果图 {{{width“auto” height“auto”}}} 需要注意的就是边界检测处理&#xff0c;到极端点击底部和右侧时如果不做处理会…...

[项目前置]如何用webbench进行压力测试

测试软件 采用webbench进行服务器性能测试。 Webbench是知名的网站压力测试工具&#xff0c;它是由Lionbridge公司开发。 webbench的标准测试可以向我们展示服务器的两项内容&#xff1a; 每秒钟相应请求数 和 每秒钟传输数据量 webbench测试原理是&#xff0c;创建指定数…...

网络七层模型:理解网络通信的架构(〇)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

format(C++20)

1. std::format format_01.cpp // g format_01.cpp -stdc20 #include <iostream> #include <string> #include <format>void test_01() {// 使用字符串填充std::cout << std::format("Hello {}!\n", "World"); // Hello World!…...

Ftrans安全数据摆渡系统 构建便捷的内外网数据交换通道

安全数据摆渡系统是一种设计用于解决内外网环境下&#xff0c;数据传输、管理、共享问题的安全系统&#xff0c;通过加密、访问控制等策略&#xff0c;提供安全可靠的数据传输和共享服务&#xff0c;尤其适用于对网络安全建设要求高的行业&#xff0c;比如研发型企业、党政机构…...

【云开发笔记No.14】持续交付、持续部署、持续交付流水线

一、持续交付 持续交付&#xff08;Continuous Delivery&#xff09;是一种软件开发方法论&#xff0c;它强调在开发过程中&#xff0c;软件可以在任何时间以最小的努力被部署到生产环境。其核心是确保代码更改在经过一系列自动化测试后&#xff0c;能够快速、安全地集成到主代…...

蓝桥杯练习07小兔子爬楼梯

小兔子爬楼梯 介绍 小兔子想去月球上旅行&#xff0c;假设小兔子拥有一个阶梯子&#xff0c;当你爬完层就可以到达月球&#xff0c;小兔子每次可以跳1或者2个台阶&#xff0c;小兔子有多少种跳法可以到达月球呢&#xff1f; 给定n是一个正整数&#xff0c;代表梯子的阶数&…...

Docker in Docker原理与实战

Docker in Docker (DinD) 是一种在Docker容器内部运行Docker的技术。它允许在一个Docker容器内部创建和管理其他的Docker容器&#xff0c;实现了一个容器内部的容器编排环境。本文将介绍Docker in Docker的原理&#xff0c;并给出一个实际的应用场景。 Docker in Docker的原理…...

Ruoyi若依框架下载流程详细解读(SpringBoot-Vue)

图解&#xff1a; 前端设计&#xff1a; 前端设计一个link文字连接或者按钮&#xff08;ElementUI&#xff09;Element - The worlds most popular Vue UI framework 前端请求设计&#xff1a; import request from /utils/request //下载示例模型定义语言的JSON export const…...

【深度学习】Pytorch中实现交叉熵损失计算的方式总结

在PyTorch中&#xff0c;计算交叉熵损失主要有以下几种方式&#xff0c;它们针对不同的场景和需求有不同的实现方式和适用范围&#xff1a; 1. nn.CrossEntropyLoss 类 这是最常用且方便的方法&#xff0c;特别适用于多分类任务。nn.CrossEntropyLoss 实际上是同时完成了 sof…...

机器学习:处理jira工单的分类问题

如何根据jira工单的category、reporter自动找到处理它的组呢?这是一个利用机器学习中knn算法的小实践. 目录 Knn算法 数据 示例 分割数据 选择Neighbors knn的优缺点 机器学习是一种技术,它的目的是给机器学习能力,让它们可以根据数据自己做决定,所以对于训练…...

后端常问面经之操作系统

请简要描述线程与进程的关系,区别及优缺点&#xff1f; 本质区别&#xff1a;进程是操作系统资源分配的基本单位&#xff0c;而线程是任务调度和执行的基本单位 在开销方面&#xff1a;每个进程都有独立的代码和数据空间&#xff08;程序上下文&#xff09;&#xff0c;程序之…...

RK3568平台 iperf3测试网络性能

一.iperf3简介 iperf是一款开源的网络性能测试工具&#xff0c;主要用于测量TCP和UDP带宽性能。它可以在不同的操作系统上运行&#xff0c;包括Windows、Linux、macOS等。iperf具有简单易用、功能强大、高度可配置等特点&#xff0c;广泛应用于网络性能测试、网络故障诊断和网…...

Spring Boot中实现对特定URL的权限验证:拦截器、切面和安全框架的比较

引言&#xff1a; 在开发Web应用程序时&#xff0c;对特定URL进行权限验证是一项常见的需求。在Spring Boot中&#xff0c;我们有多种选择来实现这一目标&#xff0c;其中包括使用拦截器、切面和专门的安全框架&#xff08;如Spring Security&#xff09;。本文将比较这三种方式…...

【能源数据分析-00】能源领域数据集集锦(动态更新)

一、前言 大数据科学在能源领域的深度应用&#xff0c;已经深刻改变了这一行业的垂直格局。它为我们提供了宝贵的见解&#xff0c;帮助降低下游市场的成本&#xff0c;使石油生产商能够更好地应对市场繁荣期的需求。近期&#xff0c;石油价格的剧烈下跌给全球经济带来了沉重打…...

数据挖掘与机器学习 1. 绪论

于高山之巅&#xff0c;方见大河奔涌&#xff1b;于群峰之上&#xff0c;便觉长风浩荡 —— 24.3.24 一、数据挖掘和机器学习的定义 1.数据挖掘的狭义定义 背景&#xff1a;大数据时代——知识贫乏 数据挖掘的狭义定义&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、…...

Matlab实现序贯变分模态分解(SVMD)

大家好&#xff0c;我是带我去滑雪&#xff01; 序贯变分模态分解(SVMD) 是一种信号处理和数据分析方法。它可以将复杂信号分解为一系列模态函数&#xff0c;每个模态函数代表信号中的特定频率分量。 SVMD 的主要目标是提取信号中的不同频率分量并将其重构为原始信号。SVMD的基…...

云安全与云计算的关系

云计算又被称为网格计算&#xff0c;是分布式计算的一种&#xff0c;能够将大量的数据计算处理程序通过网络“云”分解成多个小程序&#xff0c;然后将这些小程序的结果反馈给用户。云计算主要就是能够解决任务分发&#xff0c;并进行计算结果的合并。 云安全则是我国企业创造的…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...