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

Linux网络:使用UDP实现网络通信(服务端客户端)

文章目录

      • 1. UDP网络程序的服务端
        • 1.1 如何在UDP中读取数据
        • 1.2 如何在UDP中发送数据
        • 1.3 谈谈IP地址和Port端口号
      • 2. UDP网络程序的客户端
        • 2.1 创建UDP套接字
        • 2.2 绑定套接字
        • 2.3 获取服务端信息
        • 2.4 读取和发送数据

  • 序:在上以章中,我们对使用UDP实现网络通信的服务端部分实现了套接字的创建与绑定,深入了解了UDP是面向数据报的,以及了解到在绑定过程对网络接口统一抽象化的struct sockaddr结构体,深入了解了##的作用,而本篇文章将继续了解使用UDP实现网络通信的服务端和客户端细节,深入探寻服务端与客户端的接收和发送。

1. UDP网络程序的服务端

1.1 如何在UDP中读取数据

UDP特殊就特殊在没法使用read和write,这两个接口是面向字节流的,而UDP是面向数据报的,所以UDP要获取数据就要使用recvfrom函数接口

recvfrom函数接口:
在这里插入图片描述

需要包含下面两个库
#include<sys/types.h>
#include<sys/socket.h>ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);

该函数的第一个参数表示从指定的套接字中获取数据
第二个参数表示读到的报文的时候所需要的缓冲区,第三个参数表示该缓冲区的长度,这样读到数据后,该数据就会被放在该缓冲区当中
第四个参数设为0,默认使用阻塞方式,当然该参数不止能设置为0,还能设置为其他数字,但在该片文章的讨论中,默认设置为0

问题一:收到一条消息,需不需要知道这条消息是谁发的?为什么要知道?

因为我们收到对应的信息的时候,基本上都需要返回消息给发送者

所以,要知道是谁给服务器发的信息就看看第五个和第六个参数,这两个参数是输出型参数,其中第六个参数是输入输出参数,表示该结构体对象的大小,也就是说,如果有人访问该服务器,那么此时是谁给服务器发的消息,发送端的套接字信息就会保存到第五个参数的指针中,所以,该内存空间是需要我们自己去定义的,因为这是一个输出型参数,但由于这个指针的类型是struct sockaddr,但是我们在进行udp网络通信时,用到的是struct sockaddr_in,所以我们要定义一个sockaddr_in的结构体,然后强转为sockaddr后再传进去,因为网络接口统一抽象化!!!

返回值RETURN_VALUE,如果成功了就会返回收到了多少个字节,失败的话,-1被返回,错误码被设置

class UdpServer{
public:void Run(func_t func){_is_running = true;char inbuffer[size];while(_is_running){struct sockaddr_in client;socklen_t len=sizeof(client);ssize_t n = recvfrom(_sockfd, inbuffer,sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);if(n < 0){log(Warning, "recvfrom error,errno: %d ,strerror: %s", errno, strerror(errno));continue;}inbuffer[n]=0;}}private:int _sockfd; //网络文件描述符std::string _ip; //IP地址, 0.0.0.0 任意地址绑定uint16_t _port;  //表示服务器进程的端口号bool _is_running;//表示服务器是否在运行
};
1.2 如何在UDP中发送数据

由于UDP是面向数据报,无法使用read和write,所以UDP要发送数据就要使用sendto函数接口

sendto函数接口:

在这里插入图片描述

sendto函数的接口和recvfrom一模一样,只要搞懂了recvfrom就会用sendto,要注意的事,该函数的最后两个参数是输入型参数,由于在recvfrom的时候,我们就知道了是谁发数据给我们的

class UdpServer{
public:void Run(func_t func){_is_running = true;char inbuffer[size];while(_is_running){//读数据//......inbuffer[n]=0;// std::cout<<n<<":"<<inbuffer<<std::endl;//充当一次数据处理std::string info = inbuffer;std::string echo_string = func(info); // std::cout<<echo_string<<std::endl;sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,(struct sockaddr*)&client, len);}}private:int _sockfd; //网络文件描述符std::string _ip; //IP地址, 0.0.0.0 任意地址绑定uint16_t _port;  //表示服务器进程的端口号bool _is_running;//表示服务器是否在运行
};

至此我们已经实现了UDP网络通信的服务端的收和发的基本功能。

问题二:如何看我们的服务器成功跑起来了呢?

netstat -naup//使用该命令就能查看

其中n表示,能显示出数字的数据全部显示成数字,不带n的话,有些数据就会显示为字符串
其中p表示,将对应的pid进程信息页表示出来
其中a表示all,将所有相关的信息列出来
其中u表示udp信息

Proto表示用的协议是什么
Recv-Q表示收到的报文的个数
Send-Q表示发出的报文的个数
Local Address本地的地址(IP地址+端口号)
Foreign Address表示远端(只要有这个0.0.0.0或者 * 的就表示,当前主机能接受任何客户端给当前主机发的消息)
State表示状态
在这里插入图片描述

从上图我们看到,我们程序已经启动了

1.3 谈谈IP地址和Port端口号

关于IP地址:

公网IP绑定问题
在这里插入图片描述

当我们将自己的云服务器的公网IP传进去后,就会报错:
在这里插入图片描述

这段代码如果是虚拟机_运行,代码就是可以运行的,但是由于云服务器禁止直接bind公网IP,所以就会报错,一般写服务器的时候,_ 不会直接绑定IP地址,有可能一台主机上有两到三个网卡,有两到三个IP,如果绑定了某个IP,就无法获取到发送到另外两个IP的数据,所以一般bind(IP:0),直接绑定0,即凡是发给我这台主机的数据,我们都要根据端口号向上交付(好处:1. 好写2. 收到的数据是全的,完整的)

关于Port端口号:

在这里插入图片描述

当我们将端口号改为80时:
在这里插入图片描述

[0,1023]:系统内定的端口号,一般都要有固定的应用层协议使用,http:80,https:443,mysql:3306… ,一般选择绑定的端口是1024+的端口号
服务端的端口号一般是固定,但是客户端的端口号就是系统自由随机分配的,只要保证唯一性就行
127.0.0.1:本地环回地址,通常用来进行客户端,服务端的测试

2. UDP网络程序的客户端

2.1 创建UDP套接字

既然要实现网络通信,就必须先创建套接字

int main()
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0) {log(Fatal, "create socket error,errno: %d ,strerror: %s", errno, strerror(errno));return 1;}log(Info,"create socket success, sockfd: %d",sockfd);return 0;
}
2.2 绑定套接字

client要bind吗?肯定是要的,但是不需要用户去显式的bind!!!一般由操作系统自由随机选择
一个端口号只能被一个进程bind,对server是如此,对client,也是如此!!!
其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以了
系统什么时候给我bind?首次发送数据的时候

2.3 获取服务端信息

想要获取服务端的信息,可以通过命令行参数来获取,规定好格式
将对应的数据填充进struct sockaddr_in server中

// ./udpclient serverip serverport
void Usage(const string &proc)
{cout<<"\n\rUsage:"<< proc<<" serverip serverport\n"<<endl;return ;
}int main(int argc,char* argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}string serverip =argv[1];uint16_t serverport =stoi(argv[2]);struct sockaddr_in server;socklen_t len = sizeof(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());return 0;
}
2.4 读取和发送数据

发什么,给谁发?

int main(int argc,char* argv[])
{//......string message;char buffer[size];while(true){cout<<"Please Enter# ";getline(cin,message);//1.数据 2.发给谁ssize_t s = sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,len);struct sockaddr_in temp;//bzero(&server, sizeof(temp));socklen_t lenp = sizeof(temp);ssize_t n = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&lenp);if(n > 0){buffer[n]=0;cout<<buffer<<endl;}    }close(sockfd);return 0;
}

总结:

本篇博客详细介绍了UDP网络编程的核心内容,包括服务端和客户端的实现。服务端重点讲解了如何使用recvfrom接收数据和sendto发送数据,以及如何处理客户端地址信息;客户端则介绍了套接字创建、隐式绑定、服务端信息配置及数据收发流程。此外,还探讨了IP与端口相关知识,如公网IP绑定限制、知名端口范围等。

http://www.dtcms.com/a/410800.html

相关文章:

  • 免费个人网站手机app开发技术
  • 论坛网站开发demo查关键词排名软件
  • 茶吧机方案MCU控制方案开发相关资料-茶吧机单片机应用
  • 网站是如何做的好什么网站权重快
  • VR禁毒单车骑行:穿越百年禁毒史的沉浸式教育革命
  • k8s基础
  • 益阳北京网站建设网站代运营要多少费用
  • PHP使用Imagick库操作tiff
  • 海阔淘宝客助手wordpress演示站 | 紫色清新商城模板枣阳网站建设公司
  • 孤岛水流问题
  • SWAT模型在水文水资源、面源污染模拟中的实践技术应用及典型案例分析
  • 【C++】二叉搜索树的模拟实现和二叉树进阶OJ
  • Redis - Bitmap 类型
  • AUTOSAR 自适应平台 如何保证时间同步的可靠性?出现故障怎么办?
  • 设计互动网站建设做网页向网站提交数据
  • 北京架设网站杭州 建设网站制作
  • 学习笔记:Vue Router 编程式导航详解
  • Centos 7 创建ftp 权限最大支持上传删减
  • 哪家公司设计网站学用php做网站
  • 租用网站服务器网站改版 升级的目的是什么意思
  • java 项目docker 部署。
  • 【知识库文档】数据预处理PDF文档转成MD格式(gptpdf )
  • Java 高效实现 PowerPoint 转 PDF:不依赖Office
  • 新奇特:神经网络烘焙坊(下),万能配方的甜蜜奥义
  • 翁恺老师C语言基础教程代码学习
  • 天津建设网站的公司哪家好云南昆明网站建设价格
  • 网站开发需求书中山网络推广公司
  • RPC在分布式存储系统中的应用
  • 交互式手机网站网站建设功能
  • 07.容器监控