当前位置: 首页 > news >正文

【网络】UDP协议的简单使用

目录

服务器

客户端

测试


UDP是基于socket进行网络通信的,那我们这篇博客就来介绍一下基于UDP通信的基本流程,先让服务端和客户端进行简单的跨网络通信。

服务器

首先我们需要创建UDP套接字,用到的接口是

man socket

如果要使用UDP通信,第一个参数就传

它用来表示基于网络通信,第二个参数就传

它用来表示是面向数据报,第三个参数我们就默认传0就可以了,因为前两个参数就可以确定它就是UDP协议。它的返回值我们可以认为是一个文件描述符,未来我们就可以向这个文件中去写入或读取东西

接下来是bind socket和网络信息

man 2 bind

第一个参数就是前面接口的返回值,第二个参数是输入性参数,是一个结构体的指针,所以我们要创建这样一个结构体并且填充内容,第三个参数就是第二个参数的大小

所以在绑定之前,我们需要创建struct sockaddr的对象并且填充内容:

因为socket编程是有不同种类的,而操作系统又是拿C语言写的,为了统一接口,我们就用struct sockaddr这个类型,具体到网络通信是struct sockaddr_in这个类型,所以我们创建的是struct sockaddr_in这个类型的变量,到时候进行强制类型转换成struct sockaddr类型

local的类型是一个结构体,结构体中有这些类型:

我们可以看到这里面有port,就是我们传进来的端口号,这个端口号当然也要进行网络传输,因为放到网络中的数据必须是大端,所以我们有接口用来主机序列转网络序列

有了这些接口,我们就可以任意的16位,32位,主机序列转网络序列,网络序列转主机序列。

传完了端口号,就要传ip地址了,就是sin_addr的内容,同样这个也需要主机序列转网络序列,但同时我们知道从命令行参数传进来的IP地址是字符串风格的点分十进制的格式(比如193.168.111.222,这样占15个字节),这样占的空间就大,我们知道每一位都是0~255,这用一个字节来表示整数足够了(这样只占四个字节),所以我们要将字符串风格的点分十进制转换成4字节IP。上面两个工作可以通过下面的接口全部完成

man inet_addr

填充完了我们就可以进行bind了,到此初始化的工作就完成了

下面就是不断的从网络中获取信息,我们可以设置成死循环的形式,我们先实现这样的场景,谁给我们发的,我们再发回去

UDP是面向数据报的,从网络中获取信息的接口是:

man recvfrom

第二个参数是要把从网络中获得的信息存到哪,后两个参数是输出型参数,里面就存放着这个消息是谁发来的一些信息

我们再把消息发回去的接口是:

man sendto

后两个参数就存放着要把消息发送给谁

这样,服务器大致就写好了,还有一些细节需要处理

我们运行主函数的代码时命令行参数就要写上ip和端口号

客户端

客户端要运行主函数肯定是要加上你想访问的服务器的ip地址和端口号,因为这样可以在互联网中唯一确定一个进程,客户端和服务器一样,也是要创建socket,但是不用显示bind,就是我们不要bind,客户端第一次向服务器发消息的时候操作系统会自动bind,因为如果客户端显示的bind,那就意味着不同的客户端可能用一个端口号(比如手机中的抖音和淘宝用一个端口号),这样就导致两个客户端无法同时启动,就会出现端口号冲突的问题。那么就意味着客户端的代码要比服务器要好写

测试

我们像下面这样运行服务器

它是可以成功的,127.0.0.1这个IP我们称为本地环回,它不将服务发送到网络中,可以用于本地通信,常用于代码测试,于是此时我们再起一个SSH渠道就可以实现本地的服务器和客户端通信了

这是本地环回,毕竟不是跨网络的,我们把IP换成服务器的公网IP试一试

可以看到它会报错,因为我们的服务器,无法直接bind公网IP(云服务器不允许),我们也严重不推荐绑定任意一个确定的IP(虚拟机可以)

因为一台设备可能有多个IP地址,我向每个IP地址发,进程都应该收到,如果bind特定的IP地址,那么只有这个IP地址可以收到,所以我们一般传0.0.0.0,简写成0,表示任意IP地址bind,所以之前的代码中还是不要传IP了,因为确定是0

这样,客户端给本地环回和公网IP就都可以发消息了

还有一个查看网络服务的命令

netstat -puan

p代表process进程,u代表udp,a代表all,n代表number,就是能显示成数字的都显示成数字

//UdpServer.hpp#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cerrno>
#include<cstdlib>
#include <cstring>
#include "Log.hpp"enum error
{SOCKET_ERR = 0,BIND_ERR,USAGE_ERR,};
class UdpServer
{
public:UdpServer(uint16_t port) : _port(port), _socketfd(-1){}void InitServer(){// 1.创建UDP socket套接字_socketfd = socket(AF_INET, SOCK_DGRAM, 0);if (_socketfd < 0){LOG(FATAL, "socket error,%d,%s", errno, strerror(errno));exit(SOCKET_ERR);}LOG(INFO, "create socket success,socket:%d", _socketfd);// 填充struct sockaddr_in结构struct sockaddr_in local;bzero(&local, sizeof(local)); // 对空间进行清0local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr=INADDR_ANY;//这个数就是0,因为是0,大小端也就没意义了// local.sin_addr.s_addr = inet_addr(_ip.c_str());int n = bind(_socketfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error,%d,%s", errno, strerror(errno));exit(BIND_ERR);}LOG(INFO, "bind success");}void start(){while (true){char buffer[1024];struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_socketfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){buffer[n]=0;LOG(DEBUG,"get message from[%s,%d]:%s",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buffer);//这样解析就知道是谁发过来的sendto(_socketfd,buffer,strlen(buffer),0,(struct sockaddr*)&peer,len);}}}private:int _socketfd;// std::string _ip;uint16_t _port;
};
//Main.cc#include<iostream>
#include"UdpServer.hpp"
void Usage(char* arg)
{std::cout<<"Usage:  please enter:"<<std::endl;std::cout<<arg<<' '<<"server port"<<std::endl;
}
int main(int argc,char*argv[])
{if(argc!=2){Usage(argv[0]);exit(USAGE_ERR);}Enablescreen();uint16_t port=std::stoi(argv[1]);UdpServer udps(port);udps.InitServer();udps.start();return 0;
}
//UdpClient.cc#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
void Usage(char *arg)
{std::cout << "Usage:  please enter:" << std::endl;std::cout << arg << ' ' << "server ip " << "server port" << std::endl;
}
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);//创建socket套接字int socketfd = socket(AF_INET, SOCK_DGRAM, 0);if (socketfd < 0){std::cerr << "socket error" << std::endl;}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());std::string message;while(true){std::cout<<"Please Enter# ";std::getline(std::cin,message);sendto(socketfd,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(socketfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//接收来自服务器的消息if(n>0){buffer[n]=0;std::cout<<"server echo# "<<buffer<<std::endl;}}return 0;
}

相关文章:

  • Python实现读取Excel数据详细教学版
  • [环境配置]ubuntu20.04安装后wifi有图标但是搜不到热点解决方法
  • 安防监控视频平台LntonAIServer视频智能分析平台新增视频质量诊断功能
  • AF高可用性-主主透明模式/主主虚拟网线
  • 编译 ffmpeg 以支持AVS格式视频解码与解码
  • [Python] 从0到1实现一个简单的数字图像识别大模型
  • H5漂流瓶社交系统源码
  • 肖扬老师好书《微权力下的项目管理(第3版)》读书笔记1
  • kubelet 探针
  • 14.1 为什么说k8s中监控更复杂了
  • 营养作用的对象是有区别的 第八篇
  • 2025年25届新文出炉:如何打造Java SpringBoot Vue个性化课程推荐系统?
  • UMI复现基础环境安装配置全流程(三)——UMI环境搭建
  • 基于javaweb的茶园茶农文化交流平台的设计与实现(源码+L文+ppt)
  • JVM 调优篇1 类的加载器与加载过程
  • “大数据应用场景”之隔壁老王(连载四)
  • codis proxy处理流程
  • eclipse的离线汉化
  • es的写入过程
  • HTTP 简介
  • Java-详解HashMap
  • Laravel5.4 Queues队列学习
  • mysql innodb 索引使用指南
  • PHP面试之三:MySQL数据库
  • Python socket服务器端、客户端传送信息
  • Shadow DOM 内部构造及如何构建独立组件
  • Terraform入门 - 1. 安装Terraform
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 前端知识点整理(待续)
  • 学习HTTP相关知识笔记
  • 《码出高效》学习笔记与书中错误记录
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (备份) esp32 GPIO
  • (二)PySpark3:SparkSQL编程
  • (每日一问)基础知识:堆与栈的区别
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (原創) 未来三学期想要修的课 (日記)
  • (转)C#调用WebService 基础
  • (转)mysql使用Navicat 导出和导入数据库
  • (转载)从 Java 代码到 Java 堆
  • (状压dp)uva 10817 Headmaster's Headache
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .form文件_SSM框架文件上传篇
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET程序员迈向卓越的必由之路
  • .NET中统一的存储过程调用方法(收藏)
  • /tmp目录下出现system-private文件夹解决方法