Linux--Socket编程UDP

news2024/12/23 15:07:55

前文:Socket套接字编程

UDP协议特点

  • 无连接:UDP在发送数据之前不需要建立连接,减少了开销和发送数据之前的时延。
  • 尽最大努力交付:UDP不保证可靠交付,主机不需要维持复杂的连接状态表。
  • 面向报文:UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
  • 首部开销小:UDP的首部只有8个字节,比TCP的20个字节要短。
  • 支持多种交互通信:UDP支持一对一、一对多、多对一和多对多的交互通信。

大体思路

将通过客户端与服务端的创建,实现客户端能够与服务端进行通信;

利用Socket编程接口,实现双端互通;

UdpServer.hpp

包含一个udp服务端的类,通过一些成员函数,能够实现对应创建类对象,进行初始化和启动对应服务端;
在这里插入图片描述
对于服务器来说,只需要创建对应的端口号,IP地址这里为本地地址(已经确定)。

代码

#pragma once

#include<iostream>
using namespace std;
#include"Log.hpp"
#include<string>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include"InetAddr.hpp"
//描述错误码
enum
{
    SOCKET_ERROR =1,
    BIND_ERROR,
    USAGE_ERROR
};
const static int defaultfd = -1;//默认文件描述符为无
class UdpServer
{
public:
    UdpServer(uint16_t port):
    _sockfd(defaultfd),
    _port(port),
    _isrunning(false)
    {}
    void InitServer()
    {
        //1.创建udp socket 套接字 
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd<0)
        {
            LOG(FATAL,"socket error, %s, %d\n",strerror(errno),errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO,"socket create success,sockfd: %d\n",_sockfd);

        //2 先填充sockadd_in结构
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port= htons(_port);//htons:将主机字节序转换为网络字节序
        //local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_addr.s_addr=INADDR_ANY;
        //INADDR_ANY告诉套接字监听来自任何IP地址的连接。这对于服务器来说非常有用,因为服务器通常需要能够接受来自客户端网络上任何位置的连接请求。

        //再通过函数bind将套接字和网络信息连接起来
        int n= bind(_sockfd,(struct sockaddr* )&local,sizeof(local));
        if(n<0)
        {
            LOG(FATAL,"bind error,%s, %d\n",strerror(errno),errno);
            exit(BIND_ERROR);
        }
        LOG(INFO,"socket bind success\n");
    }
    //通过循环不断接收客户端的数据,打印出对应的内容
    void Start()
    {
        _isrunning = true;
        while(true)
        {
           char buffer[1024];
           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)
           {
                buffer[n] = 0;
                InetAddr addr(peer);//创建一个网络地址的类对象
                LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(),addr.Port(),buffer);
                // 2. 我们要将server收到的数据,发回给对方,表示已接收
                sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
                
           }
        }
        _isrunning = false;
    }
private:
    int _sockfd; //sock文件描述符
    uint16_t _port; //端口号
    bool _isrunning; //服务器是否启动
};

解释

在这里插入图片描述
利用socket函数创建socket编程接口,AF_INET表示IPv4,SOCK_DGRAM表示udp数据报套接字的;

在这里插入图片描述
INADDR_ANY: 调用bind()函数将套接字与一个特定的端口(以及可选的IP地址)绑定时,可以将IP地址设置为INADDR_ANY(其值通常为0,表示“任何地址”)。这样做的好处是,UDP服务器将能够接收来自任何网络接口的连接请求,而不仅仅是绑定时指定的那一个。

在这里插入图片描述
这样bind之后就能让IP地址与端口号和Socket编程接口联系起来,让接口 “有名有性” ;

在这里插入图片描述将接收的数据存储在buffer中,peer可以接收到发送端的地址信息;
只有n大于0,才表示接收到了具体数据;

在这里插入图片描述
将接收到的数据信息,可以经过一定的处理,然后发送回客户端,这里中间没有经过处理,只是将接收到的信息直接反馈给客户端而已;也就是将buffer中的内容发送到客户端中;

InetAddr.hpp

InetAddr类:将包含主机上一切有关地址的信息;
在这里插入图片描述

代码

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
using namespace std;

class InetAddr
{
public:
    InetAddr(const struct sockaddr_in& addr):
    _addr(addr)
    {
        GetAddress(&_ip,&_port);
    }

    string Ip()
    {
        return _ip;
    }
    uint16_t Port()
    {
        return _port;
    }
    ~InetAddr()
    {
        
    }
private:
    //通过已知的sockaddr信息获取对应的ip地址信息和端口号
    void GetAddress(string* ip,uint16_t* port)
    {
        *port=ntohs(_addr.sin_port);
        *ip=inet_ntoa(_addr.sin_addr);
    }

    struct sockaddr_in _addr; //sockaddr的信息
    string _ip; //IP地址
    uint16_t _port; //端口号
};

main.cc

初始化服务端并启动服务端

#include<iostream>
#include<memory>
using namespace std;
#include"UdpServer.hpp"


void Usage(string proc)
{
    cout<<"Usage:\n\t"<<proc<<" local_port\n"<<endl;
}

//示例: ./main.cc 8888
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScreen();//打印到屏幕
    uint16_t port = stoi(argv[1]); //获取对应的端口号
    unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(port);//智能指针
    usvr->InitServer(); // 初始化服务端
    usvr->Start(); // 启动服务端

    return 0;
}

在这里插入图片描述
通过智能指针的性质,创建一个指向UdpServer类对象的指针,能够在周期结束时自动回收对应的智能指针;

UdpClient.cc

作为客户端能够不断给服务端发送数据并接收对应数据,构建Socket编程接口与上面一致。

#include<iostream>
using namespace std;
#include<string>
#include<cstring>
#include<cstdlib>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

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

//示例: ./UdpClient.cc 127.0.0.1 8080
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip=argv[1];//服务端ip
    uint16_t serverport=stoi(argv[2]);//服务端端口号

    //创建socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        cerr<<"socket error"<<endl;
    }

    //构建目标主机socket信息
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(serverport);
    server.sin_addr.s_addr=inet_addr(serverip.c_str());

    string message;
    while(true)
    {
        cout<<"Please Enter# ";
        getline(cin,message);//获取输入的一行字符串,空格亦可接收,输入到message中
        sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr *)&server,sizeof(server));//发送给服务端
        
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];//接收存放数据
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);//接收服务端发送数据
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
    }
}

127.0.0.1

127.0.0.1 是一个特殊的IP地址,被称为回环地址(Loopback Address)。在IPv4网络中,它被用于指向本机(即运行代码的计算机)。当你尝试访问 127.0.0.1 上的某个端口时,你的操作系统会识别这是一个内部请求,并将该请求直接路由回本机上的相应服务,而不会通过网络发送到外部。

这种机制使得开发者能够在本地测试网络服务,而无需担心外部网络的影响。

验证

在这里插入图片描述
在这里插入图片描述
我们可以通过命令行netstat来进行查询对应网络接口:

netstat(网络统计)命令是Linux系统中用于显示网络连接、路由表、接口统计、伪装连接和多播成员等网络相关信息的强大工具。

在这里插入图片描述

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

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

相关文章

PTPD 在 QNX 系统上的授时精度验证与误差排查

文章目录 0. 引言1.关键函数实现2. 验证策略与结果3. 授时误差的排查与解决3. 授时误差的排查与解决4. 结论 0. 引言 PTPD是一种时间同步的开源实现&#xff0c;在不同操作系统上的表现可能存在显著差异。 本文通过在QNX系统上运行PTPD&#xff0c;针对其授时精度进行详细验证…

MySQL:增删改查、临时表、授权相关示例

目录 概念 数据完整性 主键 数据类型 精确数字 近似数字 字符串 二进制字符串 日期和时间 MySQL常用语句示例 SQL结构化查询语言 显示所有数据库 显示所有表 查看指定表的结构 查询指定表的所有列 创建一个数据库 创建表和列 插入数据记录 查询数据记录 修…

游泳馆押金原路退回源码解析

<dl class"list "><dd class"address-wrapper dd-padding"><div class"address-container"><cyberdiv style"color:#f0efed;font-size:14px;float:right;position:absolute;right:10px;top: 2px;">●●●<…

正点原子imx6ull-mini-linux字符驱动模板(0)

1&#xff1a;驱动模块的加载和卸载 module_init(xxx_init); //注册模块加载函数 module_exit(xxx_exit); //注册模块卸载函数 1.1&#xff1a;新建一个用于存放linux驱动的目录,当然这个目录位置没有要求。创建要加载的模块chrbase.c cd ~/linux/drivers makdir linux_driv…

【安卓】Android Studio简易计算器(实现加减乘除,整数小数运算,正数负数运算)

目录 前言 运算效果 一、创建一个新的项目 二、编写xml文件&#xff08;计算器显示页面&#xff09; 三、实现Java运算逻辑 ​编辑 完整代码 xml文件代码&#xff1a; Java文件代码&#xff1a; 注&#xff1a; 前言 随着移动互联网的普及&#xff0c;手机应用程序已…

fetchApi === 入门篇

目录 fetch 基本认知 fetch 如何使用 Response对象&#xff08;了解&#xff09; 常见属性 常见方法 fetch 配置参数 fetch发送post请求 fetch 函数封装 fetch 实战 - 图书管理案例 渲染功能 添加功能 删除数据 完整代码 fetch 基本认知 思考&#xff1a; 以前开发…

PCIe总线-Linux内核PCIe软件框架分析(十一)

1.简介 Linux内核PCIe软件框架如下图所示&#xff0c;按照PCIe的模式&#xff0c;可分为RC和EP软件框架。RC的软件框架分为五层&#xff0c;第一层为RC Controller Driver&#xff0c;和RC Controller硬件直接交互&#xff0c;不同的RC Controller&#xff0c;其驱动实现也不相…

永结无间Ⅰ--基于 LLM 的 AGI

在过去几周&#xff0c;传奇人物 Francois Chollet 发起的ARC 挑战引起了不小的轰动。这项挑战让很多 AI 研究人员感到困惑&#xff0c;它表明了所有 AI 系统都存在泛化能力不足的问题。ARC 上上一次 SOTA AI 的准确率约为 34%&#xff0c;而在同一挑战中&#xff0c;Mechanica…

【React】Context机制跨层传递数据详解

文章目录 一、React Context的概念二、创建和使用Context1. 创建Context2. 使用Provider提供数据3. 使用Consumer获取数据 三、使用useContext Hook四、动态更新Context值五、Context的实际应用场景1. 主题切换2. 用户认证状态 六、注意事项 在开发React应用时&#xff0c;我们…

【C语言】深入探讨数组传参

一、数组传参简介 在C语言中&#xff0c;数组传参是一个常见的操作&#xff0c;尤其是在处理大量数据或需要多次访问相同数据集时。理解如何传递数组以及这些方法之间的差异是编写高效和安全代码的关键。在这篇博客中&#xff0c;我们将详细讨论C语言中数组传参的几种常见方法&…

【网络】应用层协议(自定义协议)(序列和反序列化)

应用层协议&#xff08;自定义协议&#xff09;&#xff08;序列和反序列化&#xff09; 一、引言--应用层的使用二、应用层1、网络版本计算器&#xff08;1&#xff09;协议定制和序列反序列化&#xff08;2&#xff09;网络版计算器协议定制i、封装有效载荷&#xff08;默认上…

数据结构——单链表OJ题(上)

目录 一、移除链表元素 1.思路 2.注意 3.解题 二、反转链表 思路1&#xff1a;三指针翻转法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 思路2&#xff1a;头插法 &#xff08;1&#xff09;注意 &#xff08;2&#xff09;解题 三、链表的中间结…

depcheck 前端依赖检查

介绍 depcheck 是一款用于检测项目中 未使用依赖项 的工具。 depcheck 通过扫描项目文件&#xff0c;帮助你找出未被引用的依赖&#xff0c;从而优化项目。 优势&#xff1a; 简单易用: 仅需几个简单的命令&#xff0c;就能够扫描并列出未使用的依赖项&#xff0c;让你快速了…

The Schematic workflow failed. See above.

在使用 ng new 新建Angular项目的时候会报一个错误&#xff1a;The Schematic workflow failed. See above. 解决办法&#xff1a; 只需要在后面加上 --skip-install 参数&#xff0c;就不会报错了。 ng new myapp --skip-install

打工人电脑里都需要的远程控制软件有哪些?这4款不能错过

不巧前几天台风&#xff0c;实在没办法到公司&#xff0c;但是项目还得继续&#xff0c;这时候远程控制电脑的技巧可谓是帮了我大忙了。不知道平常的你偶尔会不会也需要远程控制电脑的操作&#xff0c;如果有就继续看下去吧。 1.向日魁远程控制 直通车>>https://down.o…

AJAX-Promise 详解

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 一、Promise基本概念 1.1 定义 1.2 状态 1.3 构造函数 二、Promise基本用法 2.1 then() 2.2 ca…

ThinkPHP一对一关联模型的运用(ORM)

一、序言 最近在写ThinkPHP关联模型的时候一些用法总忘&#xff0c;我就想通过写博客的方式复习和整理下一些用法。 具体版本&#xff1a; topthink/framework&#xff1a;6.1.4topthink/think-orm&#xff1a;2.0.61 二、实例应用 1、一对一关联 1.1、我先设计了两张表&#x…

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 问题描述 根据题意写出完…

基于微信小程序+SpringBoot+Vue的社区超市管理系统(带1w+文档)

基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 基于微信小程序SpringBootVue的社区超市管理系统(带1w文档) 为了让商品信息的管理模式进行升级&#xff0c;也为了更好的维护商品信息&#xff0c;社区超市管理系统的开发运用就显得很有必要&#xff0c;因为它不仅可…

C# 植物大战僵尸

Winform 版本开发 高效率、流畅植物大战僵尸 git地址&#xff1a;冯腾飞/植物大战僵尸