基于UDP的简易多人聊天室

1.服务器

1.服务器采用了线程进行编写,这样可以共享链表,比较简单。

2.实现思路:由客户端发送消息(内容包含:客户端自定义的名字、客户端发送消息的类别、客户端发送的消息内容),服务器接收到消息后根据消息的类别做出一下反应。

1.消息类别:登录。服务器将接收到的消息显示在服务器上,并且遍历链表发送给除改客户端的其他客户端。然后创建一个节点保存客户端的IP和端口号。

2.消息类别:发送消息。服务器将接收到的消息显示在服务器上,并且遍历链表发送给除改客户端的其他客户端。

3.消息类别:下线。服务器将接收到的消息显示在服务器上,并且遍历链表发送给除改客户端的其他客户端。当遍历到改客户端时,删除改节点。

3.当服务器要发送消息时,在子线程里面接收来自终端上的消息,遍历链表发送给所有客户端。

/*===============================================
 *   文件名称:server.c
 *   创 建 者:懒  
 *   创建日期:2022年10月09日
 *   描    述:
 ================================================*/
#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 <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <pthread.h>

typedef struct linklist{//创建存储客户端ip、端口号的链表
    struct sockaddr_in addr;
    struct linklist* next;
}l_node, *l_pnode;

typedef struct sockt_data{//创建收发的信息结构体
    char name[64];//名字
    char type[8];//消息类别
    char text[128];//信息内容
}data_t;

typedef struct Sock{//创建存储服务器发送数据的信息结构体
    struct sockaddr_in caddr; 
    data_t cdata;//发送消息结构体
    int sockfd;//套接字
}sock_t;

l_pnode create();//创建链表节点
void login(int sockfd,data_t data,struct sockaddr_in saddr);//登录
void line(int sockfd,data_t data,struct sockaddr_in saddr);//发送和下线
void *func(void *arg);//子线程

l_pnode S;//开辟一个全局变量的链表

int main(int argc, char *argv[])
{ 
    /**************** 1、创建套接字 -- socket *********************/
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    /****************2、绑定IP地址和端口号 -- bind ****************/
    struct sockaddr_in saddr, caddr;

    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(8888);//将主机字节序转换为网络字节序再赋值
    caddr.sin_addr.s_addr = htons(INADDR_ANY);

    int s_len = sizeof(saddr);
    bind(sockfd, (struct sockaddr*)&caddr, s_len);
    /**************** 3、发送接送数据 *****************************/
    data_t sdata={0,0,0};//定义接送数据结构体
    int d_len=sizeof(sdata);
    int c_len = sizeof(caddr);//计算服务器地址结构的大小
    memset(&saddr, 0,c_len);//清空接收地址结构

    S = (l_pnode)malloc(sizeof(l_node));//初始化链表
    S->next=NULL;

    sock_t csock;//创建子线程所需结构体
    csock.sockfd=sockfd;//传递套接字

    pthread_t thread;
    pthread_create(&thread,NULL,func,&csock); //创建子线程

    while(1)
    {	
        memset(&sdata, 0, d_len);//清空接送到的数据
        recvfrom(sockfd, &sdata, d_len, 0, (struct sockaddr*)&saddr, &c_len);//接送来自客户端的消息
        printf("%s:%s\n",sdata.name,sdata.text);//在服务器中显示接送到的消息
        if(strcmp(sdata.type,"login")==0)//判断接受消息内型
            login(sockfd,sdata,saddr);//上线操作
        else    
            line(sockfd,sdata,saddr);//聊天、下线操作
    }
    /**************** 4、关闭套接字 *******************************/
    close(sockfd);
    return 0;
} 
    /**************** 子线程 **************************************/
void *func(void *arg)   //线程处理函数
{
    sock_t csock=*(sock_t *)arg;//获取主函数中传递过来的内容
    strcpy(csock.cdata.type,"login");//设置要发送的结构图
    strcpy(csock.cdata.name,"服务器");
    while(1)
    {
        memset(&csock.cdata.text, 0,128);//清空输入进来的内容
        fgets(csock.cdata.text,64,stdin);//获取输入进来的内容
        csock.cdata.text[strlen(csock.cdata.text)-1] = '\0';//删除获取进来的回车'\n'
        line(csock.sockfd,csock.cdata,csock.caddr);//向所有在线的客户端发送入进来的内容
    }
}
    /**************** 上线 ***************************************/
void login(int sockfd,data_t data,struct sockaddr_in saddr)//登录
{
    int d_len = sizeof(data);
    int s_len = sizeof(saddr);
    l_pnode new = create();//创建新节点
    new->addr = saddr;//传递接送到的客户端IP、端口号
    new->next = NULL;
    l_pnode N = create();
    N=S;
    while(N->next)//尾插并发送给其他客户端
    {
        N = N->next;
        sendto(sockfd, &data, d_len, 0, (struct sockaddr*)&N->addr, s_len);//发送给除该客户信息的其他客户
    }
    N->next = new;
}
    /**************** 聊天、下线 *******************************/    
void line(int sockfd,data_t data,struct sockaddr_in saddr)//聊天、下线
{
    int d_len = sizeof(data);
    int s_len = sizeof(saddr);
    l_pnode N = create();
    N=S;
    while(N->next)
    {
        if((N->next->addr.sin_addr.s_addr==saddr.sin_addr.s_addr)
                && (N->next->addr.sin_port==saddr.sin_port))//判断是否存在该客户端数据
        {
            if(strcmp(data.type,"line")==0)//下线,删除用户信息
            {
                l_pnode Q = N->next;
                N->next = Q->next;//连线
                free(Q);//释放空间
                Q = NULL;
            }
            if(strcmp(data.type,"send_t")==0)//聊天,跳过用户
                N = N->next;
        }
        if(N->next != NULL)//判断是否到尾节点
        {
            N = N->next;
            sendto(sockfd, &data, d_len, 0, (struct sockaddr*)&N->addr, s_len);//发送给除该客户信息的其他客户
        }
        else
            break;
    }
}
    /**************** 创建链表新节点 ******************************/
l_pnode create()
{
    l_pnode S = (l_pnode)malloc(sizeof(l_node));
    S->next = NULL;
    return S;
}

2.客户端

1.客户端采用了进程进行编写,由子进程接收来自终端的消息发送出去,由父进程来接收消息。当运行程序时。

1.要求需要输入用户名,输入用户名后会向服务器发送(用户名、消息类别:登录、内容:上线了)。

2.客户端发送消息(用户名、消息类别:聊天、内容:消息内容)。

2.客户端下线,需要用户输入“line!!!”(用户名、消息类别:下线、内容:下线了)。接受程序。

2.当接受来自服务器的消息,将消息里的名字和内容打印出来

/*===============================================
 *   文件名称:client.c
 *   创 建 者:懒  
 *   创建日期:2022年10月09日
 *   描    述:
 ================================================*/
#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 <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

typedef struct sockt_data{//自定义结构体用于存放发送、接送到的信息
    char name[64];
    char type[8];
    char text[128];
}data_t;

void mysignal(int arg)//回收子进程
{
    wait(NULL);
    exit(0);
}

void login(int sockfd,data_t cdata,struct sockaddr_in caddr);//登录
void send_t(int sockfd,data_t data,struct sockaddr_in saddr);//发送信息
void line(int sockfd,data_t cdata,struct sockaddr_in caddr);//下线

int main(int argc, char *argv[])
{ 
if(argc!=2)
printf("请正确输入: ./client IP号\n");
    /**************** 1、创建套接字 -- socket *********************/
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    /****************2、绑定IP地址和端口号 -- bind ****************/
    struct sockaddr_in saddr, caddr;
    caddr.sin_family = AF_INET;
    caddr.sin_port = htons(8888);//将主机字节序转换为网络字节序再赋值
    caddr.sin_addr.s_addr = inet_addr(argv[1]);

    int s_len = sizeof(saddr);
    /**************** 3、发送接送数据 *****************************/
    data_t cdata={0,0,0},sdata={0,0,0};//定义收发数据结构体

    int d_len=sizeof(cdata);//计算数据结构体长度

    memset(&saddr, 0,s_len);//清空接收地址结构
    memset(&cdata, 0,d_len);//清空接收地址结构
    signal(SIGCHLD, mysignal);//捕获子进程释放信号

    pid_t pid=fork();//创建线程
    if(0==pid)//子线程
    {
        printf("请输入你的用户名--->");
        fgets(cdata.name,64,stdin);//获取输入进来的名字
        cdata.name[strlen(cdata.name)-1] = '\0';//删除获取进来的回车'\n'
        login(sockfd,cdata,caddr);//登录

        while(1)
        {
            fgets(cdata.text,64,stdin);//获取输入进来的内容
            cdata.text[strlen(cdata.text)-1] = '\0';//删除获取进来的回车'\n'
            if(strcmp(cdata.text,"line!!!")==0) //判断是否结束聊天
                line(sockfd,cdata,caddr);//下线       
            send_t(sockfd,cdata,caddr);//聊天
        }
    }

    while(1)
    {	
        memset(&sdata, 0, d_len);//清空接送到的数据
        recvfrom(sockfd, &sdata, d_len, 0, (struct sockaddr*)&saddr, &s_len);
        if(strcmp(sdata.type,"send_t")==0)//在客户端中显示接送到的消息
            printf("%s:%s\n",sdata.name,sdata.text);
        else    
            printf("%s-->%s\n",sdata.name,sdata.text);
    }
    /**************** 4、关闭套接字 *******************************/
    close(sockfd);
    return 0;
} 
    /**************** 登录 ***************************************/
void login(int sockfd,data_t cdata,struct sockaddr_in caddr)//登录
{
    int d_len = sizeof(cdata);
    int s_len = sizeof(caddr);
    strcpy(cdata.type,"login");
    strcpy(cdata.text,"上线了");
    sendto(sockfd, &cdata, d_len, 0, (struct sockaddr*)&caddr, s_len);         
}
    /**************** 下线 ***************************************/    
void line(int sockfd,data_t cdata,struct sockaddr_in caddr)//下线
{
    int d_len = sizeof(cdata);
    int s_len = sizeof(caddr);
    strcpy(cdata.type,"line");
    strcpy(cdata.text,"下线了");
    sendto(sockfd, &cdata, d_len, 0, (struct sockaddr*)&caddr, s_len);
    exit(0);
}
    /**************** 聊天 ***************************************/    
void send_t(int sockfd,data_t cdata,struct sockaddr_in caddr)//发送信息
{
    int ret=0;
    int d_len = sizeof(cdata);
    int s_len = sizeof(caddr);
    strcpy(cdata.type,"send_t");
    ret = sendto(sockfd, &cdata, d_len, 0, (struct sockaddr*)&caddr, s_len);
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值