基于UDP的网络聊天室(多线程实现收和发消息)

news2024/10/6 20:38:32

要求:1.有新用户登录,其他在线的用户可以收到登录信息

           2.有用户群聊,其他在线的用户可以收到群聊信息

           3.有用户退出,其他在线的用户可以收到退出信息

           4.服务器可以发送系统信息

效果图:

service.c

#include <head.h>
typedef struct _MSG
{
    char type; // 类型  'L' 登录  'C' 群聊  'Q' 退出
    char name[32];
    char txt[128];
} msg_t;
typedef struct _NODE
{
    struct sockaddr_in clientaddr;
    struct _NODE *next;
} node_t;
void create_node(node_t **p);
void do_login(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr);
void do_chat(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr);
void do_quit(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr);

int main(int argc, const char *argv[])
{
    // 入参合理性检查
    if (argc != 3)
    {
        printf("usage error:%s <ip> <port>...\n", argv[0]);
        exit(-1);
    }
    int sockfd = 0;
    // 创建套接字
    if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
    {
        perror("socket error");
        exit(-1);
    }
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    socklen_t serveraddr_len = sizeof(serveraddr);
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);
    // 绑定
    if (-1 == (bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len)))
    {
        perror("bind error");
        exit(-1);
    }
    node_t *phead = NULL;
    create_node(&phead);

    msg_t msg;

    // 收发数据
    char buff[128] = {0};
    pid_t pid = 0;
    pid = fork();
    if (pid == -1)
    {
        perror("fork error");
        exit(-1);
    }
    else if (pid == 0)
    {
        // 子进程用于接收数据
        while (1)
        {
            memset(&msg, 0, sizeof(msg));
            if (-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &clientaddr_len))
            {
                perror("recvfrom error");
                exit(-1);
            }
            printf("%s:%s\n", msg.name, msg.txt);
            switch (msg.type)
            {
            case 'L':
                do_login(phead, msg, sockfd, clientaddr);
                break;
            case 'C':
                do_chat(phead, msg, sockfd, clientaddr);
                break;
            case 'Q':
                do_quit(phead, msg, sockfd, clientaddr);
                break;
            }
        }
    }
    else if (pid > 0)
    {
        // 父进程用于发送数据
        // 把父进程当做一个客户端 以群聊的方式 把系统消息发给子进程
        strcpy(msg.name, "server");
        msg.type = 'C';
        while (1)
        {
            memset(msg.txt, 0, 128);
            fgets(msg.txt, sizeof(msg.txt), stdin);
            msg.txt[strlen(msg.txt) - 1] = '\0';
            if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len))
            {
                perror("sento error");
                exit(-1);
            }
        }
    }

    close(sockfd);
    return 0;
}

void create_node(node_t **p)
{
    *p = (node_t *)malloc(sizeof(node_t));
    memset(*p, 0, sizeof(node_t));
}
// 登录操作函数
void do_login(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr)
{
    // 遍历链表 当前在线的所有人发“***加入了群聊”的消息
    node_t *ptemp = phead;
    while (ptemp->next != NULL)
    {
        ptemp = ptemp->next;
        if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(ptemp->clientaddr), sizeof(ptemp->clientaddr)))
        {
            perror("sento error");
            exit(-1);
        }
    }
    // 把新加入的群聊客户端网络信息结构体加入到链表中
    node_t *pnew = NULL;
    create_node(&pnew);
    pnew->clientaddr = clientaddr;
    pnew->next = phead->next;
    phead->next = pnew;
    return;
}
void do_chat(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr)
{
    // 遍历链表 将群聊的消息 发给除了自己之外的所有人
    node_t *ptemp = phead;
    while (ptemp->next != NULL)
    {
        ptemp = ptemp->next;
        if (memcmp(&clientaddr, &(ptemp->clientaddr), sizeof(clientaddr)) != 0)
        {
            if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(ptemp->clientaddr), sizeof(ptemp->clientaddr)))
            {
                perror("sento error");
                exit(-1);
            }
        }
    }
    return;
}
void do_quit(node_t *phead, msg_t msg, int sockfd, struct sockaddr_in clientaddr)
{
    // 把 xxx 退出群聊的消息 发给在线的除自己的所有人  并且将自己在链表中删除
    node_t *ptemp = phead;
    while (ptemp->next != NULL)
    {
        if (memcmp(&clientaddr, &(ptemp->next->clientaddr), sizeof(clientaddr)) != 0)
        {
            if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(ptemp->next->clientaddr), sizeof(ptemp->next->clientaddr)))
            {
                perror("sento error");
                exit(-1);
            }
            ptemp = ptemp->next;
        }
        else
        {
            node_t *pdel = ptemp->next;
            ptemp->next = pdel->next;
            free(pdel);
            pdel = NULL;
        }
    }
    return;
}

client.c

#include <head.h>
typedef struct _MSG
{
    char type; // 类型  'L' 登录  'C' 群聊  'Q' 退出
    char name[32];
    char txt[128];
} msg_t;

int main(int argc, const char *argv[])
{
    // 入参合理性检查
    if (argc != 3)
    {
        printf("usage error:%s <ip> <port>...\n", argv[0]);
        exit(-1);
    }
    int sockfd = 0;
    // 创建套接字
    if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
    {
        perror("socket error");
        exit(-1);
    }
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    socklen_t serveraddr_len = sizeof(serveraddr);
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    msg_t msg;
    memset(&msg, 0, sizeof(msg));

    printf("请输入用户名:");
    fgets(msg.name, sizeof(msg.name), stdin);
    msg.name[strlen(msg.name) - 1] = '\0';
    msg.type = 'L';
    strcpy(msg.txt, "加入群聊");
    if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len))
    {
        perror("sendto error");
        exit(-1);
    }
    // 收发数据
    char buff[128] = {0};
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        perror("fork error");
        exit(-1);
    }
    else if (pid == 0)
    {
        // 子进程用于接收数据
        while (1)
        {
            memset(&msg, 0, sizeof(msg));
            if (-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL))
            {
                perror("recvfrom error");
                exit(-1);
            }
            printf("%s : %s\n", msg.name, msg.txt);
        }
    }
    else if(pid>0)
    {
        // //父进程 在终端获取数据 发给服务器
        while (1)
        {
            memset(msg.txt, 0, sizeof(msg.txt));
            fgets(msg.txt, 128,stdin);
            msg.txt[strlen(msg.txt) - 1] = '\0';
            if (strcmp(msg.txt, "quit") == 0)
            {
                msg.type = 'Q';
                strcpy(msg.txt, "退出群聊");
            }
            else
            {
                msg.type = 'C';
            }
            if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len))
            {
                perror("sendto error");
                exit(-1);
            }
            if ('Q' == msg.type)
            {
                // 父进程退出之前先给子进程发信号 杀死子进程
                kill(pid, SIGKILL);
                wait(NULL);
                break;
            }
        }
    }
    close(sockfd);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网一键部署k8s-kubeshpere,1.22.12版本

1.引言 本文档旨在指导读者在内网环境中部署 Kubernetes 集群。Kubernetes 是一种用于自动化容器化应用程序部署、扩展和管理的开源平台&#xff0c;其在云原生应用开发和部署中具有广泛的应用。然而&#xff0c;由于一些安全或网络限制&#xff0c;一些组织可能选择在内部网络…

计算机专业课面试常见问题-计算机网络篇

目录 1. 计算机网络分为哪 5 层&#xff1f; 2. TCP 协议简述&#xff1f; 3. TCP 和 UDP 的区别&#xff1f;->不同的应用场景&#xff1f; 4. 从浏览器输入网址到显示页…

论文阅读MVBench: A Comprehensive Multi-modal Video Understanding Benchmark

摘要(Abstract)&#xff1a; 论文介绍了MVBench&#xff0c;这是一个全新的多模态视频理解基准测试&#xff0c;旨在评估多模态大型语言模型&#xff08;MLLMs&#xff09;在视频理解方面的能力。 目前许多基准测试主要集中在静态图像任务的空间理解上&#xff0c;而忽视了动…

云计算【第一阶段(21)】引导过程与服务控制

目录 一、linux操作系统引导过程 1.1、开机自检 1.2、MBR引导 1.3、GRUB菜单 1.4、加载 Linux 内核 1.5、init进程初始化 1.6、简述总结 1.7、初始化进程centos 6和7的区别 二、排除启动类故障 2.1、修复MBR扇区故障 2.1.1、 实验 2.2、修复grub引导故障 2.2.1、实…

Marin说PCB之total etch length规则知多少?

魔都上海最近迎来了一轮梅雨季节了&#xff0c;小编我上周就已经提前把被子衣服袜子都晒了一遍&#xff0c;省的后面一段时间下雨就不能晒了。这种阴雨绵绵的天气当然在家里睡觉最舒服了&#xff0c;上周留正当我在家里夏眠的时候&#xff0c;突然被一阵手机铃声吵醒了&#xf…

Spring Boot如何实现跨域资源共享(CORS)?

&#x1f345; 作者简介&#xff1a;哪吒&#xff0c;CSDN2021博客之星亚军&#x1f3c6;、新星计划导师✌、博客专家&#x1f4aa; &#x1f345; 哪吒多年工作总结&#xff1a;Java学习路线总结&#xff0c;搬砖工逆袭Java架构师 &#x1f345; 技术交流&#xff1a;定期更新…

ArkTS开发系列之Web组件的学习(2.9)

上篇回顾&#xff1a;ArkTS开发系列之事件&#xff08;2.8.2手势事件&#xff09; 本篇内容&#xff1a; ArkTS开发系列之Web组件的学习&#xff08;2.9&#xff09; 一、知识储备 Web组件就是用来展示网页的一个组件。具有页面加载、页面交互以及页面调试功能 1. 加载网络…

多商户万能DIY商城小程序源码系统 支持自营+独立部署 带完整的安装代码包以及搭建教程

系统概述 多商户万能 DIY 商城小程序源码系统是一个综合性的电商平台解决方案&#xff0c;旨在满足不同用户的多样化需求。它不仅支持自营模式&#xff0c;还为多商户入驻提供了广阔的空间&#xff0c;使平台能够汇聚各类商品和商家&#xff0c;形成一个丰富多样的商业生态。 …

字节发布Depth Anything V2深度模型,比 Depth Anything V1 更精细的细节。

欢迎点击关注下方公众号并加入官方读者交流群&#xff0c;一个有趣有AI的AIGC公众号:关注AI、深度学习、计算机视觉、AIGC、Stable Diffusion、Sora等相关技术&#xff0c;欢迎一起交流学习&#x1f497;&#xff5e; 字节发布Depth Anything V2深度模型。比 Depth Anything V1…

昇思25天学习打卡营第3天|onereal

前几天不能运行代码&#xff0c;经过排查是因为我的浏览器是搜狗的&#xff0c;换成Chrome问题解决了。按照提示学习了《应用实践/计算机视觉/FCN图像语义分割.ipynb》并且尝试运行代码&#xff0c;开始训练&#xff0c;最后看到图片变化。 网络流程 FCN网络的流程如下图所示&…

【课程总结】Day11(下):YOLO的入门使用

前言 YOLO的简介 YOLO&#xff08;You Only Look Once&#xff09;是一种流行的目标检测算法&#xff0c;由Joseph Redmon等人于2015年提出。YOLO的设计思想是将目标检测任务转化为单个神经网络的回归问题&#xff0c;通过在图像上划分网格并对每个网格预测边界框和类别置信度…

Node.js全栈指南:浏览器显示一个网页

上一章&#xff0c;我们了解到&#xff0c;如何通过第二章的极简 Web 的例子来演示如何查看官方文档。为什么要把查阅官方文档放在前面的章节说明呢&#xff1f;因为查看文档是一个很重要的能力&#xff0c;就跟查字典一样。 回想一下&#xff0c;我们读小学&#xff0c;初中的…

如何创建一个vue项目

目录 1.环境准备 2.检查node和npm版本&#xff0c;确定已安装nodejs 3.全局安装vue/cli、webpack、webpack-cli、vue/cli-init 4.检查vue版本,注意V是大写 5.创建vue项目 6.得到的vue项目目录结构如下&#xff1a; 1.环境准备 安装nodejs,或者安装nvm&#xff0c;并使用…

day38动态规划part01| 理论基础 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

**理论基础 ** 无论大家之前对动态规划学到什么程度&#xff0c;一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目&#xff0c;看我讲的理论基础&#xff0c;会有感觉 是不是简单题想复杂了&#xff1f; 其实并没有&#xff0c;我讲的理论基础内容&#xff0c;…

盲源信道分离—FastICA算法性能仿真

本案例中使用Matlab软件对FastICA算法的声音分离性能进行了仿真&#xff0c;分别对简单波形的混合信号、不同类型声音的混合信号、同一类型的混合信号这三种情况进行仿真&#xff0c;主要从分离信号的波形形状、串音误差两方面对分离性能进行衡量&#xff0c;仿真结果显示快速I…

前端新手小白的第一个AI全栈项目---AI聊天室

前言 ok&#xff0c;大家好。- ̗̀(๑ᵔ⌔ᵔ๑)最近也是想做自己的第一个前后端分离的项目&#xff0c;刚好最近学了一点AI接口的实现。想着用接口做一个自己的ai聊天室并且尝试一下全栈式开发。中间真的解决了很多问题&#xff0c;也是成功之后也是想要将实现过程分享一下&a…

4.任务调度

1.基本知识 2.任务的状态 FreeRTOS中任务共存在4种状态&#xff1a;Running 运行态 当任务处于实际运行状态称之为运行态&#xff0c;即CPU的使用权被这个任务占用&#xff08;同一时间仅一个任务处于运行态&#xff09;。Ready 就绪态 处于就绪态的任务是指那些能够运行&…

6毛钱SOT-23封装28V、400mA 开关升压转换器,LCD偏置电源和白光LED应用芯片TPS61040

SOT-23-5 封装 TPS61040 丝印PHOI 1 特性 • 1.8V 至 6V 输入电压范围 • 可调节输出电压范围高达 28V • 400mA (TPS61040) 和 250mA (TPS61041) 内部开关电流 • 高达 1MHz 的开关频率 • 28μA 典型空载静态电流 • 1A 典型关断电流 • 内部软启动 • 采用 SOT23-5、TSOT23…

图解HTTP笔记整理(前六章)

图解HTTP 第一章 web使用HTTP &#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;协议作文规范&#xff0c;完成从客户端到服务器端等一系列运作流程。 协议&#xff1a;计算机与网络设备要相互通信&#xff0c;双方就必须基于相同的方法。比如…

[油猴脚本] Image To Ascii 快速转换审计网站图片中敏感信息插件

项目地址:https://github.com/MartinxMax/ImageToAscii 导入 将ImagetoAscii.user.js导入油猴 进行按照 访问网站分析图片 当鼠标靠近图片时会出现分析按钮 通过审查图片信息,我们可以快速发现这张图片存在PHP代码。 当然在渗透测试中,你可以快速查看上传的图片木马中PHP代码…