C语言利用UDP实现群聊聊天室的示例代码

 更新时间:2022年08月18日 16:00:30   作者:lhb2998658795  
UDP是一个轻量级、不可靠、面向数据报的、无连接的传输层协议,多用于可靠性要求不严格,不是非常重要的传输,如直播、视频会议等等。本文将利用UDP实现简单的群聊聊天室,感兴趣的可以了解一下

1.UDP群聊的功能

有新用户登录,其他在线的用户可以收到登录信息

有用户群聊,其他在线的用户可以收到群聊信息

有用户退出,其他在线的用户可以收到退出信息

服务器可以发送系统信息

2.写项目的流程

画流程图

根据流程图写框架

一个功能一个功能实现

3.流程图

4.代码实现

4.1头文件

#ifndef __MYHEAD_H__
#define __MYHEAD_H__
 
#include <head.h>
#define N 512
//聊天操作用的结构体
typedef struct _MSG{
    char ch;//用来'l'聊天,'q'退出,'登录d'
    char name[128];//存名字
    char text[N];//存聊天内容
}msg_t;
//用来保存每个用户信息的结构体
typedef struct _Jilu{
    struct sockaddr_in addr;
    struct _Jilu *next;
}jilu_t;
 
 
int create_head(jilu_t **head);
int input_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
int wx_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
int tuichu_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
#endif

4.2函数

#include "myhead.h"
 
//创建一个单链表头
int create_head(jilu_t **head)
{   
    if(head==NULL){
        printf("传送错误,请检查\n");
        return -1;
    }
 
    (*head)=(jilu_t *)malloc(sizeof(jilu_t));
    (*head)->next=NULL;
 
    return 0;
 
}
 
//记录登录的用户的信息
int input_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
    if(head==NULL){
        printf("传送错误,请检查\n");
        return -1;
    }
 
    //将这个用户登录的信息发送给所有人
    snprintf(msg.text,sizeof(msg.text),"[%s]%s",msg.name,"登录了");
    //这个用来记录头的地址
    jilu_t *jilu_head=head;
 
    while(jilu_head->next!=NULL){
        jilu_head=jilu_head->next;
        if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
            ERRLOG("将用户登录信息发给所有人失败");
        }
    }
    //创建一个新的节点,并且把新的用户信息放入新得单列表
    jilu_t *temp=NULL;
    create_head(&temp);
    temp->addr=clientaddr;
 
    //用头插法将用户信息插入链表
    temp->next=head->next;
    head->next=temp;
 
    return 0;
}
 
int wx_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
    
    if(head==NULL){
        printf("传送错误,请检查\n");
        return -1;
    }
 
    //将接受到的消息发给除了自己以外的所有人
    jilu_t *jilu_head=head;
    while(jilu_head->next!=NULL){
        jilu_head=jilu_head->next;
        if(0!=memcmp(&(jilu_head->addr),&clientaddr,sizeof(clientaddr))){     
            if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
                ERRLOG("将聊天内容发给所有人失败");
            }
        }
    }
 
    return 0;
}
int tuichu_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
    if(head==NULL){
        printf("传送错误,请检查\n");
        return -1;
    }
 
    snprintf(msg.text,sizeof(msg.text),"%s%s",msg.name,"退出登录");
 
    jilu_t *jilu_head=head;
    jilu_t *pdel=NULL;
    
    while(jilu_head->next!=NULL){
        
        if(0==memcmp(&(jilu_head->next->addr),&clientaddr,sizeof(clientaddr))){
            pdel=jilu_head->next;
            jilu_head->next=pdel->next;
            free(pdel);
            pdel=NULL;
        }else{
            jilu_head=jilu_head->next;
            if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
                ERRLOG("将这个退出的信息告诉所有人失败");
            }
        }
    }
 
   
    return 0;
}

4.3服务器

#include "myhead.h"
 
int main(int argc, char const *argv[])
{
    int sockfd=0;
    pid_t pid=0;
    msg_t msg;//用来进行各种操作
    msg_t faso;//用来发系统消息
 
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1){
        ERRLOG("创建服务器套接字失败");
    }
 
    //将网络信息结构体放入服务器中
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
    socklen_t serveraddr_len=sizeof(serveraddr);
 
    //将套接字与网络信息结构体绑定
    if(bind(sockfd,(struct sockaddr*)&serveraddr,serveraddr_len)==-1){
        ERRLOG("将套接字与网络信息结构体绑定失败");
    }
 
    //创建一个新的网络信息结构体来存客户端的信息
    struct sockaddr_in clientaddr;
    clientaddr.sin_family=AF_INET;
    socklen_t clientaddr_len=sizeof(clientaddr);
    
    //创建进程
    pid=fork();
    if(pid==-1){
        ERRLOG("服务器创建进程失败");
    }else if(pid==0){
        //创建一个单列表保存网络信息结构体
        jilu_t *head;
        create_head(&head);
        memset(&msg,0,sizeof(msg));
        while(1){
            if(recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,&clientaddr_len)==-1){
                ERRLOG("接受客户端传来的信息失败");
            }          
             switch(msg.ch){
                case 'd'://登录信息
                    input_addr(head,msg,sockfd,clientaddr,clientaddr_len);
                    //head->next=NULL; //这个用来测试用的                  
                    break;
                case 'l'://聊天信息
                    wx_addr(head,msg,sockfd,clientaddr,clientaddr_len);
                    break;
                case 'q'://退出信息
                    tuichu_addr(head,msg,sockfd,clientaddr,clientaddr_len);
                    break;
            }
        }
    }else{
        while(1){
            //发系统消息
            memset(&faso,0,sizeof(faso));
            fgets(faso.text,sizeof(faso.text),stdin);
            faso.text[strlen(faso.text)-1]='\0';
            faso.ch='l';
            sprintf(faso.name,"%s","系统消息");
 
            if(sendto(sockfd,&faso,sizeof(faso),0,(struct sockaddr*)&serveraddr,serveraddr_len)==-1){
                ERRLOG("发送系统消息失败");
            }
        }
    }
    return 0;
}

4.4客户端

#include "myhead.h"
 
 
int main(int argc, char const *argv[])
{
 
    //判断输入的对不对
    if(argc!=3){
        printf("输入格式错误,./a.out ip port\n");
        exit(EXIT_SUCCESS);
    }
 
    int sockfd=0;
    pid_t pid=0;
    msg_t msg;//创建发送用户的信息
    
 
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1){
        ERRLOG("创建客户端套接字失败");
    }
 
    //将客户端网络信息结构体进行绑定
    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));
    clientaddr.sin_family=AF_INET;
    clientaddr.sin_port=htons(atoi(argv[2]));
    clientaddr.sin_addr.s_addr=inet_addr(argv[1]);
    socklen_t clientaddr_len=sizeof(clientaddr);
 
    //输入用户的姓名进行登录操作
    msg.ch='d';
    printf("请输入你用来登录的姓名");
    fgets(msg.name,sizeof(msg.name),stdin);
    msg.name[strlen(msg.name)-1]='\0';
 
    //给服务器发送用户已经登录上的操作
    if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,clientaddr_len)==-1){
        ERRLOG("客户端给服务器发送的登录信息失败");
    }
    
    //创建进程,子进程用来接受,父进程用来发送
    pid=fork();
    if(pid==-1){
        ERRLOG("客户端创建进程失败");
    }else if(pid==0){
        //用来接受服务器发来的消息
        while(1){
            memset(&msg,0,sizeof(msg));
            if(recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL)==-1){
                ERRLOG("接受服务器发来的信息错误");
            }
 
            printf("[%s]>>(%s)\n",msg.name,msg.text);
        }
    }else{
        //写入要判聊天的内容
        while(1){
            memset(msg.text,0,sizeof(msg.text));
            fgets(msg.text,sizeof(msg.text),stdin);
            msg.text[strlen(msg.text)-1]='\0';
 
            if(strncmp("quit",msg.text,5)==0){
                msg.ch='q';
            }else{
                msg.ch='l';
            }
            
            //将写好的内容发送给服务器
            if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,clientaddr_len)==-1){
                ERRLOG("将聊天内容发送给服务器失败");
            }
 
            //当识别到停止的时候,关闭进程
            if(strncmp("quit",msg.text,5)==0){
                kill(pid,SIGKILL);
                close(sockfd);
                exit(EXIT_SUCCESS);
            }
        }
    }   
 
    return 0;
}

到此这篇关于C语言利用UDP实现群聊聊天室的示例代码的文章就介绍到这了,更多相关C语言 UDP聊天室内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中的移动构造函数及move语句示例详解

    C++中的移动构造函数及move语句示例详解

    这篇文章主要给大家介绍了关于C++中移动构造函数及move语句的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • C++实现LeetCode(312.打气球游戏)

    C++实现LeetCode(312.打气球游戏)

    这篇文章主要介绍了C++实现LeetCode(312.打气球游戏),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言实现学生宿舍管理系统

    C语言实现学生宿舍管理系统

    这篇文章主要为大家详细介绍了C语言实现学生宿舍管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++中SetConsoleCursorPosition()移动光标函数的用法大全

    C++中SetConsoleCursorPosition()移动光标函数的用法大全

    这篇文章主要介绍了C++中SetConsoleCursorPosition()移动光标函数的用法大全,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • C++ 程序员为什么看不起php程序员

    C++ 程序员为什么看不起php程序员

    由于当今市场状况,各种培训班飞起,PHPer越来越多,学习成本很低。导致了很多人对PHP的误解。其实PHP学到深入的时候,所需知识很多,并不是表面看到的那样。另外,PHP确实严谨性不高,这个跟C++,java确实都没法比。但是,PHP在web开发中的效率,是其他语言所不能比的
    2017-02-02
  • C++示例讲解vector容器

    C++示例讲解vector容器

    这篇文章主要介绍了C++ 容器 Vector 的使用方法,Vector 是一个能够存放任意类型的动态数组,有点类似数组,是一个连续地址空间,下文更多详细内容的介绍,需要的小伙伴可以参考一下
    2022-07-07
  • C++生成随机数的实现代码

    C++生成随机数的实现代码

    这篇文章主要介绍了C++生成随机数的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • C++中的重载、覆盖、隐藏介绍

    C++中的重载、覆盖、隐藏介绍

    这篇文章主要介绍了C++中的重载、覆盖、隐藏介绍,需要的朋友可以参考下
    2015-04-04
  • VS2022 无法打开源文件“stdio.h”问题解决

    VS2022 无法打开源文件“stdio.h”问题解决

    本文主要介绍了VS2022 无法打开源文件“stdio.h”问题解决,文中通过图文的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • 可能是全网最详细的Qt连接MySQL数据库教程

    可能是全网最详细的Qt连接MySQL数据库教程

    QT众所周知是一个开源的,以C++为底层的可视化工具库,下面这篇文章主要给大家介绍了关于最详细的Qt连接MySQL数据库教程的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-04-04

最新评论