UDP聊天室

1.头文件

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

#include 
#include           /* See NOTES */
#include 
#include 
#include   
#include 
#include 
#include 
#include 
#include 
#include 

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.结果

UDP聊天室_第1张图片

你可能感兴趣的:(udp,网络协议,网络)