Linux_实现UDP网络通信

目录

1、实现服务器的逻辑

1.1 socket

1.2 bind

1.3 recvfrom

1.4 sendto

1.5 服务器代码

2、实现客户端的逻辑 

2.1 客户端代码

3、实现通信

结语 


前言:

        在Linux下,实现传输层协议为UDP的套接字进行网络通信,网络层协议为IPv4,需要用到的接口有以下4个:socket、bind、recvfrom、sendto。具体实现方法:在云服务器上创建一个服务器进程和一个客户端进程,让客户端向服务器发送消息,并且服务器收到消息后可以反馈给对方。

        示意图如下:

1、实现服务器的逻辑

        按照以下函数的调用顺序,即可实现服务器方的UDP通信。

1.1 socket

        首先明确使用IPv4协议和UDP协议后,先调用接口socket,让其返回一个网络文件描述符给到我们,socket函数介绍如下:

#include <sys/types.h>          
#include <sys/socket.h>int socket(int domain, int type, int protocol);
//domain表示网络协议族,AF_INET为IPv4,AF_INET6为IPv6
//type表示传输层协议,SOCK_STREAM为TCP,SOCK_DGRAM为UDP
//protocol表示指定特定的协议,一般前两个参数的协议足矣,这里填0即可//调用成功返回一个类型文件描述符的网络描述符,失败返回-1

1.2 bind

        定义一个struct sockaddr_in类型的变量,该变量的作用是为调用bind接口做准备,该变量里面有3个信息需要填写,分别是:1、传输层协议,2、为该进程设置的端口号,3、该主机的IP地址。其中,端口号和IP地址需要对其转换成大端字节序,因为网络规定传输的数据采用大端字节序传输,这里介绍两个接口可以帮助我们直接将端口号和IP转换成大端字节序,接口介绍如下:

#include <arpa/inet.h>uint16_t htons(uint16_t hostshort);//常用来转换端口号
//将主机字节序转换成网络字节序并返回#include <arpa/inet.h>  unsigned long inet_addr(const char *cp);//常用来转换IP地址
//如果cp指向的是IP地址的字符串形式,那么会将其转换为网络字节序的IP地址
//并且以无符号的长整型返回

        待struct sockaddr_in类型的变量的字段填写完毕后,下一步就是进行绑定操作,绑定的目的是将socket返回的网络描述符与struct sockaddr_in类型的变量进行绑定,即可以通过网络描述符来找到对应的ip地址以及端口号,简单来说,通过网络描述符就能找到对应主机的对应进程


        接着就是调用bind接口进行绑定了,bind接口介绍如下:

int bind(int socket, const struct sockaddr *address,socklen_t address_len);
//socket表示要绑定的网络描述符
//address表示指向struct sockaddr类型的变量
//address_len表示address指向变量的大小//调用成功返回0,失败返回-1

1.3 recvfrom

         recvfrom接口有点类似文件操作中的read接口,都带有接收的意思。recvfrom接口用于从网络描述符中读取对方主机发送的数据,并且还可以将对方主机的地址信息(IP和端口号)给记录下来,该接口的介绍如下:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,  struct sockaddr *src_addr, socklen_t *addrlen);
//sockfd表示读取的网络描述符
//buf表示存放读取数据的目标缓冲区
//len表示期望读取内容的大小
//flag表示设置该函数的模式,比如阻塞或非阻塞,通常设为0表阻塞
//src_addr是个输出型参数,用于保存发送方的地址信息
//addrlen表示src_addr指向变量的大小//成功返回接收的字节数,若sockfd关闭返回0,失败返回-1

1.4 sendto

        如果说recvfrom接口类似read接口,那么sendto就好比write接口,他能够往网络文件描述符内写入数据,即发送方就是调用sendto接口向接收方发送数据,sendto和recvfrom相互搭配实现网络通信。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表示要发送数据的长度
//flag表示设置该函数的模式,比如阻塞或非阻塞,通常设为0表阻塞
//dest_addr指向的结构体里包含接收方的IP和端口号,依靠他们才能找到接收方
//addrlen表示dest_addr指向结构体的大小

1.5 服务器代码

        将服务器封装成一个类,并把服务器的端口号、ip地址、网络描述符作为该类的成员变量,这样就可以对上述功能逻辑进行分层了,服务器类代码如下:

#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 <cstdio>
#include <unistd.h>
using namespace std;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){}void Init(){// 1. 创建udp socketsockfd_ = socket(AF_INET, SOCK_DGRAM, 0); printf("socket create success, sockfd: %d\n", sockfd_);// 2. bind socketstruct 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()); bind(sockfd_, (const struct sockaddr *)&local, sizeof(local));printf("bind success, errno: %d, \err string: %s\n", errno, strerror(errno));}void Run() // 对代码进行分层{char inbuffer[size];while(true){struct sockaddr_in client;socklen_t len = sizeof(client);//服务器先接收消息ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1,0, (struct sockaddr*)&client, &len);cout<<"客户端说:"<<inbuffer<<endl;inbuffer[n] = 0;std::string info = inbuffer;std::string echo_string = "服务器的回答:"+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_; // ip地址uint16_t port_;  // 服务器端口号
};

         该服务器的逻辑是先接收客户端发送的消息,然后利用recvfrom函数保存客户端的地址信息,再使用sendto函数对客户端进行信息的反馈。

2、实现客户端的逻辑 

        客户端逻辑和服务器逻辑几乎一样,第一步必须调用socket创建网络描述符,但是第二步客户端不需要进行bind绑定,因为服务器之所以需要绑定是因为服务器必须手动自定义一个端口号,目的就是要让该端口号可见,以便让客户端知道该端口号,这样客户端才能通过该端口号定位服务器。而客户端不需要自定义端口号,因为客户端的主要任务是给服务器发送信息,这个过程服务器是不需要知道客户端的端口号也可以接收客户端的信息,因此客户端的端口号只需要保证其唯一性即可,即交给操作系统来生成,当首次发送数据的时候操作系统就会为客户端生成端口号。

2.1 客户端代码

        客户端代码如下:

#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);}//从命令行参数拿到ip地址和端口号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());//转换网络字节序// 1. socket拿到网络描述符int sockfd = socket(AF_INET, SOCK_DGRAM, 0);socklen_t len = sizeof(server);string message;char buffer[1024];while (true){cout << "Please Enter@ ";getline(cin, message);//2. 向服务器发送信息sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, len);struct sockaddr_in temp;socklen_t len = sizeof(temp);//3. 打印来自服务器的信息recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);cout << buffer << endl;}close(sockfd);//关闭文件描述符return 0;
}

3、实现通信

        实现通信的前提是让服务器以进程的形式跑起来,然后再让客户端也以进程的形式跑起来,因为网络通信的本质就是进程间通信,而上述代码中客户端本身就是在main函数中执行的,所以此时客户端可以直接运行,但是服务器还只是个类,因此现在只需要用服务器类实现一个main函数,即可完成两个进程的运行。

        服务器进程代码如下: 

#include "UDPser.hpp"
#include <memory>
#include <cstdio>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}//从命令行参数拿到端口号uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->Init();svr->Run();return 0;
}

        运行结果:

        从结果可以看到目前可以正常的进行客户端与服务器之间的通信。

结语 

        以上就是关于实现UDP网络通信的讲解,实现UDP的核心在于对套接字的理解以及相关接口的逻辑使用,其实只需要记住只要涉及到网络通信,那么socket和bind函数是必须在最开始就调用的。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!! 

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

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

相关文章

k8s+containerd(kvm版)

k8s&#xff08;Kubernetes&#xff09;是由Gogle开源的容器编排引擎&#xff0c;可以用来管理容器化的应用程序和服务&#xff0c;k 高可用&#xff1a;系统在长时间内持续正常地运行&#xff0c;并不会因为某一个组件或者服务的故障而导致整个系统不可用可扩展性&#xff1a…

【SpringBoot】 jasypt配置文件密码加解密

目前我们对yml配置文件中的密码都是明文显示&#xff0c;显然这不安全&#xff0c;有的程序员离职了以后可能会做一些非法骚操作&#xff0c;所以我们最好要做一个加密&#xff0c;只能让领导架构师或者技术经理知道这个密码。所以这节课就需要来实现一下。 我们可以使用jasypt…

爬虫学习3:爬虫的深度爬取

爬虫的深度爬取和爬取视频的方式 深度爬取豆瓣读书 import time import fake_useragent import requests from lxml import etree head {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 …

陶德:边种田边写代码,3年300万行,一个人写出了“国产大满贯QT”

这是《开发者说》的第12期&#xff0c;本期我们邀请的开发者是陶德&#xff0c;从小在国企矿山里长大&#xff0c;计算机成绩是文科班里最差的一个&#xff0c;毕业两年找不到工作&#xff0c;睡过公园&#xff0c;讨过剩饭&#xff0c;用打魔兽世界的方式磨炼技术&#xff0c;…

.NET 8+Vue2 部署到Window Server

.NET 8Vue2 部署到Window Server 1 配置环境 1.1 下载安装.NET 8 SDK&#xff0c;下载时需注意目标服务器的系统类型&#xff08;64位 or 32位&#xff09; https://dotnet.microsoft.com/zh-cn/download 1.2 下载安装SQL Server数据库&#xff08;服务和管理工具&#xff…

海外短剧系统搭建开发定制,H5/APP源码搭建部署,支持二开

目录 前言&#xff1a; 一、系统功能 二、部署流程 前言&#xff1a; 海外短剧系统搭建部署&#xff0c;前端uniapp&#xff0c;PHP语言。支持二开功能。 一、系统功能 以下是改写后的内容&#xff1a; 1. 多语言环境集成 —— 提供一键式翻译功能&#xff0c;轻松切换多…

【Vue3】计算属性

【Vue3】计算属性 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本文内…

IT common sense常识

how to input formative json data in console console.log({"message": [{"cat_id": 1,"cat_name": "大家电","cat_pid": 0,"cat_level": 0,"cat_deleted": false,,,,})2) how to clear unecessary c…

基于生物地理算法的MLP多层感知机优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 生物地理算法&#xff08;BBO&#xff09;原理 4.2 多层感知机&#xff08;MLP&#xff09; 4.3 BBO优化MLP参数 5.完整程序 1.程序功能描述 基于生物地理算法的MLP多层感知机优化mat…

【数学建模】——前沿图与网络模型:新时代算法解析与应用

目录 1.图与网络的基本概念 1. 无向图和有向图 2. 简单图、完全图、赋权图 3. 顶点的度 4. 子图与图的连通性 2.图的矩阵表示 1. 关联矩阵 2. 邻接矩阵 3.最短路问题 1.Dijkstra 算法 2.Floyd 算法 4.最小生成树问题 1.Kruskal 算法 2.Prim 算法 5.着色问题 6.…

[Linux]Mysql之主从同步

AB复制 一、主从复制概述 主从复制&#xff0c;是用来建立一个和主数据库完全一样的数据库环境&#xff0c;称为从数据库&#xff1b;主数据库一般是准实时的业务数据库。 主从复制的作用 1.做数据的热备&#xff0c;作为后备数据库&#xff0c;主数据库服务器故障后&#xf…

数据价值网络:开启数据驱动的新时代

数据价值网络&#xff1a;开启数据驱动的新时代 数据已成为第五大生产要素&#xff0c;其价值的实现有赖于广泛的应用和高效的流通。数据价值网络涵盖了从数据生成到运用的一系列价值创造环节&#xff0c;各企业基于自身资源禀赋和比较优势进行专业化分工。政策的支持和技术的创…

入门C语言Day15——关系条件逻辑操作符

今天来学习操作符中的一些内容&#xff0c;主要讲的是关系&条件&逻辑操作符 1.关系操作符 首先要来了解一下什么是关系操作符&#xff0c;关系操作符其实就是关系运算符&#xff0c;关系运算符又和关系表达式有关。 C语言中用于比较的表达式&#xff0c;就被称为 “关…

Cadence学习笔记(十三)--设置边框与异形铺铜

直接导入板框用小眼睛可以看到所有的都是线的属性&#xff1a; 那么如何让它变成板框呢&#xff1f;这里先跳转到下图中的层&#xff1a; 将Z--CPOY这一层变成shape区&#xff1a; 之后用Z--copy: Z--COPY设置如下参数&#xff0c;铺铜内缩20mil: 之后选择长方形铺铜就可以了&…

【第5章】Spring Cloud之Nacos服务注册和服务发现

文章目录 前言一、提供者1. 引入依赖2.配置 Nacos Server 地址3. 开启服务注册 二、消费者1. 引入依赖2.配置 Nacos Server 地址3. 开启服务注册 三、服务列表四、服务发现1. 获取服务列表2. 测试2.1 获取所有服务2.2 根据服务名获取服务信息 五、更多配置项总结 前言 本节通过…

CSS 两种盒模型 box-sizing content-box 和 border-box

文章目录 Intro谨记box-sizing 两个不同赋值的效果区别&#xff1f;宽高的数值计算标准盒模型 box-sizing: content-box; box-sizing 属性的全局设置 Intro 先问一句&#xff1a;box-sizing 和它的两个属性值是做什么用的&#xff1f;以前我并不知道它的存在&#xff0c;也做…

集成学习在数学建模中的应用

集成学习在数学建模中的应用 一、集成学习概述&#xff08;一&#xff09;基知&#xff08;二&#xff09;相关术语&#xff08;三&#xff09;集成学习为何能提高性能&#xff1f;&#xff08;四&#xff09;集成学习方法 二、Bagging方法&#xff08;一&#xff09;装袋&…

昇思25天学习打卡营第22天 | DCGAN生成漫画头像

昇思25天学习打卡营第22天 | DCGAN生成漫画头像 文章目录 昇思25天学习打卡营第22天 | DCGAN生成漫画头像DCGAN模型数据集数据下载和超参数创建数据集数据集可视化 搭建网络生成器判别器损失函数和优化器 模型训练总结打卡 DCGAN模型 深度卷积对抗生成网络&#xff08;Depp Co…

python打包exe文件-实现记录

1、使用pyinstaller库 安装库&#xff1a; pip install pyinstaller打包命令标注主入库程序&#xff1a; pyinstaller -F.\程序入口文件.py 出现了一个问题就是我在打包运行之后会出现有一些插件没有被打包。 解决问题&#xff1a; 通过添加--hidden-importcomtypes.strea…