UDP————套接字socket

发布于:2025-07-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、Udpserver.hpp

1.创建套接字

_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  • 功能:创建一个UDP套接字。

  • 参数

    • AF_INET:指定地址族为IPv4。

    • SOCK_DGRAM:指定套接字类型为UDP,UDP是一种无连接的网络协议,数据包通过套接字发送和接收。

    • 0:默认的协议,对于UDP来说,通常使用默认值。

  • 返回值

    • 成功返回非负整数(套接字描述符)。

    • 失败返回-1,并设置errno

2. 检查套接字创建是否成功 

if(_sockfd < 0)
{
    LOG(LogLevel::FATAL) << "socket error!";
    exit(1);
}
LOG(LogLevel::INFO) << "socket success, sockfd: " << _sockfd;
  • 功能:检查套接字是否创建成功。

  • 日志记录

    • 如果创建失败,记录致命错误并退出程序。

    • 如果创建成功,记录成功信息

 3. 绑定套接字信息(IP和端口号)

struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;
  • 功能:填充sockaddr_in结构体并绑定套接字。

  • 参数

    • AF_INET:地址族为IPv4。

    • htons(_port):将主机字节序的端口号转换为网络字节序

    • INADDR_ANY:表示绑定到所有可用的网络接口。

4.调用bind函数 

int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
  • 功能:将套接字_sockfd绑定到sockaddr_in结构体local所指定的地址和端口。

  • 参数

    • _sockfd:套接字描述符,之前通过socket函数创建。

    • (struct sockaddr *)&local:指向sockaddr_in结构体的指针,sockaddr_insockaddr的子类型,用于IPv4地址。

    • sizeof(local)sockaddr_in结构体的大小。

  • 返回值

    • 成功返回0。

    • 失败返回-1,并设置errno

5.接收消息

ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
  • 使用recvfrom函数从套接字_sockfd接收数据。

  • 参数:

    • _sockfd:套接字描述符。

    • buffer:接收数据的缓冲区。

    • sizeof(buffer) - 1:缓冲区大小减1,防止溢出。

    • 0:标志位,通常设置为0。

    • (struct sockaddr *)&peer:存储发送方的地址信息。

    • &len:发送方地址信息的长度。

  • 返回值:

    • 成功返回接收到的字节数。

    • 失败返回-1,并设置errno

6.处理接收的消息

int peer_port = ntohs(peer.sin_port);
std::string peer_ip = inet_ntoa(peer.sin_addr);
buffer[s] = 0;
std::string result = _func(buffer);
  1. 将接收到的数据转换为字符串。
  2. 调用_func函数处理接收到的消息,返回处理结果。

 7.发送响应

sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
  • 使用sendto函数将处理结果发送回客户端。

  • 参数:

    • _sockfd:套接字描述符。

    • result.c_str():要发送的数据。

    • result.size():数据的大小。

    • 0:标志位,通常设置为0。

    • (struct sockaddr*)&peer:客户端的地址信息。

    • len:客户端地址信息的长度

#pragma once

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


#include "Log.hpp"

using namespace LogModule;

using func_t = std::function<std::string(const std::string&)>;

const int defaultfd = -1;

// 为了进行网络通信
class UdpServer
{
public:
    UdpServer(uint16_t port, func_t func)
        : _sockfd(defaultfd),
          _port(port),
          _isrunning(false),
          _func(func)
    {
    }
    void Init()
    {
        //1.创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);//AF_INET 指定了地址族为 IPv4。
        //SOCK_DGRAM 指定了套接字类型为 UDP,这是一种无连接的网络协议,数据包通过套接字发送和接收
        //创建成功会返回非负整数
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error!";
            exit(1);
        }
        LOG(LogLevel::INFO) << "socket success, sockfd: " << _sockfd;
        
        //2.绑定socke信息,ip和端口号
        //2.1 填充sockaddr_in结构体
         struct sockaddr_in local;//sockaddr_in的使用,已放在CSDN上
         bzero(&local, sizeof(local));//bzero 函数将指定数量的字节设置为零,通常用于初始化数据结构。
        local.sin_family = AF_INET;
        //本地格式->网络序列
        local.sin_port = htons(_port);
        //htons假设主机使用 Little-endian 字节序,端口号 _port 为 12345(在内存中的表示为 0x3039),转换为网络字节序后,它将变为 0x3930。
        // IP也是如此,1. IP转成4字节 2. 4字节转成网络序列 -> in_addr_t inet_addr(const char *cp);
        local.sin_addr.s_addr = INADDR_ANY;

        //为什么服务器的bind是显示的呢?因为IP和端口号是众所周知且不能轻易改变的!
        //这段代码是UDP服务器初始化过程中绑定套接字到特定地址和端口的部分。
        //它的主要功能是将创建的套接字与服务器的IP地址和端口号关联起来,以便服务器能够在指定的地址和端口上接收和发送数据
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error!";
            exit(2);
        }
        LOG(LogLevel::INFO) << "bind success, sockfd: " << _sockfd;
    }
    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            //1.收消息,client发送数据,让服务器进行处理数据
            ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)//如果接收到的数据字节数大于0,表示成功接收到消息。
            {
                int peer_port = ntohs(peer.sin_port);//从网络中拿到:网络序列
                std::string peer_ip = inet_ntoa(peer.sin_addr);//4字节网络风格的IP ->点分十进制的IP

                buffer[s] = 0;

                std::string result = _func(buffer);

                //2.发消息
                sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
            }  
        }
    }
    ~UdpServer()
    {
    }

private:
    int _sockfd;
    uint16_t _port;
    // std::string _ip; // 用的是字符串风格,点分十进制, "192.168.1.1"
    bool _isrunning;

    func_t _func;//服务器的回调函数,用于进行对数据的处理
};

二、Udpserver.cc 

std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port, defaulthandler);
1. std::unique_ptr

   std::unique_ptr是一种智能指针,它拥有其所指向的对象,并在std::unique_ptr被销毁时自动删除其拥有的对象。std::unique_ptr确保了对象的唯一所有权,即同一时间只有一个std::unique_ptr可以拥有一个对象。

2. std::make_unique

    std::make_unique是一个辅助函数,用于创建一个std::unique_ptr并初始化它所指向的对象。它接受对象构造函数的参数,并将这些参数转发给对象的构造函数。

三、Udpclient.cc 

 1.sockaddr_in

struct sockaddr_in 的定义如下:

struct sockaddr_in {
    short            sin_family;   // 地址族,通常为 AF_INET
    unsigned short   sin_port;     // 端口号,网络字节序
    struct in_addr   sin_addr;     // IP 地址,网络字节序
    char             sin_zero[8];  // 未使用,通常填充为 0
};

成员变量解释

  1. sin_family

    • 类型:short

    • 用途:指定地址族,对于 IPv4,值为 AF_INET

  2. sin_port

    • 类型:unsigned short

    • 用途:指定端口号,必须使用网络字节序(大端序)。通常使用 htons 函数将主机字节序转换为网络字节序。

    • 示例:server.sin_port = htons(12345);

  3. sin_addr

    • 类型:struct in_addr

    • 用途:指定 IP 地址,必须使用网络字节序。struct in_addr 通常包含一个 s_addr 成员,类型为 in_addr_t

    • 示例:server.sin_addr.s_addr = INADDR_ANY;server.sin_addr.s_addr = inet_addr("192.168.1.100");

  4. sin_zero

    • 类型:char[8]

    • 用途:未使用,通常填充为 0,以确保结构体的大小与 struct sockaddr 一致。

 

 


网站公告

今日签到

点亮在社区的每一天
去签到