网络编程套接字(3)——Java数据报套接字(UDP协议)

目录

一、Java数据报套接字通信模型

二、UDP数据报套接字编程

1、DatagramSocket

        (1)DatagramSocket构造方法

        (2)DatagramSocket方法

2、DatagramPacket

        (1)DatagramPacket构造方法

        (2)DatagramPacket方法

3、InetSocketAddress

三、代码示例:回显服务器

        1、服务器代码

     代码解析

          2、客户端代码

代码解析

3、创建DatagramPacket三种不同的参数场景

4、执行代码

5、客户端和服务器交互的过程


一、Java数据报套接字通信模型

        对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报。

        java中使用UDP协议通信,主要基于DatagramSocket类来创建数据报套接字,并使用DatagramSocket作为发送或接收的UDP数据报。对于一次发送及接收UDP数据报的流程如下:

        以上只是一次发送端的UDP数据报发送,及接收端的数据报接收,并没有返回的数据。也就是只有请求,没有响应。对于一个服务端来说,重要的是提供多个客户端的请求处理响应,流程如下:


二、UDP数据报套接字编程

        Java把系统原生的API进行了封装,供我们使用,以下是API介绍,主要核心的类有两个:DatagramSocketDatagramPacket

1、DatagramSocket

        操作系统中,有一类文件就叫做 socket 文件,普通文件、目录文件是在硬盘上的,socket 文件抽象表示了 “网卡” 这样的硬件设备

        进行网络通信最核心的硬件设备就是 网卡

                通过网卡发送数据,就是写 socket 文件。

                通过网卡接受数据,就是读 socket 文件。

        DatagramSocket是 UDP 的 Socket,负责对 socket 文件的读写也就是借助网卡发送和接收数据报

        (1)DatagramSocket构造方法

方法签名方法说明
DatagramSocket()

创建一个UDP数据报套接字的Socket,绑定到本机

任意一个随机端口(一般用于客户端)

DatagramSocket(int port)

创建一个UDP数据报套接字的Socket,绑定到本机

指定的端口(一般用于服务端)

        (2)DatagramSocket方法

方法签名方法说明
void receive(DatagramPacket p)

从此套接字接受数据报(如果没有收到数据报,该

方法会阻塞等待)

void send(DatagramPacket p)从此套接字发送数据包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

2、DatagramPacket

        DatagramPacket是UDP Socket发送和接收的数据报

        UDP面向数据报,每次发送接受数据的基本单位,就是一个UDP数据报,表示了一个UDP数据报。

        (1)DatagramPacket构造方法

方法签名方法说明
DatagramPacket(byte[] buf, int length)

构造一个DatagramPacket以用来接收数据报,接收的

数据保存在字节数组(第一个参数buf)中,接收指定

长度(第二个参数length)

DatagramPacket(byte[] buf, int offset, int length,

SocketAddress address)

构造一个DatagramPacket以用来发送数据包,发送的

数据为字节数组(第一个参数buf)中,从0到指定长度

(第二个参数length)。address指定目的主机IP和端口号

        (2)DatagramPacket方法

方法签名方法说明
InetAddress getAddress()

从接收的数据报中,获取发送端主机IP地址;

或从发送的数据报中,获取接收端主机IP地址

int getPort()

从接收的数据报中,获取发送端主机的端口号;

或从发送的数据报中,获取接收端主机的端口号

byte[] getData()获取数据报中的数据

        构造UDP发送的数据报时,需要传入SocketAddress,该对象可以使用InetSocketAddress来创建。

3、InetSocketAddress

        InetSocketAddress (SocketAddress的子类) 构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

三、代码示例:回显服务器

        在网络编程中,回显服务器是最简单的程序了,相当于网络编程的 hello world

        功能:服务器接受客户端的请求,返回响应。(根据客户端发出的请求,返回啥响应,要更具实际的业务场景来编写代码,这里的案例没啥业务逻辑)

        1、服务器代码

public class UdpEchoServer {private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);//参数里面指定端口号}//服务器的启动逻辑public void start() throws IOException {System.out.println("服务器启动");while (true) {//1、读取请求并解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);//把读到的字节数组,转换成字符串,方便后面的逻辑处理String request = new String(requestPacket.getData(), 0, requestPacket.getLength());//2、根据请求计算响应(对于回显服务器来说,这一步啥也不用做)String response =process(request);//3、把响应返回给客户端//构造一个 DatagramPacket 作为响应对象DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印日志System.out.printf("[%s : %d] req: %s resp: %s", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);System.out.println();}}//计算响应计算方法(这里没逻辑)public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer = new UdpEchoServer(9090);udpEchoServer.start();}
}

     代码解析

        第一步需要创建 DatagramSocket 对象,接下来要操作网卡,都是通过 socket 对象来完成的。socket 对象是在内存中的,操作这个对象就能影响到网卡类似遥控器。

        这个程序需要绑定 / 关联上一个操作系统上的端口号,所以构造方法里传的就是端口号。

        端口号:是一个整数,用来区分一个主机上进行网络通信的程序。一个主机上的一个端口号只能被一个进程绑定,如果一个端口被进程A绑定了,如果进程B绑定这个端口,就会失败,除非进程A释放了这个端口,进程B才能绑定上。

        反过来,一个进程可以绑定多个端口,很常见。端口号和 socket 对象是一一对应的,如果一个进程中有多个socket对象,那这个进程就能绑定多个端口了。

        如图的异常:

        该异常是网络很常见的异常,通常表示 socket 创建失败,比如说端口号被别的进程占用了,就会抛出该异常。

        如图的循环:

        

        对于服务器来说,需要不停的收到请求,返回响应。一个服务器在单位时间能处理的请求越多,那这个服务器就越厉害。

        服务器往往是 7 * 24 小时运行的,要持续不断的运行下去,所以,这里的 while(true) 没有退出的必要,如果要退出,简单粗暴的办法就是 “杀进程”,在 linux 中有个结束进程的命令:kill,带有 -9 是强制退出,不带的话就是告诉进程,你该挂了,而进程可以选择继续工作,也可以选择自挂东南枝。所以才有了 杀进程 这一说法。

        对于内部的逻辑要写的就是如下图:

        receive方法里的参数如图:

 

        里面放的参数是 DatagramPacket 对象,而此处它是 “输出型参数”,前面的文件IO涉及到了,像InputStream里的read方法,里面参数也是输出型参数(字节数组)

        实际上,DatagramPacket 内部就会包含一个字节数组

        如图的 DatagramPacket:

        通过这个字节数组来保存收到的消息正文 (应用层数据包) ,也就是 UDP 数据报的载荷部分。后面的数字大小,就是这个 载荷 有多长,可以灵活设置。

        如图:

        这里的 receive 就能从网卡读到一个 UDP 数据报,这个数据报是被放到 requestPacket 对象中的,其中的 载荷部分 就放到 requestPacket 内置的 字节数组 中了,另外,报头部分也被 request 的其他属性保存了。

        除了 UDP 报头之外,还有其他信息,比如 收到的数据源IP,可以通过 requestPacket 知道源IP 和 源端口

        如果服务器没有收到请求,服务器就会在这里阻塞,等到有客户端的请求过来了,再receive,然后执行下面的逻辑。

        如图,拿到数据报后的解析:

        这里是基于字节数组构造出的String的,字节数组里的内容不一定是二进制数据,也可能是文本数据,把文本数据交给 String 来保存,也是恰到好处,就算是二进制数据,Java的String 也是可以保存的。

        上面的 0 是指的是从 0 号位置开始构造String

        getLength 是获取字节数组中有效数据的长度,用这么长来构造String,这里的有效长度,不一定是4096,4096是最大长度,一定要使用有效长度来构造,如果使用4096,就会生成一个非常长的 String ,后半部分都是空白。

        客户端发来的请求数据,说是存在socket文件中,但要注意,此处的数据是在 socket 的内存缓冲器的,并不是在硬盘上,如图:

        如图:

        这个代码,要根据请求构造 / 计算响应通过 process 方法来完成这个工作

        由于此处是回显服务器,所以就单纯的 return 了,如果是一些具有特点业务的服务器,process 就可以写其他任何你想要的逻辑了。

        当计算完响应,可以 send 时,如图:

        这里的 send 也需要放 DatagramPacket 的对象,作为参数。

        DatagramPacket 的对象参数第一个:不是空白字节数组,直接把 String 包含的字节数组给拎过来了;

        第二个参数:单位是字节,得到字节的长度;这里不能写成:response.length(),这指的是字符个数 ,单位是字符;字符和字节个数肯定是不一样的,除非是标准ASCII表的字符

        第三个参数:requestPacket是客户端发生过来的数据报,得到INetAddress 对象,这个对象包含了 IP 和 端口号,也就是和服务器对端(对应的客户端)的 IP 和 端口。这里是把请求中的源 IP 和 源端口,作为响应的 目的IP 和 目的端口,此时就可以做到把响应返回给客户端了。

        从上面代码中可以看到:

        UDP 是无连接的通信:UDP socket 自身不保存对端的 IP 和 端口,而是在每个数据报中都有一个,另外代码中也没有 “建立连接” 、“接受连接” 的操作。

        不可靠传输:上面代码中体现不到。

        面向数据报:send 和 receive 都是以 DatagramPacket 为单位的。

        全双工:一个 socket 既可以发送,也可以接收。

        打印日志那里:打印客户端 IP 是以点分十进制 的形式打印的,如图:

        后面陆续是打印客户端端口号,打印请求字符串,打印响应字符串

        我们设置服务器端口时的注意事项,如图:

        上面的端口号你可以指定任何你想要的端口,但也是有范围的,通常来说,使用的端口号x范围是:1024 < x < 65535。

        但是也有前提,确保你当前这个端口在当前机器上没有被其他端口占用,如果指定了端口,创建服务器失败的话,换个端口就好了

          2、客户端代码

public class UdpEchoClient {private DatagramSocket socket = null;String serverIp;int serverPort;//此处使用的IP是点分十进制的风格,例如:192.168.0.0public UdpEchoClient(String serverIp, int serverPort) throws SocketException {socket = new DatagramSocket();this.serverIp = serverIp;this.serverPort = serverPort;}public void start() throws IOException {System.out.println("客户端启动");Scanner scanner = new Scanner(System.in);while (true) {System.out.print("->");//要做四个事情//1、从控制台读取要发送的请求数据if(!scanner.hasNext()) {break;}String request = scanner.next();//2、构造请求并发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);//3、读取服务器的响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//4、把响应显示到控制台上String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);udpEchoClient.start();}
}

代码解析

        首先要创建 socket 对象,如图:

        此处是不需要手动指定端口号,让系统随机分配一个端口号即可,而构造方法中传的是服务器IP 和 服务器端口,所以要保存一下,把它们设成客户端的属性。而源 IP 就是客户端的 本机IP,源端口就是 系统分配的空闲端口。

        对于客户端来说,系统随机分配的端口,是空闲的,但如果手动指定端口,是可用,但有可能被别的进程占用。

        同理,服务器端口不一定就100%不会被别的进程占用,但服务器这个机器,是在程序员手里的,程序员可以事先知道服务器上都有哪些端口被使用,就比较有谱;普通用户千千万,其中电脑上的环境也会千差万别,天知道用户按照了什么鬼程序,把你的服务器端口给占用了,你的程序也会因为端口的绑定失败而无法启用,用户就会怪在你头上,说你代码写的不行。

        输入请求,如图:

        注意:从控制台读取,使用 scanner 读取字符串,最好是使用 next,而不是nextLine.

        如果使用 nextLine 读取,就需要手动输入换行符:enter键 来控制。由于 enter键 不仅仅会产生 \n 字符,还好产生其他字符,就会导致读取到的内容容易出问题。

        使用 next 其实是以 “空白符” 作为分隔符,分隔符包括但不限于 换行,回车,空格,制表符,垂直制表符...

        如果是从文件中读取的,那就无所谓了,因为文件中有各种字符。

        构造请求并发送,如图:

        此处创建的 DatagramPacket 对象,里面参数放的先后分别是:请求字符串的字节数组,字节数组的长度,服务器的IP地址,服务器端口。

        读取服务器的响应,如图:

        是用新的字节数组存放响应的载荷

        把响应打印到控制台上,如图:

        先把响应构造成字符串,再打印。这里 responsePacket.getData() 是得到响应的字节数组。

3、创建DatagramPacket三种不同的参数场景

        如图:

        第一种:服务器接收客户端发来的请求,所以要存放在空白数组中。

        第二种:服务器构造好响应,返回给客户端的。

        第三种:客户端构造好请求,发送给服务器的。

4、执行代码

        启动服务器和启动客户端,如图:

        客户端分别输入 hello 和 world,如图:

        服务器这边的显示:

        我们结束客户端程序,在执行客户端程序,不动服务器,输入你好,如图:

        客户端:

        服务器:

        可以看到,客户端的端口号变了,因为终止了上一次的客户端程序,又重新执行客户端程序,系统会随机分配一个空闲端口给客户端使用,所以是不一样的。

5、客户端和服务器交互的过程

        过程如图:

下面是画图解释其中的过程:


都看到这了,点个赞再走吧,谢谢谢谢谢

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

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

相关文章

高吞吐SFTP连接池设计方案

背景 在现代的数据驱动环境中&#xff0c;安全文件传输协议&#xff08;SFTP&#xff09;扮演着至关重要的角色&#xff0c;它提供了一种安全、可靠的文件传输方式。我们目前项目是一个大型数据集成平台&#xff0c;跟上下游有很多文件对接是通过SFTP协议&#xff0c;当需要处…

考研数学——高数:重积分

直角坐标系下二重积分 助记1&#xff1a; 因为一重积分求出的是二维平面的面积&#xff0c;类比得到二重积分得到的是三维的体积 而用之前求旋转体体积的思路&#xff1a;已知截面面积可求得体积。来表示二重积分 在控制一个变量不变&#xff08;x / y&#xff09;时&#x…

基于人工智能自动问答客服系统

在计算机及网络技术的飞速发展的今天&#xff0c;学校教育单位词语管理信息化、网络化是必然的趋势。然而我们利用计算机和网络技术来开发关于聊天自动控制管理系统是必不可少的&#xff0c;使用这样的技术有利于实现场景词语自动执行&#xff0c;减轻管理员的工作负荷&#xf…

【Android 内存优化】KOOM 快手开源框架线上内存监控方案-源码剖析

文章目录 前言OOMMonitorInitTask.INSTANCE.initOOMMonitor.INSTANCE.startLoopsuper.startLoopcall() LoopState.Terminate dumpAndAnalysisdumpstartAnalysisService回到startLoop方法总结 前言 这篇文章主要剖析KOOM的Java层源码设计逻辑。 使用篇请看上一篇: 【Android …

实验二(一):IPV4编址及IPV4路由基础实验

一实验介绍 1.关于本实验 IPv4( Internet Protocol Version 4)是 TCP/IP 协议族中最为核心的协议之一。 它工作在 TCP/IP参考模型的网际互联层&#xff0c;该层与 OSI参考模型的网络层相对应。 网络层提供了无连接数据传输服务&#xff0c;即网络在发送分组时不需要先建立连…

深度学习在硬件和计算平台上的优化:实现更快、更高效的突破

引言 深度学习&#xff0c;作为机器学习领域的一个子集&#xff0c;通过模拟人脑神经元的连接方式&#xff0c;构建复杂的网络结构来处理和分析数据。然而&#xff0c;随着深度学习模型规模的不断扩大和复杂度的提高&#xff0c;其对计算资源的需求也呈指数级增长。因此&#…

远程连接Linux系统

图形化、命令行 对于操作系统的使用&#xff0c;有2种使用形式&#xff1a; 图形化页面使用操作系统 图形化&#xff1a;使用操作系统提供的图形化页面&#xff0c;以获得图形化反馈的形式去使用操作系统。 以命令的形式使用操作系统 命令行&#xff1a;使用操作系统提供的各…

OpenCV学习笔记(五)——图片的缩放、旋转、平移、裁剪以及翻转操作

目录 图像的缩放 图像的平移 图像的旋转 图像的裁剪 图像的翻转 图像的缩放 OpenCV中使用cv2.resize()函数进行缩放&#xff0c;格式为&#xff1a; resize_imagecv2.resize(image,(new_w,new_h),插值选项) 其中image代表的是需要缩放的对象&#xff0c;(new_w,new_h)表…

1950-2022年各区县逐年平均降水量数据

1950-2022年各区县逐年平均降水量数据 1、时间&#xff1a;1950-2022年 2、指标&#xff1a;省逐年平均降水量 3、范围&#xff1a;33省&#xff08;不含澳门&#xff09;、360地级市、2800个县 4、指标解释&#xff1a;逐年平均降水数据是指当年的日降水量的年平均值&…

Swift 入门学习:集合(Collection)类型趣谈-上

概览 集合的概念在任何编程语言中都占有重要的位置&#xff0c;正所谓&#xff1a;“古来聚散地&#xff0c;宿昔长荆棘&#xff1b;游人聚散中&#xff0c;一片湖光里”。把那一片片、一瓣瓣、一粒粒“可耐”的小精灵全部收拢、吸纳的井然有序、条条有理&#xff0c;怎能不让…

chrome高内存占用问题

chrome号称内存杀手不是盖的&#xff0c;不设设置的话&#xff0c;经常被它内存耗尽死机是常事。以下自用方法 1 自带的memory saver chrome://settings/performance PerformanceMemory Saver When on, Chromium frees up memory from inactive tabs. This gives active tab…

【工具】Git的24种常用命令

相关链接 传送门&#xff1a;>>>【工具】Git的介绍与安装<< 1.Git配置邮箱和用户 第一次使用Git软件&#xff0c;需要告诉Git软件你的名称和邮箱&#xff0c;否则无法将文件纳入到版本库中进行版本管理。 原因&#xff1a;多人协作时&#xff0c;不同的用户可…