udp常用函数介绍 linux环境

目录

udp

预备知识

了解sockaddr_in

 网络字节顺序转主机字节顺序(大小端问题)

常用函数介绍

实践(一个回显打印服务)

大致流程

ServerMain.cpp 服务器的主程序

Udpserve.h   对udp操作封装

InterAddr.h  进行转换(主机大小端不确定)

ClientMain.cpp 客户端


udp

预备知识

了解sockaddr_in

struct sockaddr_in {  
    sa_family_t    sin_family; // 地址族,对于IPv4,总是AF_INET  
    uint16_t       sin_port;   // 端口号,网络字节序  
    struct in_addr sin_addr;   // IPv4地址  
    // 注意:在某些实现中,可能还有额外的填充字节以确保结构体的对齐  
};  
  
// 其中,struct in_addr 是一个小结构体,通常只包含一个32位的IPv4地址  
struct in_addr {  
    uint32_t s_addr; // IPv4地址,网络字节序  
};

 网络字节顺序转主机字节顺序(大小端问题)

在网络通信中,由于不同的计算机系统可能采用不同的字节序(字节在内存中的排列顺序)来存储数据,因此在数据交换时需要进行字节序的转换。这主要涉及到网络字节序和主机字节序之间的转换。网络字节序通常采用大端模式,而主机字节序则可能是大端模式或小端模式,具体取决于计算机系统的架构。

简单接口

       #include <arpa/inet.h>

       uint32_t htonl(uint32_t hostlong);

       uint16_t htons(uint16_t hostshort);

       uint32_t ntohl(uint32_t netlong);

       uint16_t ntohs(uint16_t netshort);

其中“h”代表host(主机),“n”代表network(网络),“to”表示转换方向,“s”代表short(短整型),“l”代表long(长整型)。 

常用函数介绍

UDP是一种无连接的协议,与TCP不同,它不提供错误恢复、重传机制或顺序保证。

以下是UDP连接(更准确地说,是UDP通信的初始化过程,因为UDP是无连接的)时所用的主要函数:

socket()

int socket(int domain, int type, int protocol);
  1. 作用:创建一个新的套接字。对于UDP,domain 参数通常是 AF_INET(IPv4)或 AF_INET6(IPv6),type 参数是 SOCK_DGRAM 表示数据报套接字,protocol 通常是0,让系统自动选择UDP协议。
  2. 参数意义
    1. domain:指定套接字使用的协议族。对于IPv4网络,此参数通常是AF_INET;对于IPv6网络,则是AF_INET6
    2. type:指定套接字的类型。对于UDP,此参数是SOCK_DGRAM,表示数据报套接字。
    3. protocol:指定具体的协议。通常,对于UDP来说,这个参数设置为0,因为SOCK_DGRAM已经隐含了UDP协议。
  3. 返回值:成功时返回一个新的套接字描述符(非负整数),失败时返回-1并设置errno以指示错误

bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  1. 作用:将套接字与特定的IP地址和端口号绑定。对于UDP服务器,这一步是必要的,因为它需要监听一个特定的端口以接收客户端的消息。对于UDP客户端,如果不需要在特定端口上接收数据,也可以不调用 bind()
  2. 参数意义
    1. sockfd:通过socket()函数获得的套接字描述符。
    2. addr:指向sockaddr结构的指针,该结构包含了套接字将要绑定的IP地址和端口号。对于IPv4,通常使用sockaddr_in结构。
    3. addrlenaddr参数指向的缓冲区的大小,以字节为单位。
  3. 返回值:成功时返回0,失败时返回-1并设置errno以指示错误。

sendto() 

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 
const struct sockaddr *dest_addr, socklen_t addrlen);
  1. 作用:sendto() 用于发送UDP数据报
  2. 参数意义
    1. sockfd:要发送数据的套接字描述符。
    2. buf:指向要发送数据的缓冲区的指针。
    3. len:缓冲区中数据的长度。
    4. flags:通常设置为0,表示没有特殊标志。
    5. dest_addr:指向sockaddr结构的指针,该结构包含了目标地址和端口号。
    6. addrlendest_addr参数指向的缓冲区的大小,以字节为单位。
  3. 返回值:成功时返回发送的字节数,失败时返回-1并设置errno以指示错误。

recvfrom()

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
struct sockaddr *src_addr, socklen_t *addrlen);
  1. 作用:recvfrom() 用于接收UDP数据报。 
  2. 参数意义
    1. sockfd:要接收数据的套接字描述符。
    2. buf:指向接收数据的缓冲区的指针。
    3. len:缓冲区的大小,即可以接收的最大字节数。
    4. flags:通常设置为0,表示没有特殊标志。
    5. src_addr:可选参数,如果非NULL,则接收发送方的地址信息。
    6. addrlen:输入时表示src_addr缓冲区的大小,输出时表示实际接收到的地址信息的长度。
  3. 返回值:成功时返回接收到的字节数,如果连接被对方正常关闭,则返回0;失败时返回-1并设置errno以指示错误。但请注意,对于UDP来说,recvfrom()返回0通常表示超时或某些特定条件下的错误,而不是正常的连接关闭。

close()

int close(int fd);
  1. 作用:关闭套接字。完成UDP通信后,应关闭套接字以释放资源。
  2. 参数意义: fd:要关闭的文件描述符,对于套接字编程来说,就是套接字描述符。

  3. 返回值:成功时返回0,失败时返回-1并设置errno以指示错误。

请注意,UDP编程不需要像TCP那样进行显式的“连接”和“断开连接”操作。相反,UDP应用通过 sendto() 和 recvfrom() 直接发送和接收数据报。这些函数允许UDP应用在没有任何预先建立连接的情况下,向任何IP地址和端口发送数据报,并从任何IP地址和端口接收数据报。

实践(一个回显打印服务)

大致流程

UDP通信的大致流程可以分为服务器端和客户端两部分:

服务器端流程
  1. 创建套接字(Socket)
  2. 绑定地址和端口
  3. 接收数据
  4. 处理数据
  5. 发送数据(可选)
  6. 关闭套接字
客户端流程
  1. 创建套接字(Socket)
  2. (可选)绑定地址和端口
  3. 发送数据
  4. 接收数据(可选
  5. 关闭套接字

ServerMain.cpp 服务器的主程序

#include "UdpServe.h"
#include <memory>
int main(int agrc, char *argv[])
{
    if (agrc != 2)
    {
        std::cerr << "usage: " << argv[0] << " local port" << std::endl;
        exit(0);
    }
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<Udpserver> usvr = std::make_unique<Udpserver>(port);
    usvr->Initserver();
    usvr->start();
    return 0;
}

Udpserve.h   对udp操作封装

static const int gscokfd = -1;
static const uint16_t glocalpoart = 8888;
enum
{
    SOCEKFD_ERROT = -1,
    BIND_ERROT
};
class Udpserver : public nocopy
{
public:
    Udpserver(uint16_t localport = glocalpoart)
        : _sockfd(gscokfd)
        , _localPort(localport)
        , isrunning(false)
    {
    }
    void Initserver()
    {
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            std::cout << "socket error" << std::endl;
            exit(SOCEKFD_ERROT);
        }
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_localPort);
        local.sin_addr.s_addr = INADDR_ANY; // 服务器端进行任意ip绑定
        int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            std::cout << "bind error" << std::endl;
            exit(BIND_ERROT);
        }
    }
    void start()
    {
        isrunning = true;
        char buffer[1024];
        while (isrunning)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                InterAddr addr(peer);
                buffer[n] = 0;
                std::cout << "[" << addr.ip << ":" << addr.port << "]#" << buffer << std::endl;

                std::string echostr = "udp server#";
                echostr += buffer;
                sendto(_sockfd, echostr.c_str(), echostr.size(), 0, (struct sockaddr *)&peer, len);
            }
            else
            {
                std::cout << "recvfrom ,  error" << std::endl;
            }
        }
    }
    ~Udpserver()
    {
        if (_sockfd > gscokfd)
            ::close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _localPort;
    bool isrunning;
};

InterAddr.h  进行转换(主机大小端不确定)

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
class InterAddr
{
private:
    void ToHost(const struct sockaddr_in &addr)
    {
        port = ntohl(addr.sin_port);
        ip = inet_ntoa(addr.sin_addr);
    }

public:
    std::string ip;
    uint16_t port;
    struct sockaddr_in _addr;

public:
    InterAddr() {}
    InterAddr(const struct sockaddr_in &addr)
        : _addr(addr)
    {
        ToHost(addr);
    }
    bool operator==(const InterAddr &t)
    {
        return (this->ip == t.ip && this->port == t.port);
    }
    std::string Ip()
    {
        return ip;
    }
    uint16_t Port()
    {
        return port;
    }
    struct sockaddr_in Addr()
    {
        return _addr;
    }
    std::string AddrStr()
    {
        return ip + ":" + std::to_string(port);
    }
    ~InterAddr()
    {
    }
};

ClientMain.cpp 客户端

#include<iostream>
#include <string>
#include<bits/stdc++.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;
        exit(0);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket errot" << std::endl;
        exit(0);
    }
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(serverport);
    local.sin_addr.s_addr = inet_addr(serverip.c_str());
    while (1)
    {
        std::string line;
        std::cout << "请输入:" << std::endl;
        std::getline(std::cin, line);
        int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&local, sizeof(local));
        if (n > 0)
        {
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            char buffer[1024];
            int m = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&temp, &len);
            if (m > 0)
            {
                buffer[m] = 0;
                std::cout << buffer << std::endl;
            }
            else
            {
                std::cerr << "recvform error" << std:: endl;
                exit(0);
            }
        }
        else
        {
            std::cerr << "sendto error" << std::endl;
            exit(0);
        }
    }
    close(sockfd);
    return 0;
}

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,可以使用socket和sendto函数来实现UDP发送功能。具体的发送函数可以根据你的需求来编写,以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" // 服务器IP地址 #define SERVER_PORT 8888 // 服务器端口号 int main() { int sockfd; struct sockaddr_in server_addr; // 创建socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } // 设置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero)); // 发送数据 char *message = "Hello, UDP!"; if (sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("sendto"); exit(1); } // 关闭socket close(sockfd); return 0; } ``` 上述代码中,首先创建了一个UDP socket,并设置服务器的IP地址和端口号。然后通过sendto函数将待发送的数据发送到服务器。最后关闭socket。 请注意,上述代码只是一个简单的示例,实际的发送函数可以根据具体的需求进行修改和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++socket(udptcp)常用基础函数笔记](https://blog.csdn.net/matrix_studio/article/details/127352149)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [linux C++ UDP通信工具类](https://blog.csdn.net/qq_40778196/article/details/130263498)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值