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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

esp32CAM环境搭建(arduino+MicroPython+thonny+固件)

arduino ide 开发工具 arduino版本&#xff1a;1.8.19 arduino ide 中文设置&#xff1a;​ file >> preferences >> ​ arduino IDE 获取 ESP32 开发环境&#xff1a;打开 Arduino IDE &#xff0c;找到 文件>首选项 ,将 ESP32 的配置链接填入附加开发板管理网…

FOCUS-AND-DETECT: A SMALL OBJECTDETECTION FRAMEWORK FOR AERIAL IMAGES

摘要 为了解决小对象检测问题&#xff0c;提出了一个叫做 Focus-and Detect 的检测框架&#xff0c;它是一个两阶段的框架。 第 一阶段包括由高斯混合模型监督的对象检测器网络&#xff0c;生成构成聚焦区域的对象簇 。 第二阶段 也是一个物体探测器网络&#xff0c;预测聚焦…

10基于访问权限控制和细粒度控制的方式访问资源

访问权限控制 RBAC 基于角色的访问控制(Role-Based Access Control)是按角色进行授权,如主体的角色为总经理时才可以查询企业运营报表和员工工资信息等 缺点&#xff1a;查询工资所需要的角色变化为总经理和部门经理&#xff0c;此时就需要修改判断逻辑为判断用户角色是否为…

【Django开发】0到1美多商城项目md教程第3篇:用户注册业务实现,1. 用户注册页面绑定Vue数据【附代码文档】

美多商城完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;欢迎来到美多商城&#xff01;&#xff0c;项目准备。展示用户注册页面&#xff0c;创建用户模块子应用。用户注册业务实现&#xff0c;用户注册前端逻辑。图形验证码&#xff0c;图形验证码接口设…

vue 隐藏导航栏和菜单栏,已解决

初始效果&#xff1a; 效果&#xff1a; 出现问题&#xff1a; 解决方法&#xff1a;

【数字图像处理matlab系列】使用数组索引进行简单的图像裁剪、二次取样操作

【数字图像处理matlab系列】使用数组索引进行简单的图像裁剪、二次取样操作 【先赞后看养成习惯】求点赞+关注+收藏! pout.tif是一张matlab自带的图片,图像尺寸是291*240,使用imread读取该图像>> a = imread(pout.tif); >> imshow(a);对图像a进行上下翻转操作,…

【浅尝C++】类和对象第一弹=>类的定义/访问限定符/实例化/类对象大小计算/this指针

&#x1f3e0;专栏介绍&#xff1a;浅尝C专栏是用于记录C语法基础、STL及内存剖析等。 &#x1f6a9;一些备注&#xff1a;之前的文章有点杂乱&#xff0c;这里将前面的知识点重新组织了&#xff0c;避免了过多冗余的废话。 &#x1f3af;每日努力一点点&#xff0c;技术变化看…

小程序英文口语发音评测

一、英文口语评测需求 在全球化的今天&#xff0c;英语已经成为了世界上最重要的国际语言之一。无论是在国际商务、科技研究、教育还是日常生活中&#xff0c;英语都扮演着举足轻重的角色。因此&#xff0c;掌握英文口语的能力对于个人的职业发展、学术研究以及跨文化交流都具…

【C语言】指针基础知识(二)

一&#xff0c;指针变量类型的意义 1&#xff0c;指针的类型决定了&#xff0c;对指针解引⽤的时候有多⼤的权限&#xff08;⼀次能操作⼏个字节&#xff09;。 例如&#xff1a;char* 的指针解引⽤访问⼀个字节&#xff0c;int* 的指针解引⽤访问四个字节&#xff0c;short*…

DolphinScheduler运维-页面加载缓慢

一、问题描述 DolphinScheduler调度平台的UI界面加载缓慢,项目中的任务实例加载时间过长,需要解决这个问题,提高DolphinScheduler平台UI页面的加载速度。 二、原因分析 经过分析发现,任务实例过多是导致UI加载缓慢的主要原因。由于任务实例无法直接删除,根据文档了解到需…

集成学习 | 集成学习思想:Boosting

目录 一. Boosting思想1. Adaboost 算法1.1 Adaboost算法构建流程1.2 sklearn库参数说明 2. Gradient Boosting 算法2.1 Gradient Boosting算法构建流程2.2 Gradient Boosting算法的回归与分类问题2.2.1 Gradient Boosting回归算法均方差损失函数绝对误差损失函数 2.2.2 Gradie…

SpringMVC结合设计模式:解决MyBatisPlus传递嵌套JSON数据的难题

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

有道翻译实现接口加密解密

文章目录 目标简单逆向分析源码深度逆向分析参考文献目标 实现对网易有道 sign 等参数的加密 及 返回的密文数据解密实现 简单逆向分析 首先在右上角提前登录好账号信息。 输入中文:你好 要求翻译成:英文 全局搜索:你好 或 hello,结果没有发现什么。 切换 Fetch/XHR …

QML ShapePath绘制虚线

一.qml PathLine介绍 在 QML&#xff08;Qt Modeling Language&#xff09;中&#xff0c;PathLine 是 Path 元素的一个子类型&#xff0c;用于创建两点之间的直线段。Path 类型用于描述一个二维路径&#xff0c;可以用来绘制形状、曲线和直线。PathLine 是所有路径曲线中最简单…

Day60:WEB攻防-PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改

目录 PHP-DEMO1-序列化和反序列化 序列化操作 - 即类型转换 序列化案例 PHP-DEMO2-魔术方法触发规则 __construct(): //当对象new的时候会自动调用 __destruct()&#xff1a;//当对象被销毁时会被自动调用 __sleep(): //serialize()执行时被自动调用 __wakeup(): //uns…

高中信息技术教资刷题笔记_选择题篇

1.信息技术基础 位与字节的换算 模2除法运算 网页保存 进制之间的计算 教你快速学会二进制、十进制、十六进制之间的转换 - 知乎 (zhihu.com) 原码、补码、反码计算 物联网技术 位运算 按位与&#xff1a;同位置为1&#xff0c;则为1&#xff0c;其他都是0按位或&#xff1a;有…

2024年产品品牌化深度分析:消费者心理与品牌化、产品质量的权衡

随着市场竞争的加剧和消费者需求的多样化&#xff0c;产品品牌化已经成为企业不可或缺的战略选择。在2024年&#xff0c;当消费者面对众多商品时&#xff0c;品牌化与产品质量之间的权衡成为了消费者决策的重要因素。那么&#xff0c;在消费者心理中&#xff0c;品牌化重要还是…

Docker 之 数据卷

目录 1. 数据卷是什么 1.1 运行一个带有容器卷存储功能的容器实例 2.能干什么 3. 容器卷案例 3.1 宿主机vs容器之间映射添加容器卷 3.1.1 命令添加&#xff1a; 3.1.2 查看数据卷是否挂载成功 3.1.3 容器和宿主机之间数据共享 3.2 读写规则映射添加说明 3.2.1 读写&…

详解:JS异步解决方案之回调函数,及其弊端

「异步编程」是前端工程师日常开发中经常会用到的技术&#xff0c;异步的实现有好几种方式&#xff0c;各有利弊&#xff0c;本篇先讲通过回调来实现来异步 。 一、同步和异步 同步编程和异步编程是两种不同的编程方式。 同步编程是指按照代码的顺序执行&#xff0c;每一行代…

前端小卡片:vue3路由是什么,有什么作用,该如何配置?

在 Vue 3 中&#xff0c;路由的处理使用了 Vue Router&#xff0c;它是官方提供的路由管理器。Vue Router 用于实现单页应用中的路由功能&#xff0c;通过将不同的 URL 映射到对应的组件&#xff0c;实现页面之间的切换和导航。 Vue Router 的作用包括&#xff1a; 实现页面之…
最新文章