Linux——网络(udp)

news2025/1/29 7:07:54


文章目录

目录

文章目录

前言

一、upd函数及接口介绍

1. 创建套接字 - socket 函数

2. 绑定地址和端口 - bind 函数

3. 发送数据 - sendto 函数

4. 接收数据 - recvfrom 函数

5. 关闭套接字 - close 函数

二、代码示例

1.服务端

2.客户端

总结


前言

Linux——网络基础(1)-CSDN博客


一、upd函数及接口介绍

1. 创建套接字 - socket 函数

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

  • 参数说明:
    • domain:指定协议族,对于 UDP 编程,通常使用 AF_INET 表示 IPv4 协议族。
    • type:指定套接字类型,对于 UDP,使用 SOCK_DGRAM 表示数据报套接字。
    • protocol:指定具体的协议,一般设置为 0,让系统自动选择合适的协议(对于 SOCK_DGRAM 通常为 UDP)。
  • 返回值:
    • 成功时返回一个非负的套接字描述符;失败时返回 -1,并设置 errno 以指示错误类型。

2. 绑定地址和端口 - bind 函数

#include <sys/socket.h>

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

 

  • 参数说明:
    • sockfd:由 socket 函数返回的套接字描述符。
    • addr:指向 sockaddr 结构体的指针,该结构体包含要绑定的地址和端口信息。对于 IPv4,通常使用 sockaddr_in 结构体,并进行强制类型转换。
    • addrlenaddr 结构体的长度。
  • 返回值:
    • 成功时返回 0;失败时返回 -1,并设置 errno

3. 发送数据 - sendto 函数

#include <sys/socket.h>

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

 

  • 参数说明:
    • sockfd:套接字描述符。
    • buf:指向要发送数据的缓冲区。
    • len:要发送数据的长度。
    • flags:发送标志,通常设置为 0。
    • dest_addr:指向目标地址的 sockaddr 结构体指针,包含目标主机的地址和端口信息。
    • addrlendest_addr 结构体的长度。
  • 返回值:
    • 成功时返回实际发送的字节数;失败时返回 -1,并设置 errno

4. 接收数据 - recvfrom 函数

#include <sys/socket.h>

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

 

  • 参数说明:
    • sockfd:套接字描述符。
    • buf:指向用于存储接收数据的缓冲区。
    • len:缓冲区的最大长度。
    • flags:接收标志,通常设置为 0。
    • src_addr:指向源地址的 sockaddr 结构体指针,用于存储发送方的地址和端口信息。
    • addrlen:指向 src_addr 结构体长度的指针,调用前设置为该结构体的大小,调用后会被更新为实际接收到的地址信息的长度。
  • 返回值:
    • 成功时返回实际接收的字节数;失败时返回 -1,并设置 errno

5. 关闭套接字 - close 函数

#include <unistd.h>

int close(int fd);

 

  • 参数说明:
    • fd:要关闭的套接字描述符。
  • 返回值:
    • 成功时返回 0;失败时返回 -1,并设置 errno

二、代码示例

1.服务端

#pragma once

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

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

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),isrunning_(false)
    {}
    void Init()
    {
        // 1. 创建udp socket
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INET
        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()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??
        // local.sin_addr.s_addr = htonl(INADDR_ANY);

        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 Run(func_t func) // 对代码进行分层
    {
        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;
            }
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = func(info);
            sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);
        }
    }
    ~UdpServer()
    {
        if(sockfd_>0) close(sockfd_);
    }
private:
    int sockfd_;     // 网路文件描述符
    std::string ip_; // 任意地址bind 0
    uint16_t port_;  // 表明服务器进程的端口号
    bool isrunning_;
};
  • Init 方法用于初始化 UDP 服务器:
    • 调用 socket 函数创建一个 UDP 套接字,使用 AF_INET 表示 IPv4 协议族,SOCK_DGRAM 表示 UDP 套接字。
    • 如果套接字创建失败,使用日志记录错误信息并退出程序。
    • 创建一个 sockaddr_in 结构体 local,用于存储服务器的地址信息。
    • 使用 bzero 函数将 local 结构体清零。
    • 设置 local 结构体的 sin_family 为 AF_INETsin_port 为传入的端口号,并使用 htons 函数将其转换为网络字节序。
    • 使用 inet_addr 函数将传入的 IP 地址字符串转换为网络字节序的 32 位整数,并赋值给 sin_addr.s_addr
    • 调用 bind 函数将套接字绑定到指定的地址和端口。
    • 如果绑定失败,使用日志记录错误信息并退出程序。
  • Run 方法用于启动 UDP 服务器并处理客户端请求:
    • 将 isrunning_ 标志设置为 true,表示服务器正在运行。
    • 创建一个大小为 size 的字符数组 inbuffer,用于存储接收到的数据。
    • 进入一个无限循环,不断接收客户端发送的数据。
    • 调用 recvfrom 函数接收客户端发送的数据,并将客户端的地址信息存储在 client 结构体中。
    • 如果接收失败,使用日志记录错误信息并继续下一次循环。
    • 在接收到的数据末尾添加字符串结束符 '\0',将其转换为 std::string 类型的 info
    • 调用传入的函数对象 func 对接收到的数据进行处理,并将处理结果存储在 echo_string 中。
    • 调用 sendto 函数将处理结果发送回客户端。

2.客户端

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./udpclient serverip serverport
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 sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport); //?
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    socklen_t len = sizeof(server);

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

    // client 要bind吗?要!只不过不需要用户显示的bind!一般有OS自由随机选择!
    // 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此!
    // 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以!
    // 系统什么时候给我bind呢?首次发送数据的时候

    string message;
    char buffer[1024];
    while (true)
    {
        cout << "Please Enter@ ";
        getline(cin, message);

        // std::cout << message << std::endl;
        // 1. 数据 2. 给谁发
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);
        
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);

        ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);
        if(s > 0)
        {
            buffer[s] = 0;
            cout << buffer << endl;
        }
    }

    close(sockfd);
    return 0;
}
  • argc 表示命令行参数的数量,argv 是一个字符串数组,存储了命令行参数。
  • 如果命令行参数数量不等于 3(程序名、服务器 IP 地址、服务器端口号),则调用 Usage 函数打印使用说明并退出程序。
  • 从命令行参数中获取服务器的 IP 地址和端口号。
  • std::stoi 函数将字符串类型的端口号转换为 uint16_t 类型。
  • 创建一个 sockaddr_in 结构体 server,用于存储服务器的地址信息。
  • bzero 函数将 server 结构体的内存清零。
  • server.sin_family 设置为 AF_INET,表示使用 IPv4 地址族。
  • server.sin_port 使用 htons 函数将端口号从主机字节序转换为网络字节序。
  • server.sin_addr.s_addr 使用 inet_addr 函数将字符串类型的 IP 地址转换为网络字节序的 32 位整数。
  • len 存储 server 结构体的大小。
  • socket 函数创建一个 UDP 套接字,使用 AF_INET 表示 IPv4 地址族,SOCK_DGRAM 表示 UDP 协议。
  • 如果套接字创建失败,输出错误信息并返回 1。
  • 创建一个 std::string 类型的 message 用于存储用户输入的消息,创建一个字符数组 buffer 用于存储从服务器接收的数据。
  • 使用 getline 函数从标准输入读取用户输入的消息。
  • sendto 函数将消息发送给服务器,指定套接字描述符、消息内容、消息长度、标志位、服务器地址结构体和地址结构体的大小。
  • 创建一个 sockaddr_in 结构体 temp 用于存储服务器的地址信息,len 存储 temp 结构体的大小。
  • recvfrom 函数从服务器接收数据,指定套接字描述符、接收缓冲区、缓冲区大小、标志位、服务器地址结构体指针和地址结构体大小指针。
  • 如果接收到的数据长度大于 0,在缓冲区末尾添加字符串结束符 '\0',并输出接收到的数据。

总结

该程序实现了一个简单的 UDP 客户端,通过命令行参数指定服务器的 IP 地址和端口号,从用户输入获取消息并发送给服务器,同时接收服务器的响应并输出。客户端不需要显式绑定端口,操作系统会在首次发送数据时自动分配一个可用的端口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nxopen 直齿轮参数化设计

NXUG1953 Visualstudio 2019 参考论文&#xff1a; A Method for Determining the AGMA Tooth Form Factor from Equations for the Generated Tooth Root Fillet //FullGear// Mandatory UF Includes #include <uf.h> #include <uf_object_types.h>// Internal I…

初阶数据结构:链表(二)

目录 一、前言 二、带头双向循环链表 1.带头双向循环链表的结构 &#xff08;1)什么是带头&#xff1f; (2)什么是双向呢&#xff1f; &#xff08;3&#xff09;那什么是循环呢&#xff1f; 2.带头双向循环链表的实现 &#xff08;1&#xff09;节点结构 &#xff08;2…

使用openwrt搭建ipsec隧道

背景&#xff1a;最近同事遇到了个ipsec问题&#xff0c;做的ipsec特性&#xff0c;ftp下载ipv6性能只有100kb, 正面定位该问题也蛮久了&#xff0c;项目没有用openwrt, 不过用了开源组件strongswan, 加密算法这些也是内核自带的&#xff0c;想着开源的不太可能有问题&#xff…

网络安全 | F5-Attack Signatures详解

关注&#xff1a;CodingTechWork 关于攻击签名 攻击签名是用于识别 Web 应用程序及其组件上攻击或攻击类型的规则或模式。安全策略将攻击签名中的模式与请求和响应的内容进行比较&#xff0c;以查找潜在的攻击。有些签名旨在保护特定的操作系统、Web 服务器、数据库、框架或应…

MATLAB绘图时线段颜色、数据点形状与颜色等设置,介绍

MATLAB在绘图时&#xff0c;设置线段颜色和数据点的形状与颜色是提高图形可读性与美观性的重要手段。本文将详细介绍如何在 MATLAB 中设置这些属性。 文章目录 线段颜色设置单字母颜色表示法RGB 值表示法 数据点的形状与颜色设置设置数据点颜色和形状示例代码 运行结果小结 线段…

论文速读|Matrix-SSL:Matrix Information Theory for Self-Supervised Learning.ICML24

论文地址&#xff1a;Matrix Information Theory for Self-Supervised Learning 代码地址&#xff1a;https://github.com/yifanzhang-pro/matrix-ssl bib引用&#xff1a; article{zhang2023matrix,title{Matrix Information Theory for Self-Supervised Learning},author{Zh…

FPGA工程师成长四阶段

朋友&#xff0c;你有入行三年、五年、十年的职业规划吗&#xff1f;你知道你所做的岗位未来该如何成长吗&#xff1f; FPGA行业的发展近几年是蓬勃发展&#xff0c;有越来越多的人才想要或已经踏进了FPGA行业的大门。很多同学在入行FPGA之前&#xff0c;都会抱着满腹对职业发…

计算机组成原理(2)王道学习笔记

数据的表示和运算 提问&#xff1a;1.数据如何在计算机中表示&#xff1f; 2.运算器如何实现数据的算术、逻辑运算&#xff1f; 十进制计数法 古印度人发明了阿拉伯数字&#xff1a;0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#…

简化配置与动态表达式的 Spring EL

1 引言 在现代软件开发中,配置管理和动态逻辑处理是构建灵活、可维护应用程序的关键。Spring 框架以其强大的依赖注入和面向切面编程功能而闻名,而 Spring Expression Language (Spring EL) 则为开发者提供了一种简洁且强大的方式来简化配置并实现动态表达式。 1.1 Spring …

python实现http文件服务器访问下载

//1.py import http.server import socketserver import os import threading import sys# 获取当前脚本所在的目录 DIRECTORY os.path.dirname(os.path.abspath(__file__))# 设置服务器的端口 PORT 8000# 自定义Handler&#xff0c;将根目录设置为脚本所在目录 class MyHTT…

在php中怎么打开OpenSSL

&#xff08;点击即可进入聊天助手&#xff09; 背景 在使用php做一些项目时,有用到用户邮箱注册等,需要开启openssl的能力 在php系统中openssl默认是关闭状态的,在一些低版本php系统中,有的甚至需要在服务器终端后台,手动安装 要打开OpenSSL扩展&#xff0c;需要进行以下步骤 …

二次封装的方法

二次封装 我们开发中经常需要封装一些第三方组件&#xff0c;那么父组件应该怎么传值&#xff0c;怎么调用封装好的组件原有的属性、插槽、方法&#xff0c;一个个调用虽然可行&#xff0c;但十分麻烦&#xff0c;我们一起来看更简便的方法。 二次封装组件&#xff0c;属性怎…

基于Springboot用axiospost请求接收字符串参数为null的解决方案

问题 ​ 今天在用前端 post 请求后端时发现&#xff0c;由于是以 Json对象的形式传输的&#xff0c;后端用两个字符串形参无法获取到对应的参数值 前端代码如下&#xff1a; axios.post(http://localhost:8083/test/postParams,{a: 1, b:2} ,{Content-Type: application/jso…

STM32 OLED屏配置

1.OLED简介 OLED&#xff08;Organic Light Emitting Diode&#xff09;&#xff1a;有机发光二极管 OLED显示屏&#xff1a;性能优异的新型显示屏&#xff0c;具有功耗低、相应速度快、宽视角、轻薄柔韧等特点 0.96寸OLED模块&#xff1a;小巧玲珑、占用接口少、简单易用&a…

DiffuEraser: 一种基于扩散模型的视频修复技术

视频修复算法结合了基于流的像素传播与基于Transformer的生成方法&#xff0c;利用光流信息和相邻帧的信息来恢复纹理和对象&#xff0c;同时通过视觉Transformer完成被遮挡区域的修复。然而&#xff0c;这些方法在处理大范围遮挡时常常会遇到模糊和时序不一致的问题&#xff0…

STM32完全学习——RT-thread在STM32F407上移植

一、写在前面 关于源码的下载&#xff0c;以及在KEIL工程里面添加操作系统的源代码&#xff0c;这里就不再赘述了。需要注意的是RT-thread默认里面是会使用串口的&#xff0c;因此需要额外的进行串口的初始化&#xff0c;有些人可能会问&#xff0c;为什么不直接使用CubMAX直接…

QT TLS initialization failed

qt使用QNetworkAccessManager下载文件&#xff08;给出的链接可以在浏览器里面下载文件&#xff09;&#xff0c;下载失败&#xff0c; 提示“TLS initialization failed”通常是由于Qt在使用HTTPS进行文件下载时&#xff0c;未能正确初始化TLS&#xff08;安全传输层协议&…

全面了解 Web3 AIGC 和 AI Agent 的创新先锋 MelodAI

不管是在传统领域还是 Crypto&#xff0c;AI 都是公认的最有前景的赛道。随着数字内容需求的爆炸式增长和技术的快速迭代&#xff0c;Web3 AIGC&#xff08;AI生成内容&#xff09;和 AI Agent&#xff08;人工智能代理&#xff09;正成为两大关键赛道。 AIGC 通过 AI 技术生成…

Golang之Context详解

引言 之前对context的了解比较浅薄&#xff0c;只知道它是用来传递上下文信息的对象&#xff1b; 对于Context本身的存储、类型认识比较少。 最近又正好在业务代码中发现一种用法&#xff1a;在每个协程中都会复制一份新的局部context对象&#xff0c;想探究下这种写法在性能…

VSCode+Continue实现AI辅助编程

Continue是一款功能强大的AI辅助编程插件&#xff0c;可连接多种大模型&#xff0c;支持代码设计优化、错误修正、自动补全、注释编写等功能&#xff0c;助力开发人员提高工作效率与代码质量。以下是其安装和使用方法&#xff1a; 一、安装VSCode 参见&#xff1a; vscode安…