【Linux】基于UDP协议的“聊天室”

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

目录

预备知识

基本思路

服务端设计

重要接口详解

服务端核心代码

服务端运行代码

客户端设计


预备知识

UDP协议(User Datagram Protocal用户数据报协议)

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

基本思路

如下是我们设计的一个简单的“聊天室”的大致框架图:

【Linux】基于UDP协议的“聊天室”,linux,udp,运维

        “聊天室”分为两个角色,一个是客户端,即参与聊天的用户,另一个是提供服务的服务端,负责接收来自客户端,对接收到的信息加工处理,显示发送方的ip和端口号,再转发给已经加入服务端所创建的用户列表中的所有用户(即已经在该聊天室的用户)。

服务端设计

重要接口详解

服务端设计只要有以下几个步骤:

//第一步   创建套接字socket

sockfd=socket (int domain, int type, int protocol) 

        1.domain指明使用的协议族,常用有AF_INET 、AF_INET6、AF_UNIX 、AF_ROUTE

        2. type指明socket类型 有三种:SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、  SOCK_RAW(原始类型,允许对底层协议如IP或ICMP进行直接访问,不太常用)

        3.protocol  通常赋值为0;

        --成功返回非负值的socket描述符,失败返回-1

//第二步   将创建的socket绑定到指定的IP地址和端口上

bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)

        --成功返回0,失败返回1

PS:

        1.uint16_t需要头文件 #include <unistd.h>

        2.sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义

        3.bzero函数头文件是string.h(C语言)或 cstring(C++)

        4.void bzero(void *s, size_t n);

        5.bzero函数将指定内存块的前n个字节设置为0。

        6.服务器提供服务的端口一般选择大于1023,因为【0,1023】是系统内定的端口号

服务端核心代码

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include "Log.hpp"
Log lg;

enum{
    
    SOCKET_ERR=1,
    BIND_ERR
};
uint16_t defaultport=8080;
std::string defaultip="0.0.0.0";
const int size=1024;
class UdpServer
{
public:
   UdpServer(const uint16_t& port=defaultport,const std::string&ip=defaultip)
    :_sockfd(0),
    _port(port),
    _ip(ip)
   {} 
    //初始化
    void Init()
    {
        //1.创建 udp socket
        //udp的socket是全双工的,允许被同时读写
        //AF_INET表示使用IPv4地址族  SOCK_DGRAM表示创建一个数据报套接字,0表示以阻塞的方式
        _sockfd=socket(AF_INET,SOCK_DGRAM,0);
        //创建套接字失败
        if(_sockfd<0)
        {
            lg(Fatal,"socket create error,sockfd:%d",_sockfd);
            exit(SOCKET_ERR);
        }
        //创建套接字成功
        lg(Info,"socket create success,sockfd:%d",_sockfd);
        //2.bind socket
        struct sockaddr_in local;
        bzero(&local,sizeof(local));

        local.sin_family=AF_INET;
        local.sin_port=htons(_port);//需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的
        local.sin_addr.s_addr=inet_addr(_ip.c_str()); //string->uint32_t必须是网络序列


        if(bind(_sockfd,(const struct sockaddr*)&local,sizeof(local))<0)
        {
            //绑定失败
            lg(Fatal,"bind error,errno:%d,err string:%s",errno,strerror(errno));
            exit(BIND_ERR);
        }

        //绑定成功
        lg(Info,"bind success,errno:%d,err string:%s",errno,strerror(errno));

    }
    void CheckUser(const struct sockaddr_in& client,const std::string clientip,uint16_t clientport)
    {
        auto iter=_online_user.find(clientip);
        if(iter==_online_user.end())
        {
            _online_user.insert({clientip,client});
            std::cout<<"["<<clientip<<":"<<clientport<<"] add to online user."<<std::endl;
        }
    }
    //对存在用户列表的所有用户进行转发
    void Broadcast(const std::string&info,const std::string clientip,uint16_t clientport)
    {
        for(const auto& user:_online_user)
        {
            std::string message="[";
            message += clientip;
            message += ":";
            message += std::to_string(clientport);
            message += "]# ";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)(&user.second),len);

        }
    }
    //开始运行
    void Run(){
        isrunning = true;
        char inbuffer[size];
        while(isrunning)
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            //接收客户端
            ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&client,&len);
            if(n<0)
            {
                //未收到
                lg(Warning,"recvfrom error,errno:%d,err string:%s",errno,strerror(errno));
                continue;
            }
            uint16_t clientport=ntohs(client.sin_port);
            std::string clientip=inet_ntoa(client.sin_addr);//网络字节序列转换 string
            //检查该用户是否已在聊天室
            CheckUser(client,clientip,clientport);
            std::string info=inbuffer;
            //向其他成员转发
            Broadcast(info,clientip,clientport);

        }
    }
private:
    int _sockfd;
    uint16_t _port;
    std::string _ip;
    bool isrunning; //服务器是否开始运行
    std::unordered_map<std::string,struct sockaddr_in> _online_user; //用户列表
};

服务端运行代码

#include "UdpServer.hpp"
#include <memory>
#include <cstdio>
#include <vector>
void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}
// ./udpserver port
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }

    uint16_t port = std::stoi(argv[1]);

    std::unique_ptr<UdpServer> svr(new UdpServer(port));

    svr->Init(/**/);
    svr->Run();

    return 0;
}

客户端设计

客户端也是需要绑定端口的,但是不需要用户显示绑定,一般由os自由随机选择,在首次发消息的时候绑定。不同于服务端的是:服务端端口号必须是唯一确定的,客户端可变。

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Terminal.hpp"
using namespace std;

struct ThreadDate
{
    struct sockaddr_in server;
    int sockfd;
    std::string serverip;
};

void Usage(std::string proc)
{
    std::cout<<"\n\rUsage:"<<proc<<"serverip serverport\n"<<std::endl;
}
//收信息
void* recv_message(void* args)
{
    OpenTerminal();
    ThreadDate* td = static_cast<ThreadDate*>(args);
    char buffer[1024];
    while(true)
    {
        memset(buffer,0,sizeof(buffer));
        struct sockaddr_in tmp;
        socklen_t len=sizeof(tmp);

        ssize_t n=recvfrom(td->sockfd,buffer,1023,0,(struct sockaddr*)&tmp,&len);
        if(n>0)
        {
            buffer[n]='\0';
            cerr<<buffer<<endl;
        }
    }
}
//发信息
void* send_message(void* args)
{
    ThreadDate* td=static_cast<ThreadDate*>(args);
    string message;
    socklen_t len=sizeof(td->server);

    string welcome = td->serverip;
    welcome += "coming...";
    sendto(td->sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&(td->server),len);
    while(true)
    {
        cout<<"Please Enter@ ";
        getline(cin,message);
        
        sendto(td->sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&(td->server),len);
    }
}
//多线程
//./udpclient serverip serverporta
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::string serverip=argv[1];
    uint16_t serverport=std::stoi(argv[2]);

    struct ThreadDate td;
    bzero(&td.server,sizeof(td.server));

    td.server.sin_family=AF_INET;
    td.server.sin_port=htons(serverport);
    td.server.sin_addr.s_addr=inet_addr(serverip.c_str());

    td.sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(td.sockfd<0)
    {
        cout<<"scoket error"<<endl;
        return 1;
    }

    td.serverip=serverip;

    pthread_t recver,sender;
    pthread_create(&recver,nullptr,recv_message,&td);
    pthread_create(&sender,nullptr,send_message,&td);

    pthread_join(recver,nullptr);
    pthread_join(sender,nullptr);
    close(td.sockfd);
    return 0;
}

上述客户端为了用户交互友好,我们打开两个终端模拟,一个终端负责发信息,一个终端负责收信息显示,我们重定向客户端收到消息后,往第二个终端打印。

【Linux】基于UDP协议的“聊天室”,linux,udp,运维

ls  -l  /dev/pts   //查看我们有哪些终端文件,显示它们的详细信息 

例如:

【Linux】基于UDP协议的“聊天室”,linux,udp,运维

重定向输出信息文章来源地址https://www.toymoban.com/news/detail-829888.html

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//原来的终端
std::string terminal = "/dev/pts/2";

int OpenTerminal()
{
    int fd = open(terminal.c_str(), O_WRONLY);
    if(fd < 0)
    {
        std::cerr << "open terminal error" << std::endl;
        return 1;
    }
    修改到要显示的终端
    dup2(fd, 0);

    // printf("hello world\n");
    // close(fd);
    return 0;
}

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

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

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

相关文章

  • UDP套接字的通信(实现英汉互译/程序替换/多线程聊天室/Windows与Linux通信)

    我们在客户端发英文,服务端做翻译工作,让翻译好的中文再次发给我们的客户端,然后打印出来。 翻译的操作 创建一个txt文件里面包含英汉互译的数据 dict.txt 对txt中的数据进行操作 分割函数 将英汉通过冒号分开。 将文件数据插入map里面 重新加载文件 通过捕捉2号(ctrl

    2024年02月11日
    浏览(38)
  • [Socket]Python用UDP协议建立带有私聊功能的网络聊天室-建立聊天工具

    前些天实习面试的时候被面试官问到Socket编程的问题,即“Socket创建和释放的具体过程是什么”,当时答不上来,似乎是涉及到发送和接收缓冲区的问题。由于自己在Socket编程这一块知识较为薄弱,于是写下这篇文章,当作复习下Socket编程知识。 首先,该实验是我在大三上学

    2024年02月03日
    浏览(48)
  • C语言实现--基于UDP的多人在线聊天室

    目录 实现功能 实现思想 实现代码(部分及详解) 服务端部分代码 客户端部分代码 实现效果 项目中出现的问题和解决方法 项目整体代码展示 代码优化思路 服务端代码 客户端代码 服务端可以同时连接多个客户端; 新的客户端连接服务端时,可以在服务端显示自己的名字并

    2024年02月04日
    浏览(64)
  • UDP聊天室

    1.头文件 2.服务器 3.客户端 4.makefile 5.结果

    2024年02月10日
    浏览(48)
  • 使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室

    一.1.搭建好TCP的通信模型 2.创建红黑树根节点 3.将套接字事件添加到红黑树中,使其被监听 4.当套接字事件发生,表示有客户端连接,将连接事件加入到红黑树节点当中 5.每当连接事件发生时,表示客户端发送信息到服务器 6.每当有事件准备就绪时,将对应的红黑树节点信息

    2024年02月13日
    浏览(45)
  • 网络聊天室的UDP实现以及数据库

    网络聊天室UDP实现 服务器端: 头文件: 主函数: 自定义函数: 客户端: 思维导图:

    2024年03月21日
    浏览(47)
  • 计算机网络 TCP/UDP程序开发网络聊天室

    TCP/UDP程序开发 开发TCP/UDP协议应用程序,掌握网络应用程序的工作原理。通过该实验,深入理解UDP和TCP协议的异同点,了解网络协议的工作过程,学会网络通信编程的基本方法,能够编制网络应用程序。 (1)了解和掌握“基于UDP-面向无连接的应用程序/基于TCP-面向连接的

    2024年02月05日
    浏览(89)
  • 【网络编程】UDP简单实现翻译软件与网络聊天室

    在上一章【网络编程】demo版UDP网络服务器实现实现了客户端和服务端之间的数据的发送与接收,上一章我们是直接让服务端把接收到的数据打印出来。 但是服务端并不是只接收到数据就完了,它还要 处理任务 。 所以我们可以在服务端设置一个回调函数: 用来处理接收到的

    2024年02月05日
    浏览(80)
  • 【嵌入式学习】网络通信基础-项目篇:简单UDP聊天室

    源码已在GitHub开源:0clock/LearnEmbed-projects/chat 客户端功能: 上线发送登录的用户名[yes] 发送消息和接收消息[yes] quit退出 服务器端功能: 统计用户上线信息,放入链表中[yes] 接收用户信息并给其他用户发送消息[yes] 服务器也支持给所有用户群发消息[yes] 接收下线提醒

    2024年01月25日
    浏览(64)
  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

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

    2024年02月14日
    浏览(86)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包