3.24作业

news2024/11/15 13:30:12

基于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.coloradmin.cn/o/1544431.html

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

相关文章

数据结构 之 队列习题 力扣oj(附加思路版)

优先级队列 #include<queue> --队列 和 优先级队列的头文件 优先级队列&#xff1a; 堆结构 最大堆 和 最小堆 相关函数&#xff1a; front() 获取第一个元素 back() 获取最后一个元素 push() 放入元素 pop() 弹出第一个元素 size() 计算队列中元素…

<网络>初识计算机网络

目录 一、网络发展 &#xff08;一&#xff09;背景 &#xff08;二&#xff09;类型 二、网络协议 &#xff08;一&#xff09;认识协议 &#xff08;二&#xff09;协议分层 &#xff08;三&#xff09;OSI七层模型 &#xff08;四&#xff09;TCP/IP五层模型 &…

[音视频学习笔记]八、FFMpeg结构体分析 -上一个项目用到的数据结构简单解析:AVFrame、AVFormatContext、AVCodecContext

前言 上次我们做了一个简单的视频解码&#xff0c;MediaPlay-FFmpeg - Public 这一次简单对这个代码进行一个剖析&#xff0c;对其中的数据结构进行一个解析。 这些数据结构之间的关系 AVFrame 、AVFormatContext 、AVCodecContext 、AVIOContext 、AVCodec 、AVStream 、AV…

Pillow教程01:初识Pillow模块(创建Image对象+查看属性+图片的保存与缩放)

--------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁剪…

【算法专题--双指针算法】leecode-15.三数之和(medium)、leecode-18. 四数之和(medium)

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言1. 三数之和2. 解法&…

如何使用PHP和RabbitMQ实现延迟队列(方式二)?

前言 前几天写了一篇关于PHP和RabbitMQ如何通过插件实现延迟队列的功能。 今天写另外一篇不需要插件的方式&#xff0c;使用RabbitMQ的死信队列&#xff08;Dead-Letter-Exchanges, DLX&#xff09;和消息TTL&#xff08;Time-To-Live&#xff09;。 这种方法涉及到设置消息…

OpenHarmony 源码解析之SystemUi—Statusbar(TS)

作者&#xff1a;董伟 简介 SystemUI应用是OpenHarmony中预置的系统应用&#xff0c;为用户提供系统相关信息展示及交互界面&#xff0c;包括系统状态、系统提示、系统提醒等&#xff0c;例如系统时间、电量信息。 本文主要分析batterycomponent、clockcomponent、wificompo…

C#自定义控件 生成 与 加入到项目

C#自定义控件生成 在C#中&#xff0c;自定义控件通常是通过继承现有的控件类&#xff08;如UserControl、Form等&#xff09;并添加或修改其属性和方法来实现的。以下是一个简单的示例&#xff0c;演示如何创建一个自定义控件&#xff1a; 首先&#xff0c;创建一个新的Window…

陪诊小程序成品|陪诊系统功能|陪诊小程序研发功能和流程

近年来&#xff0c;随着人们健康意识的提升和医疗行业的不断发展&#xff0c;陪诊小程序在医疗领域中扮演着越来越重要的角色。那么&#xff0c;什么是陪诊小程序&#xff1f;它具有怎样的功能和流程呢&#xff1f;本文将为您详细解读。 陪诊小程序是一种通过手机应用程序进行…

Ipython与Jupyter之间的关系

IPython 和 Jupyter 之间的关系可以从它们的历史和目标中得到很好的解释。IPython&#xff08;Interactive Python&#xff09;最初是由 Fernando Prez 于 2001 年创建的&#xff0c;旨在提升 Python 的交互式计算体验。它提供了一个强大的交互式 Python shell 和一个面向高效计…

Arduino IDE工程代码多文件编程和中文设置

一、esp8266模块信息 二、中英文切换 点击文件( File )–选择首选项( Preference )—选择语言( Language )—选择中文–点击确定( OK ) 三、多文件编程 在Arduino编程中&#xff0c;将代码分割成多个文件是一种很好的做法&#xff0c;特别是项目变得越来越大和复杂时。这样…

基于nodejs+vue基于hive旅游数据的分析与应用python-flask-django-php

系统阐述的是使用基于hive旅游数据的分析与应用系统&#xff0c;对于nodejs结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了express框架和MySql数据库技术搭建系统的整体架构。利用…

【爬虫框架pyspider】02 - pyspider 用法详解

pyspider 用法详解 前面我们了解了 pyspider 的基本用法&#xff0c;我们通过非常少的代码和便捷的可视化操作就完成了一个爬虫的编写&#xff0c;本节我们来总结一下它的详细用法。 1. 命令行 上面的实例通过如下命令启动 pyspider&#xff1a; pyspider all 命令行还有很…

如何调用occtproxy放入自己的wpf文件

1.创建一个wpf程序 2.添加项目occtproxy.vcxproj 3.把该项目配置类型设为dll 4.添加引用 5.报错显示&#xff0c;这是因为还没有生成dll 6.把occtproxy设为启动项目运行&#xff0c;设定输出目录在该目录下&#xff0c;生成dll 7.再运行&#xff0c;即可

基于Colab训练的yolov4-tiny自定义数据集(可用于OpenCV For Unity)

参考资料文档和视频。 1.打开文档,点击【文件】【在云端硬盘中保存一份副本】,即将文档复制到自己云端硬盘。 2.打开该文件,按文中提示进行。 【代码执行程序】【更改运行时类型】修改运行时为GPU(免费的GPU不好用,收费的好用,某宝上几十元就可用一个月) 步骤1) !git…

日新增百万数据clickhouse大数据解决方案记录分享

公司广告业务需求&#xff0c;需要多个维度统计每个应用的设备数&#xff0c;点击率&#xff0c;展示率&#xff0c;等相关数据&#xff0c;而且数据需要进行去重&#xff0c;我第一时间想到的是利用clickhouse来做统计&#xff0c;因为我们平台访问量比较大&#xff0c;用mysq…

STM32-01基于HAL库(CubeMX+MDK+Proteus)仿真开发环境搭建(LED点亮测试实例)

STM32-01基于HAL库&#xff08;CubeMXMDKProteus&#xff09;仿真开发环境搭建&#xff08;LED点亮测试实例&#xff09; 一、 开发工具版本列表二、安装过程三、实例测试&#xff08;点亮单个LED&#xff09;0、功能需求分析1、Proteus绘制电路原理图2、STMCubeMX 配置引脚及模…

35.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-登录成功数据包内容分析

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;34.登录数据包的…

2.3 Mac OS安装Python环境

Mac OS安装Python环境 和 Linux 发行版类似&#xff0c;最新版的 Mac OS X 也会默认自带 Python 2.x。 我们可以在终端&#xff08;Terminal&#xff09;窗口中输入python命令来检测是否安装了 Python 开发环境&#xff0c;以及安装了哪个版本&#xff0c;如下所示&#xff1…

go的for循环应该这么用

目录 目录 一&#xff1a;介绍 1: for流程控制 2&#xff1a;for-range流程控制 二&#xff1a;实例展示 1&#xff1a;//按照一定次数循环 2&#xff1a;//无限循环 3: //循环遍历整数、各种容器和通道 4&#xff1a;遍历通道 5&#xff1a;//指针数组循环 6&…