网络编程 : 基于UDP的网络群聊聊天室

这篇具有很好参考价值的文章主要介绍了网络编程 : 基于UDP的网络群聊聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、UDP网络编程:

1.1 流程

网络编程 : 基于UDP的网络群聊聊天室

服务器流程:

创建用户数据报套接字

填充服务器的网络信息结构体

绑定套接字与服务器网络信息结构体

收发数据

关闭套接字

客户端流程:

创建用户数据报套接字

填充服务器的网络信息结构体

收发数据

关闭套接字

二、基于UDP的网络群聊聊天室                                           

 2.1 功能:

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

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

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

服务器可以发送系统信息

提示:

客户端登录之后,为了实现一边发送数据一边接收数据,可以使用多进程或者多线程

服务器既可以发送系统信息,又可以接收客户端信息并处理,可以使用多进程或者多线程

服务器需要给多个用户发送数据,所以需要保存每一个用户的信息,使用链表来保存

数据传输的时候要定义结构体,结构体中包含操作码、用户名以及数据

2.2  流程图

 网络编程 : 基于UDP的网络群聊聊天室

 2.3 代码实现

 头文件:dup.h

#ifndef __UDP_H__
#define __UDP_H__

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

#define N 128
#define M 32

#define ERRLOG(msg) do{\
    printf("%s %s(%d):", __FILE__, __func__, __LINE__);\
    perror(msg);\
    exit(-1);\
}while(0)

typedef struct _Node{
    struct sockaddr_in addr; 
    struct _Node *next;
}node_t;

typedef struct _Msg{
    char code;
    char user[M];
    char text[N];
}msg_t;
#endif

服务器:server.c

#include "udp.h"

//创建节点的函数
int create_node(node_t **phead){
    *phead = (node_t *)malloc(sizeof(node_t));
    if(NULL == *phead){
        printf("内存分配失败\n");
        exit(-1);
    }
    (*phead)->next=NULL;
    return 0;
}
//尾插法
int insert_data_by_tail(node_t *phead,struct sockaddr_in addr){
     if(NULL == phead){
        printf("入参为NULL,请检查\n");
        return -1;
    }
    //将新客户端使用尾插法插入链表中
    node_t *pnew = NULL;
    create_node(&pnew);
    pnew->addr = addr;  
    node_t *ptemp =phead;
    while(ptemp->next != NULL){
        ptemp = ptemp->next;
    }
    //让尾结点的指针域指向新节点
    ptemp->next = pnew;
    return 0;
}

int main(int argc,const char *argv[]){    
    if(3 != argc){
        printf("Uage:%s <IP><port>\n",argv[0]);
        return -1;
    }
    int sockfd = 0;
    if(-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0))){
        ERRLOG("socket error");
    }
    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(-1 == bind(sockfd,(struct sockaddr *)&serveraddr,serveraddr_len)){
        ERRLOG("bind error");
    } 

    struct sockaddr_in clientaddr,temp_clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));
    socklen_t clientaddr_len = sizeof(clientaddr);
 
    char name[32] = {0};
    pid_t pid = 0;
    msg_t msg;
    msg_t msg_send;
    //创建头结点
    node_t *phead;
    create_node(&phead);
    phead->addr = clientaddr;
 
    if(-1 == (pid = fork())){
        ERRLOG("fork error");
    }else if(0 == pid){   //子进程 接收数据 (1、d 登录操作 2、q 群聊操作 3、t 退出操作)  
        while(1){
            memset(&msg,0,sizeof(msg));
            if(-1 == recvfrom(sockfd, (void*)&msg, sizeof(msg),0, (struct sockaddr *)&clientaddr,&clientaddr_len)){
                perror("recv error");
            }    
        switch(msg.code){
            // 1、d 登录操作 2、q 群聊操作 3、t 退出操作 
            case 'd':
                printf("[%s]该玩家已上线\n", msg.user);              
                insert_data_by_tail(phead,clientaddr);
                node_t *q=phead->next;                
                while(q != NULL){
                    msg.code='d';
                    if(-1 == sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&q->addr,sizeof(q->addr))){
                        ERRLOG("send error");
                    }
                    q=q->next;
                }
                break; 
            case 'q':                     
                if(strcmp("管理员",msg.user)!=0){
                printf("[%s]:%s\n",msg.user, msg.text);
                }
                node_t *p = phead->next;                
                while(p != NULL){
                    msg.code='q';
                    if(-1 == sendto(sockfd,(void *)&msg,sizeof(msg),0,(struct sockaddr *)&p->addr,sizeof(p->addr))){
                        ERRLOG("send error");
                    }
                    p=p->next;
                }                
                break; 
            case 't':    
                printf("[%s]:退出了...\n", msg.user);
                node_t *t = phead; 
                node_t *pdel = NULL;               
                while(t->next != NULL){
                    msg.code='t';
                    if( 0 == memcmp(&(t->next->addr), &clientaddr,sizeof(clientaddr))){
                        pdel = t->next;
                         t->next = pdel->next;
                        free(pdel);
                    }else{
                        t = t->next;
                        if(-1 == sendto(sockfd, &msg,sizeof(msg),0,(struct sockaddr *)&t->addr,sizeof(t->addr))){
                            ERRLOG("send error");
                        }
                    }    
                }     
                break;
            }
        }
    }else if(0 < pid){
        //父进程 发送系统消息
        while(1){  
            strcpy(msg_send.user,"管理员");
            memset(msg_send.text,0,N);
            fgets(msg_send.text,N,stdin);
            msg_send.text[strlen(msg_send.text)-1] = '\0';
            msg_send.code = 'q';              
            if(-1 == sendto(sockfd,&msg_send,sizeof(msg_send),0,(struct sockaddr *)&serveraddr,serveraddr_len)){
                ERRLOG("send error");                
            }               
        }
    } 
    kill(pid, SIGKILL);
    wait(NULL);//给子进程回收资源
    exit(0);     
    close(sockfd);
    return 0;
}

 客户端:client.c

#include "udp.h"

int main(int argc, const char *argv[])
{
    if (3 != argc){
        printf("Usage: %s <IP> <port>\n", argv[0]);
        return -1;
    }

    //创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd){
        ERRLOG("socket error");
    }

    //填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    //网络字节序的端口号 8888  9999  6789 等 都可以
    serveraddr.sin_port = htons(atoi(argv[2]));
    //网络字节序的IP地址,IP地址不能乱填
    //自己的主机ifconfig 查到的ip地址是多少就填多少
    //如果本机测试使用 也可以填写 127.0.0.1
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);

    int nbytes = 0;
    char name[32]={0};
    msg_t msg;
    pid_t pid;

    struct sockaddr_in clientaddr;
    memset(&clientaddr,0,sizeof(clientaddr));
    socklen_t clientaddr_len = sizeof(clientaddr);

    //输入用户名,完成登陆操作
    printf("请输入登录信息:");
    msg.code = 'd';
    memset(msg.user, 0, M);
    fgets(name, M, stdin);//在终端获取用户名
    strcpy(msg.user,name);
    msg.user[strlen(msg.user) - 1] = '\0'; //清空结尾的 '\n' 
    if (-1 == sendto(sockfd,&msg,sizeof(msg),0, (struct sockaddr *)&serveraddr,serveraddr_len)){  //给服务器发送用户名
        ERRLOG("send error");
    }

    //创建进程
    if(-1 == (pid = fork())){
        ERRLOG("fork error");
    }else if(0 == pid){   
        //子进程 接收数据  
        while (1){
            memset(&msg,0,sizeof(msg));
            //接收服务器的应答
            if (-1 == (nbytes=recvfrom(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&serveraddr,&serveraddr_len))){
                ERRLOG("recv error");
            }  
            // printf("current ------->%d\n",strcmp(msg.user,name)); 
                       
            if(strcmp(msg.user,name) == -10){               
                continue;
            }else{
                //打印应答信息
                switch(msg.code){
                    case 'd':
                        printf("[%s]登录上线了....\n", msg.user); 
                        break; 
                    case 'q':
                        printf("[%s]:%s\n",msg.user,msg.text);
                        break;
                    case 't':            
                        printf("[%s]退出了....\n", msg.user); 
                    break;
                }  
            } 
        }    
        }else if(0 < pid){
            //父进程 发送数据(2、q:群聊操作  3、t:退出操作)
            while(1){
                //在终端获取群聊
                memset(msg.text, 0, N);
                fgets(msg.text, N, stdin);
                msg.text[strlen(msg.text) - 1] = '\0'; //清空结尾的 '\n'               
                if( 0 ==strcmp(msg.text, "quit")){
                    msg.code = 't'; 
                    if (-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&serveraddr,serveraddr_len)){
                        ERRLOG("send error");     
                    }   
                    break;
                }else{
                    msg.code = 'q';    
                }
                //给服务器发送群聊消息
                if (-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&serveraddr,serveraddr_len)){
                        ERRLOG("send error");
                }
            }     
        }      
    //关闭套接字
    close(sockfd);
    return 0;
}

2.4 实现结果 

网络编程 : 基于UDP的网络群聊聊天室文章来源地址https://www.toymoban.com/news/detail-430933.html

到了这里,关于网络编程 : 基于UDP的网络群聊聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

    源IP地址(Source IP Address): 源IP地址是数据包发送方(或数据流出发点)的唯一标识符。它用于在互联网或本地网络中定位发送数据包的设备或主机。源IP地址是数据包的出发点,即数据从这个地址开始传送,向目的IP地址指示的设备发送。 在TCP/IP协议中,源IP地址通常由发

    2024年02月14日
    浏览(74)
  • C/C++手把手从零搭建多人群聊聊天室(客户端)

        先贴上效果图: 本文将从客户端说起,从客户端到服务端(如何搭建云电脑连接外网)开始一步步实现聊天室。 全程采用c/c++语言,教程会用到MFC,不熟悉的朋友可以先去了解一些基础知识,有用到的知识点会在文章中进行详细的介绍以及解释。  先从客户端开始做起,我们

    2024年02月02日
    浏览(32)
  • 基于TCP、UDP网络编程

    应用层 : 应用程序拿到数据怎么用 传输层 : 负责关注传输过程中起点和终点 网络层 :负责整个传输过程中的路线规划 数据链路层 :负责相邻节点之间的数据传输 物理层 : 基础设施,硬件设备 程序员写网络程序,主要编写的应用层代码,真正要发这个数据,调用下层协

    2024年02月07日
    浏览(40)
  • 【网络编程】网络编程概念,socket套接字,基于UDP和TCP的网络编程

    前言: 大家好,我是 良辰丫 ,今天我们一起来学习网络编程,网络编程的基本概念,认识套接字,UDP与TCP编程.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注

    2023年04月20日
    浏览(44)
  • Java中的网络编程------基于Socket的TCP编程和基于UDP的网络编程,netstat指令

    Socket 在Java中,Socket是一种用于网络通信的编程接口, 它允许不同计算机之间的程序进行数据交换和通信 。Socket使得网络应用程序能够通过TCP或UDP协议在不同主机之间建立连接、发送数据和接收数据。以下是Socket的基本介绍: Socket类型 :在Java中,有两种主要类型的Socket,分

    2024年02月10日
    浏览(33)
  • [网络编程]UDP协议,基于UDP协议的回显服务器

    目录 1.UDP协议介绍 2.UDP协议在Java中的类 2.1DatagramSocket类 2.2DatagramPacket 3.回显服务器 3.1Sever端  3.2Client端   UDP协议是一种网络协议,它是无连接的,全双工,并且是面向数据报,不可靠的一种协议。 常用于在线视频播放,游戏这种实时性要求比较高的应用。或者无需可靠传输

    2024年01月22日
    浏览(43)
  • 基于UDP/TCP的网络通信编程实现

    红色是心中永不褪色的赤诚 操作系统为网络编程提供了 Socket api , Socket是基于TCP/IP协议的网络通信的基本单元, 基于Socket的网络程序开发就是 网络编程. 由于直接与应用层联系的是传输层, 所以针对应用层协议(TCP, UDP), Shocket提供了三种套接字, 分别是 流套接字(使用TCP) , 数据报

    2024年02月08日
    浏览(42)
  • 【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序

    我们把数据从A主机发送到B主机,是目的吗?不是,真正通信的不是这两个机器!其实是这两台机器上面的软件(人) 数据有 IP(公网) 标识一台唯一的主机 ,用谁来标识各自主机上客户或者服务进程的唯一性呢? 为了更好的表示一台主机上服务进程的唯一性,我们采用 端口号

    2024年02月12日
    浏览(47)
  • Java 网络编程 —— 基于 UDP 的数据报和套接字

    UDP(User Datagram Protocol,用户数据报协议)是传输层的另一种协议,比 TCP 具有更快的传输速度,但是不可靠。UDP 发送的数据单元被称为 UDP 数据报,当网络传输 UDP 数据报时,无法保证数据报一定到达目的地,也无法保证各个数据报按发送的顺序到达目的地,例如:当发送方

    2024年02月08日
    浏览(32)
  • 【网络编程】基于UDP数据报实现回显服务器/客户端程序

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】【Java系列】 本专栏旨在分享学习网络编程的一点学习心得,欢迎大家在评论区交流讨论💌 前言 我们如果想让应用程序进行网络通信的话,就需要调用传

    2024年02月04日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包