Java网络编程——基于UDP的数据报和套接字

news/2024/5/18 14:00:44 标签: 网络, java, udp

java.net.ServerSocket与java.net.Socket建立在TCP的基础上。TCP是网络传输层的一种可靠的数据传输协议。如果数据在传输途中被丢失或损坏,那么TCP会保证再次发送数据;如果数据到达接收方的顺序被打乱,那么TCP会在接收方重新恢复数据的正确顺序。应用层无须担心接收到乱序或错误的数据,应用层只需从Socket中获得输入流和输出流,就可以方便地接收和发送数据。当发送方先发送字符串“hello”,再发送字符串“everyone”时,接收方在应用层必定先接收到字符串“hello”,再接收到字符串“everyone”。接收方不会先接收到字符串“everyone”,再接收到字符串“hello”。

TCP的可靠性是有代价的,这种代价就是传输速度的降低。建立和销毁TCP连接会花费很长的时间。如果通信双方实际上通信的时间很短,要传输的数据很少,那么建立和销毁TCP连接的代价就相对较高。

UDP(User Datagram Protocol,用户数据报协议)是传输层的另一种协议,它比TCP具有更快的传输速度,但是不可靠。UDP发送的数据单元被称为UDP数据报。当网络传输UDP数据报时,无法保证数据报一定到达目的地,也无法保证各个数据报按发送的顺序到达目的地。当发送方先发送包含字符串“hello”的数据报,再发送包含字符串“everyone”的数据报时,接收方有可能先接收到字符串“everyone”,再接收到字符串“hello”;也有可能什么数据也没有接收到,因为发送方发送的数据有可能在传输途中都被丢失了。

1、UDP简介

如果应用层关心数据能否正确到达,那么就应该在传输层使用TCP。例如应用层的FTP和HTTP都建立在TCP的基础上。如果应用层仅关心发送的数据能否快速到达目的地,则可以考虑在传输层使用UDP。例如,用UDP传输实时音频时,如果丢失了数据报或打乱数据报的顺序,则只会导致接收方出现噪音,而适量的噪音是可以容忍的。但是如果用TCP传输实时音频,则由于速度很慢,会导致接收方常常出现停顿,声音时断时续,这是无法容忍的。由此可见,UDP比TCP更适于传输实时音频。

由于UDP传输数据是不可靠的,假如应用层要求接收到正确的数据,那么应用层自身必须保证数据传输的可靠性。应用层的网络文件系统(Network File System,NFS)协议,以及简单文件传输协议(Trivial FTP,TFTP)协议都建立在UDP的基础上。NFS协议和TFTP会保证可靠性,这意味着实现NFS协议或TFTP的应用程序必须处理丢失或乱序的数据报。

建立在UDP上的应用层如何保证数据传输的可靠性呢?这可以用邮局发送邮包来做解释,邮局就相当于UDP的实现者,它提供不可靠的传输。发送者通过邮局先后发送3个邮包,邮包上写明了接收者的地址,邮局把邮包发送给接收者,但是有可能部分邮包在途中被丢失,也有可能邮包到达目的地的先后顺序与发送的先后顺序不一样。发送者与接收者必须事先商量好确保接收者正确接收到邮包的协议。发送者会在邮包上标上序号,接收者收到邮包后就能按照序号排序。接收者会告诉发送者已经收到了哪些邮包,发送者就能重发那些接收者未收到的邮包。发送者与接收者使用的这种协议就相当于应用层的协议。

在Java中,java.net.DatagramSocket负责接收和发送UDP数据报,java.net.DatagramPacket表示UDP数据报。如下图所示,每个DatagramSocket都与一个本地地址(包括本地主机的IP地址和本地UDP端口)绑定,每个DatagramSocket都可以把UDP数据报发送给任意一个远程DatagramSocket,也都可以接收来自任意一个远程DatagramSocket的UDP数据报。在UDP数据报中包含了目的地址的信息,DatagramSocket根据该信息把数据报发送到目的地。
在这里插入图片描述
采用TCP通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有与客户连接的Socket,客户端的Socket与服务器端的Socket是一一对应的,它们构成了两个端点之间的虚拟的通信线路。

而UDP是无连接的协议,客户端的DatagramSocket与服务器端的DatagramSocket不存在一一对应关系,两者无须建立连接,就能交换数据报。DatagramSocket提供了接收和发送数据报的方法。

  • public void receive(DatagramPacket dst)throws IOException //接收数据报
  • public void send(DatagramPacket src)throws IOException //发送数据报

下面的EchoServer与例EchoClient就利用DatagramSocket来发送和接收数据报。在这里插入图片描述

在这里插入图片描述
在同一时刻,可能会有多个EchoClient进程与EchoServer进程通信。EchoServer的DatagramSocket与UDP端口8000绑定,EchoClient则与任意一个可用的UDP端口绑定。EchoServer在一个while循环中不断接收EchoClient的数据报,然后给EchoClient回复一个数据报。

EchoClient从控制台读取用户输入的字符串,把它包装成一个数据报,然后把它发送给EchoServer,接着再接收EchoServer的响应数据报。

值得注意的是,EchoClient与EchoServer之间的通信是不可靠的,EchoClient以及EchoServer发送的数据报有可能丢失,或者各个数据报到达目的地的顺序与发送数据报的顺序不一致。

2、DatagramPacket类

DatagramPacket表示数据报,它的构造方法可以被分为两种:一种构造方法创建的DatagramPacket对象用来接收数据,还有一种构造方法创建的DatagramPacket对象用来发送数据。两种构造方法的主要区别是,用于发送数据的构造方法需要设定数据报到达的目的地的地址,而用于接收数据的构造方法无须设定地址。

用于接收数据的构造方法包括:

  • public DatagramPacket(byte[] data,int length)
  • public DatagramPacket(byte[] data,int offset,int length)

以上data参数被用来存放接收到的数据,参数length指定要接收的字节数,参数offset指定在data中存放数据的起始位置,即data[offset]。如果没有设定参数offset,那么起始位置为data[0]。参数length必须小于或等于data.length-offset,否则会抛出IllegalArgumentException。

用于发送数据的构造方法包括:

  • public DatagramPacket(byte[] data,int offset,int length,InetAddress address,int port)
  • public DatagramPacket(byte[] data,int offset,int length,SocketAddress address)
  • public DatagramPacket(byte[] data,int length,InetAddress address,int port)
  • public DatagramPacket(byte[] data,int length,SocketAddress address)

以上data参数中存放了要发送的数据,参数length指定要发送的字节数,参数offset指定要发送的数据在data中的起始位置,即data[offset]。如果没有设定参数offset,那么起始位置为data[0]。参数length必须小于或等于data.length-offset,否则会抛出IllegalArgumentException。参数address和参数port用来指定目的地的地址。以下两段程序代码都创建了一个数据报,它的送达地址为主机“myhost”的UDP端口100。
在这里插入图片描述

3、DatagramSocket类

DatagramSocket类负责接收和发送数据报。每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。在客户程序中,一般由操作系统为DatagramSocket类分配本地端口,这种端口也被称为匿名端口。在服务器程序中,一般由程序显式地为DatagramSocket类指定本地端口。

3.1、构造DatagramSocket

DatagramSocket的构造方法有以下几种重载形式:
在这里插入图片描述
第1个不带参数的构造方法使DatagramSocket对象与匿名端口绑定(即由操作系统分配的任意的可用端口),其余的构造方法都会显式地指定本地端口。第3和第4个构造方法需要同时指定IP地址和端口,这适用于一个主机有多个IP地址的场合,这两个构造方法能明确指定DatagramSocket对象所绑定的IP地址。

如果想知道一个DatagramSocket对象所绑定的本地地址,那么可以调用它的以下方法:

  • int getLocalPort():返回DatagramSocket所绑定的端口。
  • InetAddress getLocalAddress():返回DatagramSocket所绑定的IP地址。
  • SocketAddress getLocalSocketAddress():返回一个SocketAddress对象,它包含DatagramSocket所绑定的IP地址和端口信息。

3.2、接收和发送数据报

DatagramSocket的send()方法负责发送一个数据报,该方法的定义如下:
在这里插入图片描述
值得注意的是,UDP提供不可靠的传输,如果数据报没有到达目的地,那么send()方法不会抛出任何异常,发送方程序就无法知道数据报是否被接收方接收到,除非双方通过应用层的特定协议来确保接收方未收到数据报时,发送方能重发数据报。

send()方法可能会抛出IOException,但是与java.uti.Socket相比,DatagramSocket的send()方法抛出IOException的可能性很小。如果发送的数据报超过了底层网络所支持的数据报的大小,就可能会抛出SocketException,它是IOException的子类。
在这里插入图片描述
DatagramSocket的receive()方法负责接收一个数据报,该方法的定义如下:
在这里插入图片描述
此方法从网络上接收一个数据报。如果网络上没有数据报,执行该方法的线程就会进入阻塞状态,直到收到数据报为止。

参数datagramPacket用来存放接收到的数据报,datagramPacket的数据缓冲区应当足够大,以存放接收到的数据。否则,如果接收的数据报太大,receive()方法就会在datagramPacket的数据缓冲区内存放尽可能多的数据,其余的数据则被丢失。

如果接收数据时出现问题,receive()方法就会抛出IOException,但这种情况实际上极少出现。

3.3、管理连接

两个TCP Socket之间存在固定的连接关系,而一个DatagramSocket可以与其他任意一个DatagramSocket交换数据报。

在某些场合,一个DatagramSocket可能只希望与固定的另一个远程DatagramSocket通信。例如,NFS客户只接收来自与之通信的服务器的数据报。再例如,在网络游戏中,一个游戏玩家只接收他的游戏搭档的数据报。

从JDK1.2开始,DatagramSocket添加了一些方法,利用这些方法,可以使一个DatagramSocket只能与另一个固定的DatagramSocket交换数据报:

  • (1)public void connect(InetAddress host,int port):connect()方法实际上不建立TCP意义上的连接,但它能限制当前DatagramSocket只对参数指定的远程主机和UDP端口收发数据报。如果当前DatagramSocket试图对其他的主机或UDP端口发送数据报,send()方法就会抛出IllegalArgumentException。从参数以外的其他主机或UDP端口发送过来的数据报则被丢弃,程序不会得到任何通知,也不会抛出任何异常。
  • (2)public void disconnect():disconnect()中止当前DatagramSocket已经建立的“连接”,这样,DatagramSocket就可以再次对任何其他主机和UDP端口收发数据报。
  • (3)public int getPort()当且仅当DatagramSocket已经建立连接时,getPort()方法才返回DatagramSocket所连接的远程UDP端口,否则返回“-1”。
  • (4)public InetAddress getInetAddress()当且仅当DatagramSocket已经建立连接时,getInetAddress()方法才返回DatagramSocket所连接的远程主机的IP地址,否则返回null。
  • (5)public SocketAddress getRemoteSocketAddress()当且仅当DatagramSocket已经建立连接时,getRemoteSocketAddress()方法才返回一个SocketAddress对象,表示DatagramSocket所连接的远程主机以及端口的地址,否则返回null。

UDP客户程序通常只和特定的UDP服务器通信,因此可在UDP客户程序中把DatagramSocket与远程服务器连接。UDP服务器需要与多个UDP客户程序通信,因此在UDP服务器中一般不用对DatagramSocket建立特定的连接。

3.4、关闭DatagramSocket

DatagramSocket的close()方法会释放所占用的本地UDP端口。在程序中及时关闭不再需要的DatagramSocket,这是好的编程习惯。

3.5、DatagramSocket的选项

DatagramSocket有以下选项:

  • SO_TIMEOUT:表示接收数据报时的等待超时时间。
  • SO_RCVBUF:表示接收数据的缓冲区的大小。
  • SO_SNDBUF:表示发送数据的缓冲区的大小。
  • SO_REUSEADDR:表示是否允许重用DatagramSocket所绑定的本地地址。
  • SO_BROADCAST:表示是否允许对网络广播地址发送广播数据报。

3.6、IP服务类型选项

UDP与TCP都建立在IP基础之上,DatagramSocket也提供了设置和读取IP服务类型的方法:

  • 设置服务类型:public void setTrafficClass(int trafficClass) throws SocketException
  • 读取服务类型:public int getTrafficClass() throws SocketException

4、DatagramChannel类

从JDK1.4开始,添加了一个支持按照非阻塞方式发送和接收数据报的DatagramChannel。DatagramChannel是SelectableChannel的子类,可以被注册到一个Selector。使用DatagramChannel,可以使得UDP服务器只需用单个线程就能同时与多个客户通信。DatagramChannel在默认情况下采用阻塞模式,如果希望该为非阻塞模式,那么可以调用configureBlocking(false)方法。


http://www.niftyadmin.cn/n/5255154.html

相关文章

【LVGL】STM32F429IGT6(在野火官网的LCD例程上)移植LVGL官方的例程(还没写完,有问题 排查中)

这里写目录标题 前言一、本次实验准备1、硬件2、软件 二、移植LVGL代码1、获取LVGL官方源码2、整理一下,下载后的源码文件3、开始移植 三、移植显示驱动1、enable LVGL2、修改报错部分3、修改lv_config4、修改lv_port_disp.c文件到此步遇到的问题 Undefined symbol …

E4990A 阻抗分析仪,20 Hz 至 10/20/30/50/120 MHz

01 E4990A 阻抗分析仪 20 Hz 至 10/20/30/50/120 MHz 产品综述: E4990A 阻抗分析仪具有 20 Hz 至 120 MHz 的频率范围,可在宽阻抗范围内提供出色的 0.045%(典型值)基本准确度,并内置 40 V 直流偏置源,适…

举例讲解MSSQL存储过程的功能和用法

【关注微信公众号:跟强哥学SQL,回复“笔试”免费领取大厂SQL笔试题。】 MSSQL(Microsoft SQL Server)存储过程是一组预定义的SQL语句集合,它们被封装在一个名称下,并可以在需要时被调用。 存储过程具有以下…

mysql原理--B+树索引的使用

1.索引的代价 在介绍如何更好的使用索引之前先要了解一下使用这玩意儿的代价,它在空间和时间上都会拖后腿: (1). 空间上的代价 这个是显而易见的,每建立一个索引都要为它建立一棵 B 树,每一棵 B 树的每一个节点都是一个数据页&…

前端知识笔记(一)———Base64图片是什么?原理是什么?优缺点是什么?

Base64图片是一种将图像数据编码为文本字符串的方法,通常用于将图像嵌入到网页或其他文档中,以减少HTTP请求或实现某些特定的需求。Base64编码不是一种压缩算法,而是一种数据编码方法,它将二进制数据转换为一种可读的ASCII字符集表…

4.PyTorch——优化器

import numpy as np import pandas as pdimport torch as tPyTorch将深度学习中常用的优化方法全部封装在torch.optim中,其设计十分灵活,能够很方便的扩展成自定义的优化方法。 所有的优化方法都是继承基类optim.Optimizer,并实现了自己的优…

java中什么是ioc?

IoC(Inversion of Control),即控制反转,是一种软件设计思想或设计模式,用于降低计算机程序的耦合度。在Java中,IoC主要体现在Spring框架中。 IoC的核心思想是将组件的创建、组装和管理等控制权从应用程序代…

知识笔记(五十四)———mysql比较varchar值大小_Mysql varchar大小长度问题

1、限制规则 字段的限制在字段定义的时候有以下规则: a) 存储限制 varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度(长度超过255时需要2个字节),因此最大长度不能超过65535。 b) 编码长度限制 字符类…