UDP建立聊天群

参考网上代码

接收端
#include<myhead.h>

#define PRINT_ERR(msg)                                      \
    do                                                      \
    {                                                       \
        printf("%s,%d,%s\n", __FILE__, __LINE__, __func__); \
        perror(msg);                                        \
        exit(-1);                                           \
    } while (0)
 
typedef struct
{
    char code; // 操作码 'L' 登录  'C' 群聊  'Q' 退出
    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[])
{
    // 入参合理性判断
    if (argc != 3)
    {
        printf("age:%s ip port\n", argv[0]);
        return -1;
    }
    // 创建套接字
    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 PRINT_ERR(msg)                                      \
    do                                                      \
    {                                                       \
        printf("%s,%d,%s\n", __FILE__, __LINE__, __func__); \
        perror(msg);                                        \
        exit(-1);                                           \
    } while (0)
 
typedef struct
{
    char code; // 操作码 'L' 登录  'C' 群聊  'Q' 退出
    char name[32];
    char txt[128];
} msg_t;
 
int main(int argc, const char *argv[])
{
    // 入参合理性判断
    if (argc != 3)
    {
        printf("age:%s ip port\n", argv[0]);
        return -1;
    }
    // 创建套接字
    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;
}
 

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

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

相关文章

求解线性方程组

如图题意看出x1有且仅有两种可能&#xff0c;1或者0&#xff0c;且知道了所有a的值&#xff0c;且因为要求所得答案字典序最小&#xff0c;所以先假设x10。 又因a2x1x2所以可以求出x2的值&#xff0c;又如a2x1x2x3,所以可以求出x3的值依次求出所有x的值&#xff0c;但每求出一…

Java 基础知识- 创建线程的几种方式

大家好我是苏麟 , 今天聊聊创建线程的几种方式 . 创建线程的几种方式 1. 继承Thread类实现多线程 /*** className: ThreadTest* author: SL 苏麟**/ public class ThreadTest extends Thread{public static void main(String[] args) {ThreadTest threadTest new ThreadTes…

第十二届蓝桥杯省赛CC++ 研究生组-路径

记录到每个结点的最短距离&#xff0c;以此为基础计算后续结点最优值 #include<iostream> #include<algorithm> using namespace std; typedef long long ll;ll gcd(int a, int b){if(!b) return a;return gcd(b, a % b); }int main(){ll dp[2022] {0};//dp[i]记…

ppp协议

一.实验拓扑 二.实验要求 1.R1和R2使用PPP链路直连&#xff0c;R2和R3把2条PPP链路捆绑为PPP MP直连 2.按照图示配置IP地址 3.R2对R1的PPP进行单向chap验证 4.R2和R3的PPP进行双向chap验证 三.实验思路 1.R2对R1进行ppp单向chap验证&#xff1a; R2配置为主&#xff0c;…

数据库语言一些基本操作

1&#xff0c;消除取值重复的行。 例如&#xff1a;查成绩不及格的学号&#xff1a;SELECT DISTINCT sno FROM SC WHERE grade<60. 这里使用DISTINCT表示取消取值重复的行。 2&#xff0c;比较。 例如&#xff1a;查计算机系全体学生的姓名&#xff1a;SELECT Sname FROM…

模拟实现字符串库函数(一)

在C语言的标准库中提供了很多针对字符串的库函数&#xff0c;这篇文章我们会学习并模拟实现几个简单的库函数 求字符串长度函数strlen strlen函数我们在之前已经用过很多次了&#xff0c;同时也模拟实现过&#xff0c;但是都不是模仿标准库中的strlen来实现&#xff0c;首先我…

三.寄存器(内存访问)

1.内存中字的存储 2.并不是所有cpu都支持将数据段送入段寄存器&#xff0c;所以有时候用个别的寄存器先把数据段存储起来&#xff0c;再把该寄存器mov到段寄存器。 3.字的传送 4.栈 5.栈机制 举例说明 6.栈顶超界问题 push超界 pop超界 7.栈段

pta-洛希极限

科幻电影《流浪地球》中一个重要的情节是地球距离木星太近时&#xff0c;大气开始被木星吸走&#xff0c;而随着不断接近地木“刚体洛希极限”&#xff0c;地球面临被彻底撕碎的危险。但实际上&#xff0c;这个计算是错误的。 洛希极限&#xff08;Roche limit&#xff09;是一…

python写爬虫爬取京东商品信息

工具库 爬虫有两种方案&#xff1a; 第一种方式是使用request模拟请求&#xff0c;并使用bs4解析respond得到数据。第二种是使用selenium和无头浏览器&#xff0c;selenium自动化操作无头浏览器&#xff0c;由无头浏览器实现请求&#xff0c;对得到的数据进行解析。 第一种方…

实战高效RPC方案在嵌入式环境中的应用与揭秘

实战高效RPC方案在嵌入式环境中的应用与揭秘 开篇 在嵌入式系统开发中&#xff0c;大型项目往往采用微服务架构来构建&#xff0c;其核心思想是将一个庞大的单体应用分割成一系列小型、独立、松耦合的服务模块&#xff0c;这些模块可以是以线程或进程形式存在的多个服务单元。…

OpenHarmony开发-线程安全阻塞队列

概述 简介 ​线程安全阻塞队列SafeBlockQueue类&#xff0c;提供阻塞和非阻塞版的入队入队和出队接口&#xff0c;并提供可最追踪任务完成状态的的SafeBlockQueueTracking类。 #include <safe_block_queue.h> 涉及功能 接口说明 OHOS::SafeBlockQueue OHOS::SafeBl…

[Java C++] JNI开发

JNI&#xff08;Java Native Interface&#xff09;是 Java 提供的一种编程桥梁&#xff0c;它允许 Java 代码和本地&#xff08;Native&#xff09;代码进行交互。通过 JNI&#xff0c;Java 程序可以调用本地语言&#xff08;如C、C&#xff09;编写的代码&#xff0c;并且本地…

如何用python编写记录你女友的生日呢?

如何用python编写记录你女友的生日呢&#xff1f; 我这边写一个简单的 Python 程序示例,可以用来记录生日.这个程序将用户输入的姓名和生日信息保存到一个字典中,并允许用户查找特定姓名对应的生日信息. def record_birthday():birthdays {}while True:print("1. 添加生…

IP地址、子网掩码、网关

这些概念的来源 很久以前&#xff0c;有两个计算机想要相互通信&#xff0c;于是它们在自己的设备上安装了一个网卡&#xff0c;并用网线连接&#xff1a; 这个时候&#xff0c;又来了一个计算机想要加入它们&#xff0c;于是这三个计算机互相通过网线连接&#xff1a; 随着想…

taro之Swiper的使用

图样&#xff1a; 往往我们需要轮播图去显示我们想要的图片之类的 这是工作的代码 <View classNametop-title><SwiperclassNamebanner-swiperinterval{3000}circularautoplay>{homeBannerList.map((item) > {return (<SwiperItem key{item.id}><View…

Linux之git

一、什么叫做版本控制 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。简单来说就是用于管理多人协同开发项目的技…

MySQL临时表:临时存储数据的灵活利器

MySQL临时表&#xff1a;临时存储数据的灵活利器 MySQL临时表是处理数据时非常有用的工具&#xff0c;它提供了临时存储数据的能力&#xff0c;使得复杂查询、排序、聚合以及数据筛选变得更加高效和简单。在本文中&#xff0c;我们将深入探讨MySQL临时表的概念以及何时需要使用…

【算法刷题day1】Leetcode:704. 二分查找、27. 移除元素

文章目录 Leetcode 704. 二分查找解题思路代码总结 Leetcode 27. 移除元素解题思路代码总结 草稿图网站 java的Deque Leetcode 704. 二分查找 题目&#xff1a;704. 二分查找 解题思路 1.左闭右闭区间的搜索&#xff0c;循环条件为left < right。 2.左闭右开区间的搜索&…

C++一维数组练习oj(3)

为什么C的一维数组练习要出要做那么多的题目&#xff1f;因为我们是竞赛学生&#xff01;想要将每个知识点灵活运用的话就必须刷大量的题目来锻炼思维。 我使用的是jsswoj.com这个刷题网站&#xff0c;当然要钱... C一维数组练习oj(2)-CSDN博客这是上一次的题目讲解 这道题有…

Unity学习笔记 6.2D换帧动画

下载源码 UnityPackage 目录 1.导入图片 1.1. 图片的叠放顺序 2.图片切片 3.用动画控制器让马&#x1f40e;动起来 1.导入图片 直接拖拽进场景 检查 Texture Type&#xff08;纹理类型&#xff09;是否为 Sprite 创建2D精灵对象&#xff0c;拖拽图片到Sprite&#xff08…