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

要求: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;
}

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

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

相关文章

Day5:有效的字母异位词 242 两个数组的交集 349 快乐数 202 两数之和1

题目242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool isAnagram(string s, string t) {//把数组当成哈希表&#xff0c;用两个数组来存储取模后的字母对应的数量//26个字母&#xff0c; 模25 0~25int arrs[26];int arrt[26];for(…

<电力行业> - 《第4课:什么是电力?什么是发输变配用5环节?》

1 什么是电力&#xff1f; 我们对于平日生活中离不开的电很熟悉&#xff0c;但是电力是什么&#xff1f; 其实&#xff0c;电力就是电能作为动力的能源。电力就是我们说的电&#xff0c;不过更多了系统化。 19世纪70年代&#xff0c;电力的发明和应用掀起了第二次工业化高潮。…

MySQL之可扩展性(四)

可扩展性 向外扩展 分片?还是不分片&#xff1f; 这是一个问题&#xff0c;对吧&#xff1f;答案很简单:如非必要&#xff0c;尽量不分片。首先看是否能通过性能调优或者更好的应用或数据库设计来推迟分片。如果能足够长时间地推迟分片&#xff0c;也许可以直接购买更大地服…

springcloud第4季 springcloud-alibaba之openfegin+sentinel整合案例

一 介绍说明 1.1 说明 1.1.1 消费者8081 1.1.2 openfegin接口 1.1.3 提供者9091 9091微服务满足&#xff1a; 1 openfegin 配置fallback逻辑&#xff0c;作为统一fallback服务降级处理。 2.sentinel访问触发了自定义的限流配置&#xff0c;在注解sentinelResource里面配置…

吴恩达机器学习 第三课 week2 推荐算法(下)

目录 01 学习目标 02 基于内容的过滤算法 03 实现“电影推荐系统” 3.1 问题描述 3.2 算法实现 04 大项目&#xff08;数据很大&#xff09;的推荐方法※ 4.1 方法原理 4.2 实施示例 05 总结 01 学习目标 &#xff08;1&#xff09;理解基于内容的过滤算法&#xff08…

(四十六)Vue Router组件所独有的两个钩子activate、deactivated

文章目录 activated钩子函数deactivated钩子函数demo 上一篇&#xff1a;&#xff08;四十五&#xff09;Vue Router之编程式路由导航 Vue Router提供了两个钩子函数&#xff0c;分别是activated和deactivated。 这两个钩子函数可以用于在路由组件的激活状态发生变化时执行相…

前端开发实战项目:实时天气预报应用

引言 在本实战项目中&#xff0c;我们将开发一个实时天气预报应用。这个项目将帮助你掌握前端开发的核心技能&#xff0c;包括HTML、CSS、JavaScript&#xff0c;以及如何使用API来获取实时数据。通过这个项目&#xff0c;你将学会如何构建用户界面、处理用户交互、以及与第三…

HarmonyOS Next开发学习手册——通过startAbility拉起文件处理类应用

使用场景 开发者可以通过调用startAbility接口&#xff0c;由系统从已安装的应用中寻找符合要求的应用来实现打开特定文件的意图&#xff0c;例如&#xff1a;浏览器下应用下载PDF文件&#xff0c;可以调用此接口选择文件处理应用打开此PDF文件。开发者需要在请求中设置待打开…

IO-Link ISDU

目录 一、引言 二、ISDU定义与功能 三、ISDU指令构成 四、ISDU应用场景 五、ISDU优势 六、总结 一、引言 IO-Link技术作为工业自动化领域的创新通信标准&#xff0c;通过单一电缆实现了设备层级的透明化通信。其中&#xff0c;Indexed Service Data Unit&#xff08;ISDU…

目标检测mAP

【目标检测】目标检测算法评估指标(性能度量) AP&#xff0c;mAP 详细介绍_ap和map的区别-CSDN博客 目标检测中的mAP | Clouds Blog 目标检测AP如何理解&#xff1f;_置信度与ap-CSDN博客 一、IOU (Intersection Over Union, 交并比) 二、查准率和查全率 True Positive (TP…

力扣随机一题 6/26 哈希表 数组 思维

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 题目一&#xff1a; 2869.收集元素的最少操作次数【简单】 题目&#xff…

Scania斯堪尼亚SHL题库综合能力性格测试真题题型解析及面试经验

一、走进Scania斯堪尼亚 Scania是一家成立于1891年的瑞典公司&#xff0c;专注于重型卡车和巴士的制造&#xff0c;以其模块化系统和环保设计闻名。作为全球领先的运输解决方案提供商&#xff0c;Scania不仅提供高质量的车辆&#xff0c;还提供相关服务和融资解决方案。公司秉…

我对AI赋能的未来畅想

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

经验分享,在线word转图片

这里分享一个在线word转图片的网站&#xff0c;比较好用 网址&#xff1a;http://www.docpe.com/word/word-to-image.aspx 截图&#xff1a;

一加全机型TWRP合集/橙狐recovery下载-20240603更新-支持一加12/Ace3V手机

TWRP是目前安卓平台的刷机神器&#xff0c;可快速刷写第三方ROM或官方系统&#xff0c;刷入TWRP之前需要解锁BL&#xff0c;目前已适配一加多个机型。ROM乐园小编20240603整理&#xff0c;涵盖一加1到一加Ace3V多机型专用TWRP文件&#xff0c;个人机型橙狐recovery适配相对完整…

react学习——17react中todoList案列

1、项目目录 2、App.js //创建“外壳”组件APP import React, {Component} from "react"; //引入Header组件 import Header from "./components/Header"; //引入List组件 import List from "./components/List"; //引入Footer组件 import Foot…

[极客大挑战 2020]Roamphp2-Myblog

又来喽 经过一番测试&#xff0c;发现文件包含&#xff0c;使用伪协议读取文件 例&#xff1a;php://filter/readconvert.base64-encode/resourcelogin //这里我只写php部分 //login.php <?php require_once("secret.php"); mt_srand($secret_seed); $_SESSION…

Kubernetes之Controller详解

本文尝试从Kubernetes Controller的种类、交互逻辑、最佳实践、伪代码示例及历史演进5个方面对其进行详细阐述&#xff0c;希望对您有所帮助&#xff01; 一、Kubernetes Controller种类 Kubernetes Controller Manager 是 Kubernetes 集群的核心组件之一&#xff0c;负责管理…

分布式系统:常见的陷阱和复杂性

分布式系统的复杂性是工程师和开发人员面临的重要挑战。复杂性往往会随着系统的发展而增加&#xff0c;因此积极主动非常重要。让我们来谈谈您可能会遇到哪些类型的复杂性以及在工作中应对它的有效策略。 分布式系统和复杂性 在开发中&#xff0c;分布式系统是相互连接并执行…

nacos 简述 安装运行

一、下载 官网:Redirecting to: https://nacos.io/ 文档:Nacos 快速开始 github地址:GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. 下载nacos server(tips:也…