当前位置: 首页 > news >正文

Linux网络套接字编程——UDP服务器

Linux网络套接字编程——创建并绑定-CSDN博客

前面已经介绍了网络套接字的创建和绑定,这篇文章会通过UDP套接字实现一个UDP服务器。

先介绍将使用的接口。

recvfrom

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

第一个表示从哪个套接字获取信息,第二个是缓冲区,len是缓冲区长度,数据就会被读到缓冲区中,flags可直接填0。

src_addr是输出型参数,将会返回是哪个客户端发送的信息,addrlen指向src_addr结构的结构体大小,若这两个参数为NULL,则不获取客户端的相关信息。

使用这个函数时,需要自己定义sockaddr_in结构体,并且定义一个变量为sockaddr_in的大小。

然后将其传入,以获得客户端的IP地址和端口。

返回值为实际收到的字节数,出现错误则返回-1。

sendto

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd是服务器绑定的套接字的文件描述符,buf是你将要发送的信息,len为buf的长度,flags设为0, dest_addr表示要发消息给谁,addrlen是传入的结构体的长度。

返回值为发送的字节数,出现错误则返回-1。

服务器代码

现在已经可以实现一个UDP服务器了

#include <iostream>
#include <string>
#include <sys/types.h>
#include <cstdlib>
#include <sys/socket.h>
#include <cstring>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
//服务器将绑定的IP地址
const std::string IP = "0.0.0.0";
//缓冲区的大小
const int MAXBUFSIZE = 1024;
//服务器对发送过来的信息进行何种处理后返回处理后的信息
using func_t = std::function<std::string(const std::string&)>;
class server
{
public:
    //创建并绑定套接字
    server(uint16_t port, func_t handle, std::string ip = IP);
    //服务器运行,并且接收信息,向客户端返回处理后的信息
    void run();
private:
    //服务器如何处理收到的信息,由程序员自己定义
    func_t _handle;
    //打开的套接字的文件描述符
    int _sock_fd;
    //服务器是否运行
    bool running;
    //服务器绑定的端口
    uint16_t _port;
    //服务器绑定的IP地址
    std::string _ip;
};

这里解释一下0.0.0.0IP地址,它意味着服务器绑定本机上的所有网络接口,一般服务器主机会有不止一块网卡,需要将所有网络接口都绑定到服务器上,而不是某一个IP地址。

下面给出服务器初始化的代码

    server(uint16_t port, func_t handle, std::string ip = IP)
        :_port(port),_ip(ip)
    {
        running = false;
        _handle = handle;
        //创建UDP套接字
        _sock_fd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sock_fd < 0)
        {
           std::cout<<"socket error"<<std::endl;
           exit(1);
        }
        //绑定套接字
        sockaddr_in svr_addr;
        svr_addr.sin_family = AF_INET;
        //将端口由主机字节序转为网络字节序
        svr_addr.sin_port = htons(_port);
        //将字符串风格的IP地址转为网络字节序的32位
        svr_addr.sin_addr.s_addr = inet_addr(_ip.c_str());
        socklen_t len = sizeof(svr_addr);
        //由于bind的参数是sockaddr*类型,这里需要强转
        int err = bind(_sock_fd, (sockaddr*)&svr_addr, len);
        if(err != 0)
        {
            std::cout<<"bind error"<<std::endl;
            exit(1);
        }

    }

 然后是服务器运行的代码

    void run()
    {
        running = true;
        //保存客户端的IP地址和端口号,以便于将处理后的信息发给他
        sockaddr_in client_addr;
        //用于接收数据的缓冲区
        char buf[MAXBUFSIZE];
        std::cout << "Server running..." << std::endl;
        while(running)
        {
            memset(buf, 0, MAXBUFSIZE);
            socklen_t len = sizeof(client_addr);
            ssize_t recv_size = recvfrom(_sock_fd, buf, sizeof(buf) - 1,0,(sockaddr*)&client_addr, &len);
            client_addr.sin_port = ntohs(client_addr.sin_port);
            client_addr.sin_addr.s_addr = ntohl(client_addr.sin_addr.s_addr);
            if(recv_size < 0)
            {
                std::cout<<"recv error"<<std::endl;
                continue;
            }
            //程序员自定义的处理数据函数
            std::string ret = _handle(std::string(buf));
            std::cout << ret << std::endl;

            memset(buf, 0, MAXBUFSIZE);
            //向客户端发送处理后的信息
            ssize_t send_size = sendto(_sock_fd, ret.c_str(), ret.size(), 0, (sockaddr*)&client_addr, len);
            if(send_size < 0)
            {
                std::cout<<"send error"<<std::endl;
                continue;
            }

        }
    }

然后是main函数

//服务器程序像这样使用 ./server [port]
//server是我自己的可执行程序名称,你可以定义自己的
void Usage()
{
    std::cout<< "Usage: " << "./server" << " port"<<std::endl;
}
//只是简单的将信息回显
std::string handle(const std::string& s)
{
    return "Server recv:" + s;
}


int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        Usage();
        return 1;
    }

    uint16_t port = atoi(argv[1]);
    server svr(port, handle);
    svr.run();
    return 0;
}

结合前面的讲解,这些代码应该可以理解,如果还不理解,建议自己动手敲一遍,百分百能理解。

只有服务端还不够完整,不能直观的看到运行效果,下面再给出客户端代码

客户端代码

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <cstring>

const int MAXBUFSIZE = 1024;
class client
{
public:
    client();
 
    void SendTo(const sockaddr_in& server_addr, const std::string& msg);
    
private:
    int _sock_fd;
};

客户端初始化

    client()
    {
        _sock_fd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sock_fd < 0)
        {
            std::cerr<<"socket error"<<std::endl;
            exit(1);
        }
        
    }

客户端发送消息

    void SendTo(const sockaddr_in& server_addr, const std::string& msg)
    {
        int ret = sendto(_sock_fd, msg.c_str(), msg.size(), 0, (sockaddr*)&server_addr, sizeof(server_addr));
        if(ret < 0)
        {
            std::cerr<<"sendto error"<<std::endl;
            exit(2);
        }
    }

 main函数

void Usage()
{
    std::cout<< "Usage: " << "./server" << " port"<<std::endl;
}

std::string handle(const std::string& s)
{
    return "Server recv:" + s;
}
int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        Usage();
        return 1;
    }

    uint16_t port = atoi(argv[1]);
    server svr(port, handle);
    svr.run();
    return 0;
}

有了服务器的编写经验,客户端代码应该很容易看懂,你也可以自己写一个运行在电脑上看效果。

你可以使用本地回环地址 127.0.0.1对代码进行测试,用法如下,假设端口是8080

./client 127.0.0.1 8080 

相关文章:

  • vue项目如何实现条件查询?
  • Word 小黑第21套
  • 市面上常用的23种设计模式,分析实现方式以及实际使用场景案例
  • 安卓实现魔改版 CRC32 算法
  • 大模型架构记录5-向量数据库
  • 领域驱动设计(DDD)与业务驱动划分
  • 汽车无钥匙启动系统不使用传统机械钥匙启动汽车
  • PTA乙级 A除以B
  • Deepseek Chatgpt Kimi 推荐的深度学习书单
  • Sourcetree——使用.gitignore忽略文件或者文件夹
  • 解决leetcode第3455题最短匹配子字符串
  • ui要放在析构函数里吗?
  • Java-ArrayList
  • css基本功
  • 【Troubleshot】Qt 长按按键 keyPressEvent keyReleaseEvent 自动重复问题
  • 【从零开始学习计算机科学】数据库系统(六)DBMS事务管理
  • C# 发送邮件 报错:此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站。
  • 【学习笔记】语言模型的发展历程
  • SpringBoot3+Lombok如何配置logback输出日志到文件
  • JVM 垃圾回收器的选择
  • 做公司网站的企业/百度推广深圳分公司
  • 好看的 网站后台模板/千锋教育地址
  • 武汉网站建站/立即优化在哪里
  • 一个虚拟空间做两个网站/武汉seo论坛
  • 怎样看一个网站是不是织梦做的/seo咨询顾问
  • 建网络商城网站吗/网络营销公司好不好