零基础学习计算机网络编程----socket实现UDP协议

        本章将会详细的介绍如何使用 socket 实现 UDP 协议的传送数据。有了前面基础知识的铺垫。对于本章的理解将会变得简单。将会从基础的 Serve 的初始化,进阶到 Client 的初始化,以及 run。最后实现一个简陋的小型的网络聊天室。

        

目录

        1.UdpSever.h

       1.1 构造函数

       1.2 initServe 函数

         2. UdpSever.cc

        3. udpClient.hpp 

         3.1 构造函数

        3.2 init(初始化函数)

        3.3 run

      4. udpClient.cc

5. 对于  UdpSever.h 中run函数的实现

 6. 结尾


 

        1.UdpSever.h

            在这个文件里面需用定义 Sever 的一个类,里面变量为:_ip, _port,_sockfd。要实现的函数为构造函数、初始化(init)、运行(run)、析构函数。进行初始化是实现服务器的网络的核心,当然run 也非常重要。关于实现构造函数的时候为什么只需要 port 以及defaultip是什么我后面都会进行讲解。

class udpServer{public:udpServer(uint16_t port): _ip(defaultIp), _port(port){}void initServe(){}void start(){}~udpServer() {}private:uint16_t _port;string _ip;int _sockfd;};

       1.1 构造函数

        里面使用了 port,因为这个服务器的特点,我们使用的 linux 有2个,或是2个以上的 ip,因此我们在进行绑定的时候是不可以进行直接绑定的。要不然当使用不同的 ip 的时候,会导致数据无法从客户端发送到服务端。因此需要使用默认的ip = “0.0.0.0”。(后面的cc文件中使用智能指针的方式进行定义)

        1.2 initServe 函数

        进行初始化,本质就是使用 socket 打开文件,会返回文件描述符(可以理解为打开网卡这个文件,然后进行传递数据)。然后进行 bind ,使用 struct sockaddr_in loacl, 对于这个结构体里面的数据进行填写,然后进行 bind 绑定。

        对于soket 函数,官方的定义为:返回一个文件描述符fd, 第一个参数是选择网络通信还是本地通信,第二变量表示是使用udp 还是 tcp, 我们使用的是 sock_dgram(数据报的方式发送数据),第三个变量表示阻塞状态默认为 0;最后如何是返回 -1 进行退出操作。

 _sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERROR);}

        使用 bind 函数进行绑定。 需要文件的描述符,一个 sockaddr 的结构体,sizeof(sockaddr 的结构体)。关键就是在于处理这个 sockaddr 我们使用的 sockaddr_in 然后进行类型的转化。最后我们使用的是先进行清零,然后 第一个位置放上协议家族:AF_INET, 第二位置放上 port 端口号需要使用 htons(host主机到net网络当中为 short 的 16 位的方式), ip 直接使用 inet_addr(将点分十进制转化位 32 位的整形,然后变成网络当中的大端方式发送)。还需要注意的是由于 inet_addr 传递的字符串的指针,所以是使用 c_str。整体的代码如下。

 

void initServe(){// 使用 socket 函数,然后进行绑定_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERROR);}// 进行band 绑定,注意是使用结构体进行绑定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());// 设置好 addr_in 就去进行绑定int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n == -1){cerr << "bind_error" << " : " << strerror(errno) << endl;exit(BIND_ERROR);}}

        对于启动函数,他本质就是一个无限的循环。 

for(;;)

         2. UdpSever.cc

        就。是在运行的时候,我们需要用户使用 ./ UdpSever port 如果不是就会提示如何使用。然后因为 argv[1] 使用的是字符串的方式,直接 atoi 就可以转化位整型。然后就是智能指针的定义,进行初始化,以及启动函数。

#include "udpServe.hpp"
#include <memory>
#include <unordered_map>using namespace djx;
static void Usage(std::string tmp)
{std::cout << "Usage:\n" << tmp << "local_port\n\n";
}
int main(int argc, char* argv[])
{if(argc != 2){//说明输入错误,需要重新输入Usage(argv[0]);exit(IN_ERROR);}uint16_t port = atoi(argv[1]);//然后需要启动定义好的 udpstd::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServe();usvr->start();return 0;
}

         最终实现的样式为这样,已经成功运行了。

        3. udpClient.hpp 

                跟udpSever是一样的都是4个函数,接下来我将会一一介绍。

         3.1 构造函数

                这个构造函数就需要告诉你,你要发送的服务器的ip地址,以及 port 端口号。 所以定义如下:

udpClient(uint16_t serveport, string serveip):_serveport(serveport),_serveip(serveip){}

        3.2 init(初始化函数)

                与udpSever一样都是使用socket,然后使用bind。但是这个bind 不要们进行显示的绑定,但是是需要进行绑定的。 为了保证独立性,ip 与 port 由操作系统为我们进行提供。

void initClient(){//click 是实现服务端的传送数据_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(2);}//进行绑定不要显示的进行绑定,是操作系统会帮助我们进行生成随机独立唯一的一个 port}

        3.3 run

        run就是运行函数,使用函数接口 sendto ,这个是一个输出型函数。是要将buf 缓冲里面的东西发送到 severip 和 severport 的 服务器当中,所以需要使用 sockaddr_in 对于里面的内容进行初始化。然后是对于 buffer 内容的输入。是使用 string message 类型的来进行存放数据。

 void run(){//需要使用函数 sendto, 但是在之前需要先获取到 secve 的ip 以及 portstruct sockaddr_in serve;serve.sin_family = AF_INET;serve.sin_port = htons(_serveport);serve.sin_addr.s_addr = inet_addr(_serveip.c_str());while(1){string message;cin >> message;sendto(_sockfd, message.c_str(), sizeof(message), 0, (struct sockaddr*)&serve, sizeof(serve));}}

      4. udpClient.cc

        与 Serve 类型都是先进行判断输入的argc 是为 3,如果是就进行启动。不是就告诉你如何使用。

#include "udpClient(fianll).hpp"
#include <memory>
using namespace std;
using namespace djx;
static void Usage(std::string tmp)
{cout << "Usage:\n\t" << tmp << " local_ip "<< "local_port\n\n";
}
int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[2]);string ip = argv[1];std::unique_ptr<udpClient> uclick(new udpClient(port, ip));uclick->initClient();uclick->run();return 0;
}

5. 对于  UdpSever.h 中run函数的实现

        start 函数需要接受从客服端发来的信息,通过之前启动的 sokce 以及 bind,启动了 udp 协议。内核就会帮助我们对于定义的 struct scokaddr_in 进行内容的填充(来自客户端发送过来的数据)。里面需要的函数为 recvfrom 这是一个输入输出型参数。从  struct scokaddr_in 获取输入参数,将参数输出到 buffer 这个缓存当中,然后进行打印。

        整体的实现如下,其中 full 定义为 1024 个整形。然后 s 表示的是buffer 里面读取到的个数,如果是小于0,就表示没有数据,直接结束此次循环。为了照顾计算机网络的数据严格传送的特点,len 的长度需要定义为 socklen_t 。使用 char 的时候写入的大小是从 0 开始的,所以是要sizeof -  1.

void start(){char buffer[full]; for (;;){// 用来存放 哪个click发送来的数据struct sockaddr_in peer;socklen_t len = sizeof(peer); // 为了照顾vecvfrom函数,所以使用这个socken_t;ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = 0; string message = buffer;    // s表示获取到的长度string click_ip = inet_ntoa(peer.sin_addr); // 直接就是从结构体变过来int click_port = ntohs(peer.sin_port);cout << click_ip << "[" << click_port << "]#" << message << endl;}}}

 6. 结尾

        到此 udp 简单实现已经结束了,还不是很全面因为我们是要得到数据,然后实现相应的工作,这里还没有实现功能,就是实现了一个简陋的网络聊天室。任意一台主机通过输入./udpClient ip + port, 我都可以在我的服务器上看见对应的信息。

        以上是对于计算网络中基础知识的了解,这个文章用于我的学习记录,如果是有其他的错误还请批评指正。如果对你有帮助还请给我点个赞👍👍👍。     

        

                 

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

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

相关文章

普中STM32F103ZET6开发攻略(二)

接上文&#xff1a;普中STM32F103ZET6开发攻略&#xff08;一&#xff09;-CSDN博客 各位看官老爷们&#xff0c;点击关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 目录 接上文&#xff1a;普中…

用提示词写程序(3),VSCODE+Claude3.5+deepseek开发edge扩展插件V2

edge扩展插件;筛选书签,跳转搜索,设置背景 链接: https://pan.baidu.com/s/1nfnwQXCkePRnRh5ltFyfag?pwd86se 提取码: 86se 导入解压的扩展文件夹: 导入扩展成功: edge扩展插件;筛选书签,跳转搜索,设置背景

电脑桌面便签软件哪个好?桌面好用便签备忘录推荐

在日常办公中&#xff0c;一款优秀的桌面便签工具能显著提升工作效率。面对市面上琳琅满目的选择&#xff0c;不少用户都难以抉择。如果你正在寻找一款兼具轻量化与多功能性的便签软件&#xff0c;那么集实用性与便捷性于一身的"好用便签"&#xff0c;或许就是你的理…

性能优化 - 工具篇:基准测试 JMH

文章目录 Pre引言1. JMH 简介2. JMH 执行流程详解3. 关键注解详解3.1 Warmup3.2 Measurement3.3 BenchmarkMode3.4 OutputTimeUnit3.5 Fork3.6 Threads3.7 Group 与 GroupThreads3.8 State3.9 Setup 与 TearDown3.10 Param3.11 CompilerControl 4. 示例代码与分析4.1 关键点解读…

2025年十大AI幻灯片工具深度评测与推荐

我来告诉你一个好消息。 我们已经亲自测试和对比了市面上最优秀的AI幻灯片工具&#xff0c;让你无需再为选择而烦恼。 得益于AI技术的飞速发展&#xff0c;如今你可以快速制作出美观、专业的幻灯片。 这些智能平台的功能远不止于配色美化——它们能帮你头脑风暴、梳理思路、…

雪花算法:分布式ID生成的优雅解决方案

一、雪花算法的核心机制与设计思想 雪花算法&#xff08;Snowflake&#xff09;是由Twitter开源的分布式ID生成算法&#xff0c;它通过巧妙的位运算设计&#xff0c;能够在分布式系统中快速生成全局唯一且趋势递增的ID。 1. 基本结构 雪花算法生成的是一个64位&#xff08;lo…

第1章:走进Golang

第1章&#xff1a;走进Golang 一、Golang简介 Go语言&#xff08;又称Golang&#xff09;是由Google的Robert Griesemer、Rob Pike及Ken Thompson开发的一种开源编程语言。它诞生于2007年&#xff0c;2009年11月正式开源。Go语言的设计初衷是为了在不损失应用程序性能的情况下…

Higress项目解析(二):Proxy-Wasm Go SDK

3、Proxy-Wasm Go SDK Proxy-Wasm Go SDK 依赖于 tinygo&#xff0c;同时 Proxy - Wasm Go SDK 是基于 Proxy-Wasm ABI 规范使用 Go 编程语言扩展网络代理&#xff08;例如 Envoy&#xff09;的 SDK&#xff0c;而 Proxy-Wasm ABI 定义了网络代理和在网络代理内部运行的 Wasm …

NVMe IP现状扫盲

SSD优势 与机械硬盘&#xff08;Hard Disk Driver, HDD&#xff09;相比&#xff0c;基于Flash的SSD具有更快的数据随机访问速度、更快的传输速率和更低的功耗优势&#xff0c;已经被广泛应用于各种计算领域和存储系统。SSD最初遵循为HDD设计的现有主机接口协议&#xff0c;例…

`docker commit` 和 `docker save`区别

理解 docker commit 和 docker save 之间的区别对于正确管理 Docker 镜像非常重要。让我们详细解释一下这两个命令的作用及其区别。 1. docker commit 作用&#xff1a; docker commit roop-builder roop:v1 命令的作用是基于一个正在运行的容器 roop-builder 创建一个新的镜…

Linux内核体系结构简析

1.Linux内核 1.1 Linux内核的任务 从技术层面讲&#xff0c;内核是硬件和软件之间的一个中间层&#xff0c;作用是将应用层序的请求传递给硬件&#xff0c;并充当底层驱动程序&#xff0c;对系统中的各种设备和组件进行寻址。从应用程序的角度讲&#xff0c;应用程序与硬件没有…

python爬虫:Ruia的详细使用(一个基于asyncio和aiohttp的异步爬虫框架)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Ruia概述1.1 Ruia介绍1.2 Ruia特点1.3 安装Ruia1.4 使用案例二、基本使用2.1 Request 请求2.2 Response - 响应2.3 Item - 数据提取2.4 Field 提取数据2.5 Spider - 爬虫类2.6 Middleware - 中间件三、高级功能3.1 …

网络攻防技术二:密码学分析

文章目录 一、传统密码分析方法1、根据明文、密文等信息的掌握情况分类 2、从密码分析途径分类二、密码旁路分析1、概念2、旁路分析方法三、现代密码系统1、对称密码&#xff08;单密钥&#xff09;2、公开密码&#xff08;成对密钥&#xff09; 四、典型对称密码&#xff08;单…

Linux --TCP协议实现简单的网络通信(中英翻译)

一、什么是TCP协议 1.1 、TCP是传输层的协议&#xff0c;TCP需要连接&#xff0c;TCP是一种可靠性传输协议&#xff0c;TCP是面向字节流的传输协议&#xff1b; 二、TCPserver端的搭建 2.1、我们最终好实现的效果是 客户端在任何时候都能连接到服务端&#xff0c;然后向服务…

pc端小卡片功能-原生JavaScript金融信息与节日日历

代码如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金融信息与节日日历</title><…

C语言——获取变量所在地址(uint8和uint32的区别)

前言&#xff1a; 1.使用uint8 *的原因 在C语言中&#xff0c;获取或操作一个4字节地址&#xff08;指针&#xff09;时使用uint8_t*&#xff08;即unsigned char*&#xff09;而不是uint32_t*&#xff0c;主要基于以下关键原因&#xff1a; 1.1. 避免违反严格别名规则&…

Python----目标检测(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理与网络结构)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 标题&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 机构&#xff1a;华盛顿大学&#xff08;University of Washington&#xff09; 发表时间&#xff1a;2018年 代…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— FormWave组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 构建一个美观、动态的登录表单&#xff0…

【数据结构】--二叉树--堆(上)

一、树的概念和结构 概念&#xff1a; 树是一种非线性的数据结构&#xff0c;他是由n(n>0)个有限结点组成一个具有层次关系的集合。其叫做树&#xff0c;是因为他倒过来看就和一棵树差不多&#xff0c;其实际上是根在上&#xff0c;树枝在下的。 树的特点&#xff1a; 1…

linux有效裁剪视频的方式(基于ffmpeg,不改变分辨率,帧率,视频质量,不需要三方软件)

就是在Linux上使用OBS Studio录制一个讲座或者其他视频&#xff0c;可能总有些时候会多录制一段时间&#xff0c;但是如果使用剪映或者PR这样的工具在导出的时候总需要烦恼导出的格式和参数&#xff0c;比如剪映就不支持mkv格式的导出&#xff0c;导出成mp4格式的视频就会变得很…