UDP 协议的多人聊天室(linux C)

基于UDP多用户聊天系统
总体思想:
先是创建一个链表用来存放连入服务器的sockaddr_in信息,有人登录加入链表节点,有人下线删除节点。
创建一个类似消息队列结构的结构体,用来存放用户的当前状态的类型、名字、消息。
将每个UDP客户端连接服务器后将其的sockaddr_in信息加入到链表中,通过数据的类型去判断服务器去执行哪个函数(登录广播、转发聊天信息、下线广播)。
1、UDP服务器部分
第一点 运行服务器会fork()一个子进程,该子进程用于服务器本身发送聊天的内容,父进程用于接收客户端的消息。
第二点 服务器通过接收的信息去判断类型(‘L’ ‘C’ ‘Q’)执行相应的处理函数。
第三点 处理函数 type = ‘L’ 会向当前链表中除自己以外的所有成员广播 name上线,随后将其加入到链表中。
type = ‘C’ 会向当前链表中除自己以外的所有成员广播消息内容。
type = ‘Q’ 首先是通知所有用户name下线,在链表中将其删除。
2、客户端部分
第一点 运行客户端程序,首先是将消息类型设置成’L’,令服务器广播告知其他成员该用户上线。
第二点 随后创建一个子进程,该进程用于发送聊天的消息,父进程用于接收服务器转发的其他用户发过来的消息。
第三点 当子进程判断输入的消息是"quit"将数据类型改成’Q’,以便服务器将其在链表中删除,并告知其他用户某人下线。

明白大概思路了 ,直接上代码:

服务器部分:server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define  N  128

typedef struct node 
{
    struct sockaddr_in  addr; //ip 地址
    struct node * next;
}linklist_t;   //链表

typedef struct msg
{
    char type;
    char name[N];
    char text[N];
}msg_t;

linklist_t * create_empty_linkist(void)
{
    linklist_t * h = (linklist_t *)malloc(sizeof(linklist_t)) ;
    h->next = NULL;
    memset(&h->addr,0,sizeof(h->addr));
    return h;
}

int process_login(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{
    linklist_t * p = h->next; 
    int ret ;
    sprintf(msgp->text,"%s login",msgp->name);
    while(p != NULL)
    {
        if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 )
        {
            ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));
            if(ret  < 0)
            {
                printf("sendto");
                exit(-1);
            }
        }
        p = p->next;
    }
    p = (linklist_t * )malloc(sizeof(linklist_t)) ; //倒叙插入法
    p->next = h->next;
    h->next = p ; 
    p->addr = *addr;

    return 0;
    
}
int process_chat(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{
    linklist_t * p = h->next; 
    char buf[128] = {0};
    int ret ;
    sprintf(buf,"%s said %s",msgp->name,msgp->text);
    strcpy(msgp->text,buf);
    while(p != NULL)
    {
        if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 )
        {
            ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));
            if(ret  < 0)
            {
                printf("sendto");
                exit(-1);
            }
        }
        p = p->next;
    }
    return 0;
    
}
int process_quit(linklist_t * h,int sockfd,struct sockaddr_in *addr,msg_t *msgp)
{
    linklist_t * p = h->next; 
    linklist_t * q;
    char buf[128] = {0};
    int ret ;
    sprintf(msgp->text,"%s offline",msgp->name);
    while(p != NULL)
    {
        if(memcmp(&p->addr,addr,sizeof(*addr)) != 0 )
        {
            ret = sendto(sockfd,msgp,sizeof(msg_t),0,(struct sockaddr *)&p->addr,sizeof(p->addr));
            if(ret  < 0)
            {
                printf("sendto");
                exit(-1);
            }
        }
        p = p->next;
    }
    p = h ; 
    while(p->next != NULL)
    {
        if(memcmp(&p->next->addr,addr,sizeof(*addr)) == 0 )
        {
            q = p->next;
            p->next = p->next->next;
            free(q);
            break ;
        }
        p = p->next;
    }
    return 0;
    
}

int main(int argc, const char *argv[])
{
    char buf[N] ={0};
    int sockfd,ret,newsockfd;
    struct sockaddr_in myaddr,client;
    pid_t pid;
    msg_t msg;
    memset(&msg,0,sizeof(msg));
    if(argc != 3)
    {
        printf("运行程序时带入ip 和 port\n");
        exit(-1);
    }
    socklen_t addrlen = sizeof(client);
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    printf("sockfd=%d\n",sockfd);
    memset(&myaddr,0,sizeof(myaddr));
    myaddr.sin_family = AF_INET ;
    myaddr.sin_port   = htons(atoi(argv[2]));
    myaddr.sin_addr.s_addr   = inet_addr(argv[1]); 
    ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr)) ; // 给socket 一个固定的ip 和端口 
    if(ret < 0)
    {
        perror("bind");
        exit(-1);
    }
    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid == 0 ) // 发送广播
    {
        msg.type = 'C';
        while(1)
        {
            printf(">:");
            fgets(msg.text,N,stdin);
            strcpy(msg.name,"server");
            ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&myaddr,addrlen) ;
            if(ret < 0)
            {
                perror("write");
                exit(-1);
            }
            memset(buf,0,N);
        }
        
    }
    else // parent 
    {
        linklist_t * H = create_empty_linkist();
        while(1)
        {
            ret = recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&client,&addrlen);
            if(ret  < 0)
            {
                printf("read");
                exit(-1);
            }
            printf("msg.type = %c\n",msg.type);
            printf("msg.name = %s\n",msg.name);
            printf("msg.text = %s\n",msg.text);
            switch(msg.type)
            {
                case 'L':
                    process_login(H,sockfd,&client,&msg);
                    break ; 
                case 'C': 
                    process_chat(H,sockfd,&client,&msg);
                    break ;
                case 'Q':
                    process_quit(H,sockfd,&client,&msg);
                    break ; 
                default:
                    break ;
            }
#if 0
            ret = sendto(sockfd,buf,N,0,(struct sockaddr *)&client,addrlen);
            if(ret  < 0)
            {
                printf("sendto");
                exit(-1);
            }
#endif

        }


    }

    
    return 0;
}

客户端部分:client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define  N  128 

typedef struct msg
{
    char type;
    char name[N];
    char text[N];
}msg_t;

int main(int argc, const char *argv[])
{
    char buf[N] = {0};
    int sockfd,ret,newsockfd;
    struct sockaddr_in server,client;
    pid_t pid;
    msg_t msg;
    memset(&msg,0,sizeof(msg)) ;
    if(argc != 3)
    {
        printf("运行程序时带入ip 和 port\n");
        exit(-1);
    }
    socklen_t addrlen = sizeof(client);
    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    printf("sockfd=%d\n",sockfd);
    memset(&server,0,sizeof(server));
    server.sin_family = AF_INET ;
    server.sin_port   = htons( atoi(argv[2]) );
    server.sin_addr.s_addr   = inet_addr(argv[1]); 

    msg.type = 'L';
    printf("input your name >:");
    fgets(msg.name,N,stdin);
    msg.name[strlen(msg.name) -1 ] = 0 ; 
    ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;
    if(ret < 0)
    {
        perror("write");
        exit(-1);
    }

    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid == 0)
    {
        msg.type = 'C';
        while(1)
        {
            printf(">:");
            fgets(msg.text,N,stdin);
            if(strncmp(msg.text,"quit",4) == 0 )
            {
                msg.type = 'Q';
                ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;
                if(ret < 0)
                {
                    perror("write");
                    exit(-1);
                }
                kill(getppid(),SIGUSR1);
                exit(0);
            }
            ret  = sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&server,addrlen) ;
            if(ret < 0)
            {
                perror("write");
                exit(-1);
            }
        }
        
    }
    else 
    {
        while(1)
        {
            ret = recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL);
            if(ret  < 0)
            {
                printf("read");
                exit(-1);
            }
            printf("%s\n",msg.text);

        }

    }
    close(sockfd);


    
    return 0;
}

Makefile

all:
	gcc server.c -o server 
	gcc client.c -o client

clean:
	rm -rf server client

s:
	./server 192.168.1.57 9000  

c:
	./client 192.168.1.57 9000 
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于UDP多人聊天设计可以通过以下步骤来实现。 首先,进行网络连接的建立。在客户端,创建一个UDP套接字,并指定服务器的IP地址和端口号,然后将客户端的套接字绑定到一个固定的本地IP地址和端口。在服务器端,创建一个UDP套接字,并将服务器端的套接字绑定到一个指定的IP地址和端口。 接下来,客户端可以向服务器发送连接请求。客户端发送一个连接请求数据包到服务器,并等待服务器的响应。如果服务器接受了连接请求,则返回一个确认连接的响应给客户端。 在聊天过程中,客户端可以通过套接字发送消息给服务器,服务器将接收到的消息转发给所有其他客户端。客户端可以通过接收套接字接收消息,并将其显示在用户界面上。 为了实现多人聊天功能,服务器需要维护一个客户端列表,用于记录所有连接到服务器的客户端。当有新的客户端连接到服务器时,服务器将将其添加到客户端列表中。当服务器接收到一个消息时,它将遍历客户端列表,并将消息发送给每个客户端。 为了确保消息的可靠传递,可以在发送消息时添加一些消息头,包含发送者的信息、消息的类型等。接收方可以根据消息头来解析消息,并作出相应的处理。 此外,为了实现并发处理,可以使用多线程的方式。每个客户端连接到服务器时,服务器将创建一个新的线程来处理该客户端的消息。这样可以避免阻塞其他客户端的消息传递。 总结起来,基于UDP多人聊天设计包括网络连接建立、消息传递、客户端列表维护、并发处理等步骤,通过这些步骤的实现,可以实现一个简单的基于UDP多人聊天系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值