UDP聊天室

news2025/1/19 19:16:30

1.头文件

/*===============================================
*   文件名称:UDP.h
*   创 建 者:crx    
*   创建日期:2023年09月3日
*   描    述:
================================================*/
#ifndef _UDP_H
#define _UDP_H

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>  
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

typedef struct node{            //链表节点保存用户地址结构体信息
    struct sockaddr_in caddr;  
    struct node *next;   
}Node,*Pnode;

typedef struct mesg{          //用户状态、姓名、消息
    char state;  
    char name[20];  
    char text[60];  
}Mesg;

enum state{         //状态:登录、转发、下线
    Login,  
    Relay,   
    Quit,   
};

//创建头节点
Pnode create_node();
//插入,登录
int insert_node(Pnode p,struct sockaddr_in caddr,Mesg msg);
//转发
void relay(int sockfd,Pnode p,struct sockaddr_in caddr,Mesg msg);
//删除,下线
int delete_node(Pnode p,struct sockaddr_in caddr,Mesg msg);

#endif

2.服务器

/*===============================================
 *   文件名称:UDPs.c
 *   创 建 者:crx     
 *   创建日期:2023年09月3日
 *   描    述:
 ================================================*/
#include "UDP.h"

int main(int argc, char *argv[])
{

    //1.创建套接字
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    //2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8181);
    saddr.sin_addr.s_addr = INADDR_ANY;
    int bindfd = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(-1 == bindfd)
    {
        perror("bind");
        return -1;
    }

    //3.准备保存客户端地址结构体,等待登录
    printf("等待登录....\n");
    struct sockaddr_in caddr;               
    socklen_t addrlen = sizeof(caddr);
    Mesg msg;

    //4.创建头节点
    Pnode p = create_node();    
    Pnode q = p;
    Pnode temp = p;

    while(1)
    {

        memset(&caddr,0,sizeof(caddr));  

        //5.接收用户登录信息
        recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&caddr,&addrlen); 

       //6.根据用户状态执行对应操作

//msg.state = Login
//用户登录,链表插入用户信息
//判断链表是否为空,为空则插入用户
//链表不为空,判断是否是链表中已有用户,不是则插入用户信息
//转发登录消息给其他在线用户

        if(msg.state == Login)     
        {
            if(NULL == q->next)
            {           
                insert_node(p,caddr,msg);   
                strcpy(msg.text,"已登录!");
            }
            else
            {   
                temp = p->next;
                while(temp)
                {        
                    if(temp->caddr.sin_port == caddr.sin_port && temp->caddr.sin_addr.s_addr == caddr.sin_addr.s_addr)
                        break;        
                    else
                        temp = temp->next;
                }
                if(NULL == temp)
                {
                    insert_node(p,caddr,msg);
                    strcpy(msg.text,"已登录!");
                    relay(sockfd,p,caddr,msg);
                }
            }
        }

//msg.state = Relay
//转发用户信息给其他在线用户

        if(msg.state == Relay)   
        {
            relay(sockfd,p,caddr,msg);
        }

//msg.state = Quit
//用户下线
//链表中删除用户信息
//转发用户下线信息给其他用户

        if(msg.state == Quit)        
        {
            delete_node(p,caddr,msg);
            strcpy(msg.text,"已下线");
            relay(sockfd,p,caddr,msg);
        }
    }

    return 0;
} 

//创建头节点
Pnode create_node()
{            
    Pnode p = (Pnode)malloc(sizeof(Node));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    p->next = NULL;
    return p;
}

//插入,登录
int insert_node(Pnode p,struct sockaddr_in caddr,Mesg msg)
{ 
    if(NULL == p)
    {
        return -1;
    }
    Pnode new = (Pnode)malloc(sizeof(Node));
    if(NULL == new)
    {
        perror("malloc");
        return -2;
    }
    new->next = p->next;
    p->next = new;
    new->caddr = caddr;
    printf("已登录!name:%s,ip:%s,port:%d\n",msg.name,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));

    return 1;
}

//转发
void relay(int sockfd,Pnode p,struct sockaddr_in caddr,Mesg msg)        {
    while(p->next)
    {    
        p = p->next;
        if(p->caddr.sin_port == caddr.sin_port && p->caddr.sin_addr.s_addr == caddr.sin_addr.s_addr)
        {
            continue;    
        }
        else
        {
            sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->caddr),sizeof(p->caddr)); 
        }
    }
}

//删除,下线
int delete_node(Pnode p,struct sockaddr_in caddr,Mesg msg)  
{
    if(NULL == p)
    {
        return -1;
    }
    while(p->next)
    {
        if(memcmp(&(p->next->caddr),&caddr,sizeof(caddr)) == 0)
        {
            Pnode q = p->next;
            p->next = q->next;
            free(q);
            break;
        }
        else
        {
            p = p->next;
        }
    }
    printf("已下线!name:%s,ip:%s,port:%d\n",msg.name,inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
}

3.客户端

/*===============================================
 *   文件名称:UDPc.c
 *   创 建 者:crx     
 *   创建日期:2023年09月3日
 *   描    述:
 ================================================*/
#include "UDP.h"

int main(int argc, char *argv[])
{
    //1.创建套接字
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        return -1;
    }

    //2.服务器地址
    struct sockaddr_in saddr;   
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8181);
    saddr.sin_addr.s_addr = inet_addr("192.168.17.225");

    //3.创建用户
    Mesg msg;    

    //4.运行后触发msg.state = Login
    //登录填写用户名
    //发送给服务器登录信息转发登录消息操作
    printf("登录\n");   
    msg.state = Login;
    printf("请输入用户名:\n");
    fgets(msg.name,20,stdin);
    printf("******************************************\n");
    if(msg.name[strlen(msg.name)-1] == '\n')
        msg.name[strlen(msg.name) -1] = '\0';

    //发送用户登录消息
    if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr))) 
    {
        perror("sendto");
        return -1;
    }
   
    //5.创建子进程
    pid_t pid = fork();

    //6.子进程循环接收其他用户消息并打印发送人及信息
    if(pid == 0)  
    {
        while(1)
        {
            socklen_t addrlen = sizeof(saddr);
            recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,&addrlen);
            printf("~%s~ : %s\n",msg.name,msg.text);
        }
    } 
    else
    {
        //7.父进程获取用户终端输入到用户消息中
        while(1)
        {
            fgets(msg.text,sizeof(msg.text),stdin);    
            if(msg.text[strlen(msg.text)-1] == '\n')
                msg.text[strlen(msg.text) -1] = '\0';

         //8.处理终端输入

//终端输入Quit则触发msg.state = Quit
//发送给服务器下线信息执行对应操作
//使用SIGKILL强制结束进程

            if(strcmp(msg.text,"Quit") == 0)  
            {
                msg.state = Quit;
                if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr)))
                {
                    perror("sendto");
                    return -1;
                }
                kill(pid,SIGKILL); 
                wait(NULL);
                exit(0);
            }
//终端输入不是Quit则触发msg.state = Relay
//发送给服务端执行转发操作
            
            else
            {
                msg.state = Relay;  
            }
            if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&saddr,sizeof(saddr)))
            {
                perror("sendto");
                return -1;
            }
        }
    }
    close(sockfd);

    return 0;
}

4.makefile

all:UDPs UDPc
UDPs:UDPs.c
	gcc  UDPs.c -o UDPs
UDPc:UDPc.c
	gcc  UDPc.c -o UDPc
clean:
	rm UDPs UDPc

5.结果

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

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

相关文章

Day53|leetcode 1143.最长公共子序列、1035.不相交的线、53. 最大子序和

leetcode 1143.最长公共子序列 题目链接&#xff1a;1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划子序列问题经典题目 | LeetCode&#xff1a;1143.最长公共子序列_哔哩哔哩_bilibili 题目概述 给定两个字符串 text1 和 text2&…

【Flutter】Flutter 使用 percent_indicator 实现基于百分比显示进度

【Flutter】Flutter 使用 percent_indicator 实现基于百分比显示进度 文章目录 一、前言二、安装和基本使用三、圆形百分比指示器四、线性百分比指示器五、完整示例六、总结 一、前言 今天我要为你介绍一个非常实用的Flutter包——percent_indicator。这个包允许我们基于百分比…

STM32f103入门(9)编码器接口测速

TIM3 PA6 PA7 上拉输入 原理上也是PWM捕获输入 捕获两个输入 我们用中断处理读取CNT的值 读取完将CNT置0 这样我们就得到了旋转编码器的速度/s 中断配置代码 #include "stm32f10x.h" // Device headervoid Timer_Init(void) {RCC_APB1PeriphClockC…

【Linux-Day9-进程间通信】

进程间通信 前提引入&#xff1a; 我们之前接触过管道 | 将前一个命令的输出结果作为后一个命令的输入 如&#xff1a;ps | grep “sleep” 其实就是将 ps 的结果写入到一个区域&#xff0c;在从这个区域读出目标数据 有名管道 mkfifo 创建管道文件 : mkfifo fifo ->创…

verilator——牛刀小试

verilator——牛刀小试 安装verilator可见&#xff1a;https://blog.csdn.net/qq_40676869/article/details/132648522?spm1001.2014.3001.5501 正文开始 编写一个异或的电路模块如下&#xff1a; top.v module top(input a,input b,output f );assign f a ^ b; endmodul…

[华为云云服务器评测] 华为云耀云服务器 Java、node环境配置

系列文章目录 第一章 [linux实战] 华为云耀云服务器L实例 Java、node环境配置 文章目录 系列文章目录前言一、任务拆解二、修改密码三、配置安全规则四、远程登录并更新apt五、安装、配置JDK环境5.1、安装openjdk,选择8版本5.2、检查jdk配置 六、安装、配置git6.1、安装git6.2…

【从0学习Solidity】合约入门 Hello Web3

【学习Solidity的基础】入门智能合约开发 Hello Web3 &#x1f4f1;不写代码没饭吃上架主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是全栈开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索全栈开发的无限…

torch.bmm功能解读

bmm 是 batch matrix multiple 的简写&#xff0c;即批量矩阵乘法&#xff0c;矩阵是二维的&#xff0c;加上batch一个维度&#xff0c;因此该函数的输入必须是两个三维的 tensor&#xff0c;三个维度代表的含义分别是&#xff1a;&#xff08;批量&#xff0c;行&#xff0c;列…

递归与递推

会独立敲一遍代码并debug&#xff08;1&#xff0c;3题较难&#xff1b;2&#xff0c;4题较简单&#xff09; 部分题需要买课&#xff0c;可到洛谷或其他OJ找原题 目录 &#x1f4d5;空间复杂度(计算方法) &#x1f33c;1&#xff0c;费解的开关 &#x1f33c;2&#xff0c;…

图神经网络教程之HAN-异构图模型

异构图 包含不同类型节点和链接的异构图 异构图的定义&#xff1a;节点类别数量和边的类别数量加起来大于2就叫异构图。 meta-path元路径的定义&#xff1a;连接两个对象的复合关系&#xff0c;比如&#xff0c;节点类型A和节点类型B&#xff0c;A-B-A和B-A-B都是一种元路径。 …

[C++] STL_list常用接口的模拟实现

文章目录 1、list的介绍与使用1.1 list的介绍1.2 list的使用 2、list迭代器3、list的构造4、list常用接口的实现4.1 list capacity4.2 插入删除、交换、清理4.2.1 insert任意位置插入4.2.2 push_front头插4.2.3 push_back尾插4.2.4 erase任意位置删除4.2.5 pop_front头删4.2.6 …

Keil 编译 Debug

# 头文件无法导入进来 # 导入头文件&#xff0c;只有函数声明&#xff0c;但缺少函数实现 已经导入了air32f10x_gpio.h但是没有导入 .c&#xff0c;就导致 编译出错出现undefined symbol (某个函数)&#xff0c;这时候按照下面的操作&#xff0c;导入外设模块就好。

PQUEUE - Printer Queue

题目描述 The only printer in the computer science students union is experiencing an extremely heavy workload. Sometimes there are a hundred jobs in the printer queue and you may have to wait for hours to get a single page of output. Because some jobs are …

pip切换源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

CLFS信息泄露漏洞CVE-2023-28266分析

引用 这篇文章的目的是介绍今年4月发布的CLFS信息泄露漏洞CVE-2023-28266分析. 文章目录 引用简介CVE-2023-28266漏洞分析CVE-2023-28266调试过程漏洞复现相关引用参与贡献 简介 文章结合了逆向代码和调试结果分析了CVE-2023-28266漏洞利用过程和漏洞成因. CVE-2023-28266漏洞…

两个线程同步执行:解决乱箭穿心(STL/Windows/Linux)

C自学精简教程 目录(必读) C并发编程入门 目录 多线程同步 线程之间同步是指线程等待其他线程执行完某个动作之后再执行&#xff08;本文情况&#xff09;。 线程同步还可以是像十字路口的红绿灯一样&#xff0c;只允许一个方向的车同行&#xff0c;其他方向的车等待。 本…

UART串口Shell软硬件模型分析总结

文章目录 层次一、最底层逻辑配置交互----如何从Uart硬件读写单个字节数据层次二、抽象串口软件模块交互----基于串口对接输入输出流 和 Printf适配层次三、类似Shell封装抽象交互----基于串口交互命令行界面&#xff08;命令解析、补全、修改、记录&#xff09;case1 依次输入…

自建音乐服务器Navidrome之一

这里写自定义目录标题 1.1 官方网站 2. Navidrome 简介2.1 简介2.2 特性 3. 准备工作4. 视频教程5. 界面演示5.1 初始化页5.2 专辑页 前言 之前给大家介绍过 Koel 音频流服务&#xff0c;就是为了解决大家的这个问题&#xff1a;下载下来的音乐&#xff0c;只能在本机欣赏&…

上海的正西边有哪些城市

背景 上海一路向西&#xff0c;来一趟拉萨之行&#xff0c;那么上海出现&#xff0c;所经过的那么多城市&#xff0c;哪些是在上海的正西边呢&#xff1f; 画一幅地图 基于这个背景需求&#xff0c;我们需要拿来一幅地图&#xff0c;一看便知。下面的python代码生成了一幅地…

通信原理板块——平稳随机过程

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、平稳随机过程的定义 (1)严平稳随…