解决方案

java的UDP(一)

seo靠我 2023-09-23 06:15:03

文章目录

1. 简介2. UDP客户端3. UDP服务器4. DatagramPacket类

1. 简介

Java中的UDP实现分为两个类:DatagramPacket和DatagramSocket。DatSEO靠我agramPacket类将数据字节填充到UDP包汇总,这称为数据报,由你来解包接收的数据报。DatagramSocket可以收发UDP数据报。为发送数据,要将数据放到DatagramPacket中,使SEO靠我用DatagramPacket来发送这个包。要接受数据,可以从DatagramSocket中接受一个DatagramSocket对象,然后检查这个包的内容。Socket本身非常简单,在UDP种,关于数SEO靠我据报的所有信息(包括发送的目标地址)都包含在包本身中。Socket只需要了机在哪个本地端口监听或发送。这种职责划分和TCP使用的Socket和ServerSocket有所不同,首先,UDP没有两台主机SEO靠我间唯一连接的概念。一个Socket会收发所有指向指定端口的数据,而不需要知道对方时哪一个远程主机。一个DatagramPacket可以从多个独立主机收发数据。与TCP不同这个Socket并不专用于一个SEO靠我连接。事实上,UDP没有任何两台主机之间连接的概念,它只知道单个数据报。要确定由谁发送什么数据,这个时应用程序的责任。其次,TCP socket把网络连接看作流:通过从Socket得到的输入和输出流来SEO靠我接受数据,其次TCP socket把网络节点看作是流:通过从Socket得到的输入和输出流来收发数据。UDP不支持这一点,你处理的总是单个数据报包,填充在一个数据报的所有数据会以一个包的形式进行发送,SEO靠我这些数据作为一个组要么全部接受,要么完全丢失。一个包不一定与下一个包相关。给定两个包,数据报回尽可能地传递到接收方。

2. UDP客户端

这里我们还是拿美国国家标准与技术研究院的daytime服务器举例子SEO靠我。这里使用的传输层协议是UDP。首先在端口0打开一个Socket

DatagramSocket socket= new DatagramSocket(0);

只需要指定一个本地端口,Socket并不知道远SEO靠我程服务器是什么。通过指定端口为0,就是在请求Java为你随机选择一个可用的端口。下面使用SetTimeout()方法在连接上设置一个超时时间。单位为毫秒

socket.setSoTimeout(1000SEO靠我0);

超时对于UDP比TCP更重要,因此TCP中会导致IOException异常的很多问题在UDP中只会悄无声息地失败。接下来需要建立数据包。需要建立两个数据包,一个是要发送的数据包,另一个是需要接受SEO靠我的数据包。

InetAddress host=InetAddress.getByName("time.nist.nov"); DatagramPacket request=new DatSEO靠我agramPacket(new byte[1],1,host,13); //如果接受的数据大小超过1kb,多出的数据会被自动截断 byte[] data=new bytSEO靠我e[1024]; DatagramPacket response= new DatagramPacket(data,data.length);

现在已经准备就绪,首先在这个Socket发SEO靠我送数据包,然后接受响应:

socket.send(request); socket.receive(response);

最后从响应中提取字节,将它们转换为可以显示给最终用户的字符串:

StSEO靠我ring daytime=new String(response.getData(),0,response.getLength,"US-ASCII"); System.out.prinSEO靠我tln(daytime);

构造函数以及send()和receive()方法都可能抛出一个IOException,且DatagramSocket实现了Autocloseable,下面是完整代码:

publSEO靠我ic class QuizCardBuilder {public static void main(String[] args) {try(DatagramSocket socket=new DataSEO靠我gramSocket(0)){socket.setSoTimeout(10000);InetAddress address=InetAddress.getByName("time.nist.gov")SEO靠我;DatagramPacket request=new DatagramPacket(new byte[1],1,address,13);DatagramPacket response=new DatSEO靠我agramPacket(new byte[1024],1024);socket.send(request);socket.receive(response);String result=new StrSEO靠我ing(response.getData(),0,response.getLength(),"US-ASCII");System.out.println(result);}catch (IOExcepSEO靠我tion e){e.printStackTrace();}} }

3. UDP服务器

UDP服务器几乎遵循与UDP客户端同样的模式,只不过通常在发送之前会先接收,而且不会选择绑定的匿名端口SEO靠我,与TCP不同,并没有单独的DatagramServerSocekt类。现在我们实现一个上面的简单的daytime服务器,首先在一个已知的端口上打开一个数据报Socket。对于daytime协议,这个SEO靠我端口为13:

DatagramSocket socket=new DatagramSocket(13);

接下来创建一个将接收请求的数据包,要提供一个将存储如站数据的byte数组,数组中的偏移量,以及要存SEO靠我储的字节数,

DatagramPacket request =new DatagramPacket(new byte[1024],0,1024);

然后接收这个数据包

socket.receive(requSEO靠我est);

这个调用会被无限阻塞,直到一个UDP数据包到达13端口,如果有UDP数据包到达,java会将这个数据填充到byte数组,receive()方法返回。然后创建一个响应包,包括四个部分:要发送的SEO靠我原始数据、待发送的原始数据的字节数、要发送到的主机,以及发送到该主机上哪个端口。

String daytime= new Data().toString()+"\r\n"; byte[]SEO靠我 data=daytime.getBytes("US-ASCII"); InetAddress host=request.getAddress(); int port SEO靠我= request.getPort(); DatagramPacket response=new DatagramPacket(data,data.length,host,port);SEO靠我

最后发送数据即可,下面是完整的服务器代码

public class QuizCardBuilder {public static void main(String[] args) throws InteSEO靠我rruptedException {Thread server= new Thread(new server());Thread client= new Thread(new client());seSEO靠我rver.start();Thread.sleep(1000);client.start();} } class client implements Runnable{SEO靠我@Overridepublic void run() {try(DatagramSocket socket=new DatagramSocket(0)){socket.setSoTimeout(100SEO靠我00);InetAddress address=InetAddress.getByName("localhost");DatagramPacket request=new DatagramPacketSEO靠我(new byte[1],1,address,8080);DatagramPacket response=new DatagramPacket(new byte[1024],1024);socket.SEO靠我send(request);socket.receive(response);String result=new String(response.getData(),0,response.getLenSEO靠我gth(),"US-ASCII");System.out.println(result);}catch (IOException e){e.printStackTrace();}} }SEO靠我 class server implements Runnable{@Overridepublic void run() {try(DatagramSocket socket=new SEO靠我DatagramSocket(8080)){while(true){DatagramPacket request=new DatagramPacket(new byte[1024],1024);socSEO靠我ket.receive(request);String daytime=new Date().toString();byte[] data=daytime.getBytes("US-ASCII");DSEO靠我atagramPacket datagramPacket=new DatagramPacket(data,data.length,request.getAddress(),request.getPorSEO靠我t());socket.send(datagramPacket);}}catch (IOException e){e.printStackTrace();}} }

这个例子可以看出,UDSEO靠我P服务器与TCP服务器不同,往往不是多线程的,它们通常不会对某一个客户做太多工作,而且不会阻塞来等待另一端响应,因为UDP从来不会报告错误。对于UDP服务器来说,除非为了准备响应需要做大量耗费时间的工SEO靠我作,否则使用一种迭代方法就可以了。

4. DatagramPacket类

UDP数据报是基于IP数据报建立的,只向其底层IP数据报添加了很少的一点内容。如下图,UDP首部只向IP首部天际了8个字节。UDPSEO靠我首部包括源和目标端口号,IP首部之后所有内容的长度,以及一个可选的校验和。由于端口号以2字节无符号整数给出,因此每台主机有65536个不同的UDP端口可以使用。它们与每台主机的65536不同的TCP端SEO靠我口截然不同。因为长度也是一2字节无符号整数给出,所以数据报中的字节数不能超过65536-8字节。不过,这与IP首部中的数据报的长度字段是冗余的,它将数据报限制为65467-65507之间(具体大小取决SEO靠我于IP首部)。检验和字段是可选的,应用层程序不使用这个校验和,页无法访问这个校验和。如果数据的校验失败,那么底层网络软件会悄悄丢掉这个数据报。发送方或接受方都不会得到这个通知。毕竟UDP是不可靠的。在SEO靠我Java中,UDP数据报用品DatagramPacket类的实例表示:

构造函数

取决于数据包用于发送数据还是接收数据。在这里6个构造函数都接受两个参数,一个时保存数据报数据的byte数组,另一个参数时该SEO靠我数组中用于数据报数据的字节数。希望接收数据报时,只需要提供这两个参数。当Socket从网络接收数据报时,它将数据报的数据存储在DatagramPacket对象的缓存区数组中,直到达到你指定的长度。第二SEO靠我组DatagramPacket构造函数用于创建通过网络发送的数据报。与前一组一样,这些构造函数需要一个缓冲区数组和一个长度,另外还需要指定数据包发去的地址和端口。

接收数据报的构造函数 SEO靠我 public DatagramPacket(byte[] buffer, int length) public DatagramPacket(byte[] buffer, int oSEO靠我ffset, int length)

构造函数不关心缓冲区多大,甚至它希望你创建几M得DatagramPacket。不过,底层网络软件却不那么宽容,大多数底层UDP实现都不支持超过8192字节数据的数据SEO靠我报。事实上,很多操作系统不支持超过8KB的UDP数据报,否则就会将更大的数据报截断、分解或丢掉。如果数据报太大,而导致网络将其截断或者丢弃,java会收不到任何通知。

发送数据的构造函数 SEO靠我 public DatagramPacket(byte[] data, int length, InetAddress destination ,int port) public DSEO靠我atagramPacket(byte[] data, int offset, int length, InetAddress destination , int port) publiSEO靠我c DatagramPacket(byte[] data, int length, SocketAddress destination) public DatagramPacket(bSEO靠我yte[] data, int offset, SocketAddress destination)

每个构造函数都创建一个发往另一台主机的DatagramPacket。

在一个包中填充多少数据才合适?

SEO靠我其实取决于实际情况,有些协议规定了包大小。如果网络非常不可靠,如分组无线电网络,则要选择较小的包,因为这样可以减少在传输中被破坏的可能性。另一方面,非常快速而可靠的LAN应当使用尽可能大的包。对于很多SEO靠我类型的网络,8KB字节往往是一个很好的折中方案。 get方法

DatagramPacket有6个获取数据报不同部分的方法:这些部分包括具体的数据以及首部的几个字段。这些方法主要用于从网络接收的数据报:

puSEO靠我blic InetAddress getAddress()

该方法返回一个InetAddress对象,其中包含远程主机的地址。如果数据报时从Internet接收的,返回的地址是发送该数据报的机器地址(源SEO靠我地址)。另一方面,如果数据报是本地创建的,要发送到一个远程机器,那么这个返回会返回数据报将发往的那个主机地址。这个方法常用于确定发送UDP数据报的主机地址,使接收方可以回复

public int getSEO靠我Port()

该返回返回远程端口。如果数据从Internet接收,这就是发送包的主机上的端口。如果数据报是本地创建的,要发送一个远程主机,那么这就是远程机器上包发往的目标端口

public SocketASEO靠我ddress getSocketAddress()

该方法返回一个SocketAddress对象,包含远程主机的IP地址和端口。如果数据报从Internet接收的,返回的地址就是发送该数据报的机器的地址SEO靠我。如果是本地创建的,要发送到远程主机,这个返回数据报发往的主机地址。此外,如果你使用非阻塞I/O,DatagramChannel类可以接收一个SocketAddress,而不接收单独的InetAddrSEO靠我ess和端口

public byte[] getData()

返回一个byte数组,其中包含数据报中的数据。为了能够在你的程序中使用,通常必须将这些字节转换为其他的某种数据形式。一种方法是将byte数组转SEO靠我换为一个String。

String s=new String(dp.getData() ,"UTF-8")

如果数据报不包含文本,那么将它转换为java数据会更加困难。一种方法是将getData()返回SEO靠我的Byte数组转换一个ByteArrayInputStream。

//指定offset和length的原因是,返回的数组可能有额外的空间没利用到 InputStream in= new SEO靠我ByteArrayInputStream(packet.getData(),packet.getOffset(),packet.getLength());

然后ByteArrayInputStream可SEO靠我以串链到DataInputStream,接下来可以使用DataInputStream得readInt()、readLong()、readChar()及其他方法读取数据。

public int getLeSEO靠我ngth()

该方法返回数据报中数据的字节数。

public int getOffset()

对于getData()返回的数组,这个方法会返回该数组的一个位置,即开始填充数据报数据的那个位置。

下面的程序使用SEO靠我了上面介绍的所有get方法

public static void main(String[] args) throws InterruptedException {String s="This ia aSEO靠我 test";try{byte[] data =s.getBytes("UTF-8");InetAddress ia=InetAddress.getByName("www.ibiblio.org");SEO靠我int port=7;DatagramPacket dp=new DatagramPacket(data,data.length,ia,port);System.out.println("This pSEO靠我acket is adderssed to "+ dp.getAddress()+" on port "+dp.getPort());System.out.println("There are "+dSEO靠我p.getLength()+" bytes of data in the packet");System.out.println(new String(dp.getData(),dp.getOffseSEO靠我t(),dp.getLength(),"UTF-8"));} catch (UnsupportedEncodingException e) {throw new RuntimeException(e)SEO靠我;} catch (UnknownHostException e) {throw new RuntimeException(e);}} Set方法

Java还提供了几个方法,可以在创SEO靠我建数据报之后改变数据、远程地址和远程端口。如果创建和垃圾回收新DatagramPacket对象的时间会严重影响性能,这些方法就很重要。

public void setData(byte[] data)

SEO靠我方法可以改变UDP数据报的有效载荷。如果要向远程主机发送大文件,可能就用到这个方法。你可以重复地发送相同的DatagramPacket对象,每次只改变数据

public void setData(bytSEO靠我e[] data, int offset, int length)

这个重载的setData方法提供了另一个途径来发送大量的数据。与发送大量新数组不同,可以将所有数据放到一个数组中,每次发送一个部分。如SEO靠我下:

int offset=0; DatagramPacket dp= new DatagramPacket(bigarray, offset, 512); int bySEO靠我tesSent=0; while(bytesSent < bigarray,length){socekt.send(dp);bytesSent+=dp.getLength();int SEO靠我bytesToSend=bigarray.length-bytesSent;dp.setData(bigarray,bytesSent,size); public void setASEO靠我ddress(InetAddress remote)

该方法会修改数据报发往的地址,这允许你将同一个数据报发送多个不同的接收方。

String s="Really Important Message"; SEO靠我 byte[] data= s.getBytes("UTF-8); DatagramPacket dp=new DatagramPacket(data ,data.lenSEO靠我gth); dp.setPort(2000); int network="128.238.5."; for (int host= 1;host <255SEO靠我 ;host++) {try{InetAddress remote=InetAddress.getByName(network+host);dp.setAddress(remote);SEO靠我socket.send(dp);}catch(IOException ex){} } public void setPort(int port)

该方法会改变数据报发往SEO靠我的端口。

public void setAddress(SocketAddress remote)

该方法会改变数据包要发往的地址和端口,在回复时可以使用这个方法,例如下面代码将接收一个数据报包,用包含字SEO靠我符串的包响应同一个地址

DatagramPacket input= new DatagramPacket(new byte[8192] ,8192); socekt.receive(inSEO靠我put); DatagramPacket output=new DatagramPacket(" hello there".getBytes("UTF-8"),11) ; SEO靠我 SocketAddress address=input.getSocketAddress(); output.setAddress(address); socketSEO靠我.send(output); public void setLength(int length)

该方法会改变内部缓冲区汇总包含实际数据报数据的字节数,而不包括未填充的数据的空间。这个SEO靠我方法在接收数据报时很有用,当接收到数据报时,其长度设置为入站数据的长度。这表示如果试图在同一个DatagramPacket中接收另一个数据报,那么会限制第二个数据报的字节数不能对于第一个数据报的字节数SEO靠我。也就是说,一旦接受了一个10字节的数据报,所有后续的数据报都将截断为10字节。一旦接受到9个字节数据报,所有后续的数据报都会截断到9字节。有了这个方法,我们可以修改缓冲区的长度,这样使用相同的DatSEO靠我agrampacket接受其他数据报时不会截断数据报

“SEO靠我”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与 我们联系删除或处理,客服邮箱:html5sh@163.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同 其观点或证实其内容的真实性。

网站备案号:浙ICP备17034767号-2