Linux之套接字UDP实现网络通信

Linux之套接字UDP实现网络通信

文章目录

  • Linux之套接字UDP实现网络通信
  • 1.引言
  • 2.具体实现
    • 2.1需要知道的套接字接口
      • 1.socket()
      • 2.bind()
      • 3.recvfrom()
      • 4.sendto()
    • 2.2服务器端server.hpp
    • 2.3服务器端server.cc
    • 2.4客户端Client.cc

1.引言

​ 套接字(Socket)是计算机网络中实现网络通信的一种编程接口。它提供了应用程序与网络通信之间的一座桥梁,因为它允许应用程序通过网络发送和接收相应的数据以实现不同主机之间的通信。

在这里插入图片描述

通常套接字由以下两部分组成:

1.网络IP和端口号:IP用来标识主机,而端口号可以标识到单台主机的唯一进程。

2.通信协议:套接字通过规定通信协议来制定数据传输和发送的规则。常见的有TCP和UDP等协议。

TCP是一种面向连接的协议,提供可靠的、有序的、基于字节流的数据传输。

UDP是一种无连接的协议,提供不可靠的、无序的、基于数据报的数据传输。

​ 我们今天要实现的是通过UDP协议实现网络通信。UDP协议通信虽然无连接不可靠,可是足够简单到我们了解通信的基本原理。

那么话不多讲,我们赶快看看我们学习完今天这一篇能够实现出怎么样的结果吧:

在这里插入图片描述

我们通过实现客户端和服务器端,实现了通过套接字UDP创建了一个服务器,之后通过客户端链接并且通信的一个功能。

事不宜迟,我们马上实现!

2.具体实现

​ 首先我们需要明确具体的大思路: 先服务器端创建socket套接字,并recvfrom接收到。客户端也创建套接字绑定后确定到唯一IP和端口号之后即可进行通信。

​ 在具体实现之前我们首先需要一些必要的套接字接口

2.1需要知道的套接字接口

1.socket()

​ socket函数是用于创建套接字的函数,创建成功返回文件描述符fd,失败返回-1

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

​ 参数说明:

  • domain

    :指定套接字的地址族(Address Family)

    今天我们选择:

    • AF_INET:IPv4 地址族
  • type

    :指定套接字的类型(Socket Type)

    今天我们选择:

    • SOCK_DGRAM:无连接的数据报套接字,用于 UDP 协议
  • protocol

    :可选参数,指定具体的传输协议。常用的有:

    ​ 今天我们选择:

    • 0:自动选择合适的协议

2.bind()

​ 在Linux下,bind() 函数用于将一个套接字(socket)与特定的IP地址和端口号进行绑定

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

参数说明:

  • sockfd:要进行绑定的套接字的文件描述符。
  • addr:指向一个 struct sockaddr 结构体的指针,其中包含要绑定的IP地址和端口号信息。
  • addrlenaddr 结构体的长度。

在绑定bind的第二个参数中,我们也需要用到库中定义好的sockaddr_in结构体来初始化!

具体结构体struct sockaddr_in说明:

结构体中有三个值也需要初始化指定一下:

  1. sin_family:表示地址族(Address Family),一般为 AF_INET

  2. sin_port:表示端口号。它是一个 16 位的整数,使用网络字节序(大端字节序)表示。在使用时,通常需要使用 htons() 函数将主机字节序转换为网络字节序。

  3. sin_addr:表示 IPv4 地址。它是一个 struct in_addr 类型的结构体,用于存储 32 位的 IPv4 地址。

    一般服务端用INADDR_ANY,让udp_server在启动时候可以绑定任何ip.

    ​ 客户端用inet_addr函数将字符串转化成32位无符号整数

3.recvfrom()

从套接字接收数据,并获取发送方的地址

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:用于存储发送方的地址信息(对于面向数据报的套接字)。它是一个 struct sockaddr 结构体的指针。
  • addrlensrc_addr 结构体的长度,作为输入参数指定 src_addr 缓冲区的大小,作为输出参数返回实际地址的长度。

4.sendto()

通过套接字发送数据到指定目的地

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:指向目标地址(接收方地址)的结构体指针,可以是 struct sockaddr 或其派生类型的指针。
  • addrlendest_addr 结构体的长度。

2.2服务器端server.hpp

​ 在服务器的头文件中,我们首先需要定义一个udpserver的类,服务器类中需要有服务器的初始化与启动命令,当然需要有构造析构等。默认的私有成员是**_sock套接字和port端口**


const static uint16_t default_port = 8080;class UdpServer
{public:UdpServer(uint16_t port = default_port):_port(port){std::cout<< "server addr: "<<_port <<std::endl;}~UdpServer() {}void InitServer() //初始化服务器{_sock = socket(AF_INET,SOCK_DGRAM,0);if(_sock < 0){std::cerr << " socket create err " << std::endl;}std::cout << "create socket success: " << _sock << std::endl;struct sockaddr_in local; //利用库中创建好的结构体来初始化socketmemset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port); // 本地主机序列转网络序列local.sin_addr.s_addr = INADDR_ANY; //让udp_server在启动时候可以绑定任何ip//绑定if(bind(_sock,(struct sockaddr*)&local,sizeof(local)) < 0){std::cerr << " bind error" << std::endl;exit(-1);}std::cout << "bind socket success: " << _sock << std::endl;}   void Start()    //执行逻辑{char buffer[1024];while(true){   //接收数据//ssize_t recvfrom(套接字,缓冲区,缓冲区大小,flag = 0,client的IP和port,实际结构体大小);struct sockaddr_in far; //远端定义结构体socklen_t len = sizeof(far); int n = recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&far,&len);if(n > 0) buffer[n] = '\0';else continue;std::string clientip = inet_ntoa(far.sin_addr); //ipv4的地址从二进制转化为点分十进制的函数uint16_t clientport = ntohs(far.sin_port);      //将网络字节序转化为一个本地主机字节序std::cout<< clientip << "-" << clientport << "#" << buffer << std::endl;//发送数据//ssize_t sendto(套接字,发的数据,数据大小,flag = 0,(struct sockaddr*)&far,sizeof(far));sendto(_sock,buffer,sizeof(buffer),0,(struct sockaddr*)&far,sizeof(far));}}private:int _sock; //套接字uint16_t _port; //端口};

2.3服务器端server.cc

在服务器端的使用中,我们采用智能指针unique_ptr来帮助资源创建以及销毁,在使用中,我们调用以上server.hpp中类的初始化与启动函数即可.

//输出格式说明:./udp_server portstatic void usage(string proc)//使用手册
{std::cout << "Usage:\n\t" << proc << "port\n" <<std::endl;}int main(int argc,char* argv[]) //获取到参数{if(argc != 2) //若输入参数不是两个的话,就弹出使用手册 {usage(argv[0]);exit(-1);}uint16_t port = atoi(argv[1]); //获取到端口直接进行构造后面std::unique_ptr<UdpServer> ptr(new UdpServer(port));ptr->InitServer();ptr->Start();return 0;
}

2.4客户端Client.cc

在客户端中我们首先需要知道主函数的服务端的ip和端口,也就是我们需要从输入的参数来知道服务端是谁?之后由用户输入消息后发送给服务器端并输出。


// 执行格式:./udp_client ip serverport
static void usage(std::string proc) //使用手册{std::cout << "Usage:\n\t" << proc << "port\n" <<std::endl;}
int main(int argc,char* argv[])
{if(argc != 3)		//如果输入参数个数不是3个就弹出使用手册{usage(argv[0]);exit(-1);}//从主函数获取到了服务端的ip和端口std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);int sock = socket(AF_INET,SOCK_DGRAM,0);  //创建套接字if(sock < 0){std::cerr << "create socket errno" <<std::endl;exit(-1);}//明确server是谁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());//这里client一定需要绑定bind 不过由os来帮我们做,因为OS需要随机分配端口,防止冲突//用户输入while(true){std::string message;std::cout<< "please Enter#  ";std::cin >> message;sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));//接收消息char buffer[1024];struct sockaddr_in temp;socklen_t len = sizeof(temp); int n = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);if(n > 0){buffer[n] = 0;std::cout << "server echo#  " << buffer << std::endl; }}return 0;
}

最后执行后我们便可以看出结果: 说明执行成功!

在这里插入图片描述

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

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

相关文章

docker安装clickhouse

安装 docker安装 创建clickhouse目录 mkdir -P /data/clickhouse/datamkdir -P /data/clickhouse/confmkdir -P /data/clickhouse/log 拉取镜像 这里直接拉取最新镜像, 如果需要某个特定版本, 则再拉取的时候指定版本号即可. docker pull clickhouse/clickhouse-server 启动临…

Springboot实现ENC加密

Springboot实现ENC加密 1、导入依赖2、配置加密秘钥&#xff08;盐&#xff09;3、获取并配置密文4、重启项目测试5、自定义前缀、后缀6、自定义加密方式 1、导入依赖 关于版本&#xff0c;需要根据spring-boot版本&#xff0c;自行修改 <dependency><groupId>co…

STM32 CubeMX (第四步Freertos内存管理和CPU使用率)

STM32 CubeMX STM32 CubeMX &#xff08;第四步Freertos内存管理和CPU使用率&#xff09; STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1&#xff08;不要选择滴答定时器&#xff1b;滴答定时器留给OS系统做时基&#xff09;使用STM32 CubeMX 库&#xff0c;配置Fr…

java maven项目打jar包发布(精简版)

目录 一、maven打包 二、安装jdk环境 三、安装mysql 四、jar包传输到服务器 一、maven打包 先clean再package target文件夹下面有生成一个jar包 二、安装jdk环境 1、下载jdk cd /usr/local wget https://repo.huaweicloud.com/java/jdk/8u201-b09/jdk-8u201-linux-x64.tar.…

RocketMQ消息存储

一、存储介质 ● 关系型数据库DB Apache下开源的另外一款MQ—ActiveMQ (默认采用的KahaDB做消息存储)可选用JDBC的方式来做消息持久化&#xff0c;通过简单的xmI配置信息即可实现JDBC消息存储。由于&#xff0c;普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下&a…

uniapp小程序位置信息配置

uniapp 小程序获取当前位置信息报错 报错信息&#xff1a; getLocation:fail the api need to be declared in the requiredPrivateInfos field in app.json/ext.json 需要在manifest.json配置文件中进行配置&#xff1a;

期权是什么?期权的优缺点是什么?

期权是一种合约&#xff0c;有看涨期权和看跌期权两种类型&#xff0c;也就是做多和做空两个方向&#xff0c;走势标的物对应大盘指数&#xff0c;这也是期权与其他金融工具的主要区别之一&#xff0c;可以用于套利&#xff0c;对冲股票和激进下跌的风险&#xff0c;下文介绍期…

安全(权限)框架Shiro概述及原理

1.1 Shiro是什么 Apache Shiro是一个功能强大且易于使用的Java安全&#xff08;权限&#xff09;框架。Shiro可以完成&#xff1a;认证、授权、加密、会话管理、与Web集成、缓存 等。借助Shiro您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的Web和企业应用…

opencv 水果识别+UI界面识别系统,可训练自定义的水果数据集

目录 一、实现和完整UI视频效果展示 主界面&#xff1a; 测试图片结果界面&#xff1a; 自定义图片结果界面&#xff1a; 二、原理介绍&#xff1a; 图像预处理 HOG特征提取算法 数据准备 SVM支持向量机算法 预测和评估 完整演示视频&#xff1a; 完整代码链接 一、…

MyBatis分页思想和特殊字符

目录 一、MyBatis分页思想 1.1 使用场景 1.2 代码演示 二、MyBatis特殊字符 2.1代码演示 一、MyBatis分页思想 1.1 使用场景 Mybatis分页应用场景&#xff1a; MyBatis是一个Java持久层框架&#xff0c;它提供了一种将SQL查询和结果映射到Java对象的简单方式。分页是MyBa…

金融学博迪第4章作业

金融学第四章作业题目 1.推导名义利率i、实际利率r与通货膨胀率ρ之间的关系式。 注:名义利率(nominal interest rate)用in表示,实际利率(real interest rate)用ir表示,通货膨胀率用ρ表示。 答:假设现在用 元购买价格为 的商品,可以得到个 单位商品。如果推迟至一…

GitLab-CI 指南

GitLab CI 指南 前置工作 部署GitLab 部署GitLab-Runner 注册Runner到GitLab docker exec -it gitlab-runner bash # 进入容器 gitlab-runner register #调用register命令开始注册 # 在Gitlab Setting中找到Runners,如下图所示Enter the GitLab instance URL (for example, …

Linux下的系统编程——vim/gcc编辑(二)

前言&#xff1a; 在Linux操作系统之中有很多使用的工具&#xff0c;我们可以用vim来进行程序的编写&#xff0c;然后用gcc来生成可执行文件&#xff0c;最终运行程序。下面就让我们一起了解一下vim和gcc吧 目录 一、vim编辑 1.vim的三种工作模式 2.基本操作之跳转字符 &a…

Unity ProBuilder SetUVs 不起作用

ProBuilder SetUVs 不起作用 &#x1f41f; 需要设置face.manulUV true public static void Set01UV(this ProBuilderMesh mesh){foreach (var face in mesh.faces){face.manualUV true;//设置为手动uv}var vertices mesh.GetVertices().Select(v > v.position).ToArray(…

kubernetes--技术文档-真--集群搭建-三台服务器一主二从(非高可用)附属文档-使用不同运行商服务器-搭建公网集群

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;版本&#xff01;&#xff01;&#xff01;&#xff01; 使用公网初始化 Kubernetes 需要 Kubernetes 版本 1.19 或更高版本。在早期的版本中&#xff0c;Kubernetes 还不支持公网初始化。因此&#xff0c;请确保…

15.树与二叉树基础

目录 一. 树&#xff0c;基本术语 二. 二叉树 &#xff08;1&#xff09;二叉树 &#xff08;2&#xff09;满二叉树 &#xff08;3&#xff09;完全二叉树 三. 二叉树的性质 四. 二叉树的存储结构 &#xff08;1&#xff09;顺序存储结构 &#xff08;2&#xff09;链…

单片机UART一对多:同时读取多个传感器基于modbus协议

文章目录 背景MODBUS协议介绍UART接口改一对多参考链接 背景 很多传感器现在都做成了串口模块&#xff0c;如激光测距传感器TOF050&#xff0c;在开发时使用串口功能模块不仅大大加快了我们的开发进度&#xff0c;还能降低功能模块直接的耦合度&#xff0c;专业是功能交给专业…

PMP证书的正确打开方式 get✓

在职场竞争日益激烈的今天&#xff0c;拥有一项专业认证成为了许多人提升自身竞争力的必备条件。而作为项目管理领域的顶级认证&#xff0c;PMP证书备受关注。不过&#xff0c;很多人对于PMP证书的费用颇有顾虑。那么&#xff0c;PMP证书有什么补贴政策呢&#xff1f;下面就为大…

数据库——Redis 常见数据结构以及使用场景分析

文章目录 1. string2. list3. hash4. set5. sorted set 你可以自己本机安装 redis 或者通过 redis 官网提供的在线 redis 环境。 1. string 介绍 &#xff1a;string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的&#xff0c;但是 Redis 并没有使用 C 的字符串…

基于JAVA SpringBoot和UniAPP的宠物服务预约小程序

随着社会的发展和人们生活水平的提高&#xff0c;特别是近年来&#xff0c;宠物快速进入人们的家中&#xff0c;成为人们生活中重要的娱乐内容之一&#xff0c;过去宠物只是贵族的娱乐&#xff0c;至今宠物在中国作为一种生活方式得到了广泛的认可&#xff0c;随着人们精神文明…