使用DatagramSocket发送接收数据Socket之UDP套接字.docx
- 文档编号:24698535
- 上传时间:2023-05-31
- 格式:DOCX
- 页数:9
- 大小:19.24KB
使用DatagramSocket发送接收数据Socket之UDP套接字.docx
《使用DatagramSocket发送接收数据Socket之UDP套接字.docx》由会员分享,可在线阅读,更多相关《使用DatagramSocket发送接收数据Socket之UDP套接字.docx(9页珍藏版)》请在冰豆网上搜索。
使用DatagramSocket发送接收数据Socket之UDP套接字
使用DatagramSocket发送、接收数据(Socket之UDP套接字)
用DatagramSocket发送、接收数据
(1)
Java使用DatagramSocket代表UDP协议的Socket,DatagramSocket本身只是码头,不维护状态,不能产生IO
流,它的唯一作用就是接收和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过
DatagramPacket对象完成的。
先看一下DatagramSocket的构造器。
DatagramSocket():
创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
DatagramSocket(intprot):
创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口。
DatagramSocket(intport,InetAddressladdr):
创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。
通过上面三个构造器中的任意一个构造器即可创建一个DatagramSocket实例,通常在创建服务器时,创建指定端口的
DatagramSocket实例--这样保证其他客户端可以将数据发送到该服务器。
一旦得到了DatagramSocket实例之后,就可以通过如下两
个方法来接收和发送数据。
receive(DatagramPacketp):
从该DatagramSocket中接收数据报。
send(DatagramPacketp):
以该DatagramSocket对象向外发送数据报。
从上面两个方法可以看出,使用DatagramSocket发送数据报时,DatagramSocket并不知道将该数据报发送到哪里,而是由
DatagramPacket自身决定数据报的目的地。
就像码头并不知道每个集装箱的目的地,码头只是将这些集装箱发送出去,而集装箱本身包含了该集装箱
的目的地。
下面看一下DatagramPacket的构造器。
DatagramPacket(byte[]buf,intlength):
以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据。
DatagramPacket(byte[]buf,intlength,InetAddressaddr,int
port):
以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象时还指定了IP地址和端口--这就决
定了该数据报的目的地。
DatagramPacket(byte[]buf,intoffset,intlength):
以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节。
DatagramPacket(byte[]buf,intoffset,intlength,InetAddress
address,int
port):
创建一个用于发送的DatagramPacket对象,指定发送buf数组中从offset开始,总共length个字节。
当Client/Server程序使用UDP协议时,实际上并没有明显的服务器端和客户端,因为两方都需要先建立一个DatagramSocket
对象,用来接收或发送数据报,然后使用DatagramPacket对象作为传输数据的载体。
通常固定IP地址、固定端口的DatagramSocket
对象所在的程序被称为服务器,因为该DatagramSocket可以主动接收客户端数据。
在接收数据之前,应该采用上面的第一个或第三个构造器生成一个DatagramPacket对象,给出接收数据的字节数组及其长度。
然后调用
DatagramSocket
的receive()方法等待数据报的到来,receive()将一直等待(该方法会阻塞调用该方法的线程),直到收到一个数据报为止。
如下代码所示:
//创建一个接收数据的DatagramPacket对象DatagramPacketpacket=newDatagramPacket(buf,256);//接收数据报socket.receive(packet);
在发送数据之前,调用第二个或第四个构造器创建DatagramPacket对象,此时的字节数组里存放了想发送的数据。
除此之外,还要给出完整的
目的地址,包括IP地址和端口号。
发送数据是通过DatagramSocket的send()方法实现的,send()方法根据数据报的目的地址来寻径以
传送数据报。
如下代码所示:
//创建一个发送数据的DatagramPacket对象DatagramPacketpacket=newDatagramPacket(buf,length,address,port);//发送数据报socket.send(packet);
使用DatagramPacket接收数据时,会感觉DatagramPacket设计得过于烦琐。
开发者只关心该DatagramPacket能
放多少数据,而DatagramPacket是否采用字节数组来存储数据完全不想关心。
但Java要求创建接收数据用的DatagramPacket时,
必须传入一个空的字节数组,该数组的长度决定了该DatagramPacket能放多少数据,这实际上暴露了DatagramPacket的实现细节。
接
着DatagramPacket又提供了一个getData()方法,该方法又可以返回Datagram
Packet对象里封装的字节数组,该方法更显得有些多余--如果程序需要获取DatagramPacket里封装的字节数组,直接访问传给
DatagramPacket构造器的字节数组实参即可,无须调用该方法。
当服务器端(也可以是客户端)接收到一个DatagramPacket对象后,如果想向该数据报的发送者"反馈"一些信息,但由于UDP协议是面向
非连接的,所以接收者并不知道每个数据报由谁发送过来,但程序可以调用DatagramPacket的如下3个方法来获取发送者的IP地址和端口。
InetAddressgetAddress():
当程序准备发送此数据报时,该方法返回此数据报的目标机器的IP地址;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的IP地址。
intgetPort():
当程序准备发送此数据报时,该方法返回此数据报的目标机器的端口;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的端口。
SocketAddressgetSocketAddress():
当程序准备发送此数据报时,该方法返回此数据报的目标SocketAddress;当程序刚接收到一个数据报时,该方法返回该数据报的发送主机的SocketAddress。
getSocketAddress()方法的返回值是一个SocketAddress对象,该对象实际上就是一个IP地址和一个端口号。
也就是
说,SocketAddress对象封装了一个InetAddress对象和一个代表端口的整数,所以使用SocketAddress对象可以同时代表
IP地址和端口。
17.4.2使用DatagramSocket发送、接收数据
(2)
下面程序使用DatagramSocket实现了Server/Client结构的网络通信。
本程序的服务器端使用循环1000次来读取DatagramSocket中的数据报,每当读取到内容之后便向该数据报的发送者送回一条信息。
服务器端程序代码如下。
程序清单:
codes\17\17.4\UdpServer.javapublicclassUdpServer{publicstaticfinalintPORT=30000;//定义每个数据报的最大大小为4KBprivatestaticfinalintDATA_LEN=4096;//定义接收网络数据的字节数组byte[]inBuff=newbyte[DATA_LEN];//以指定字节数组创建准备接收数据的DatagramPacket对象privateDatagramPacketinPacket=newDatagramPacket(inBuff,inBuff.length);//定义一个用于发送的DatagramPacket对象privateDatagramPacketoutPacket;//定义一个字符串数组,服务器端发送该数组的元素String[]books=newString[]{"疯狂Java讲义","轻量级JavaEE企业应用实战","疯狂Android讲义","疯狂Ajax讲义"};publicvoidinit()throwsIOException{try(//创建DatagramSocket对象DatagramSocketsocket=newDatagramSocket(PORT)){//采用循环接收数据for(inti=0;i<1000;i++){//读取Socket中的数据,读到的数据放入inPacket封装的数组里socket.receive(inPacket);//判断inPacket.getData()和inBuff是否是同一个数组System.out.println(inBuff==inPacket.getData());//将接收到的内容转换成字符串后输出System.out.println(newString(inBuff,0,inPacket.getLength()));//从字符串数组中取出一个元素作为发送数据byte[]sendData=books[i%4].getBytes();//以指定的字节数组作为发送数据,以刚接收到的DatagramPacket的//源SocketAddress作为目标SocketAddress创建DatagramPacketoutPacket=newDatagramPacket(sendData,sendData.length,inPacket.getSocketAddress());//发送数据socket.send(outPacket);}}}publicstaticvoidmain(String[]args)throwsIOException{newUdpServer().init();}}
上面程序中的粗体字代码就是使用DatagramSocket发送、接收DatagramPacket的关键代码,该程序可以接收1000个客户端发送过来的数据。
客户端程序代码也与此类似,客户端采用循环不断地读取用户键盘输入,每当读取到用户输入的内容后就将该内容封装成DatagramPacket数据
报,再将该数据报发送出去;接着把DatagramSocket中的数据读入接收用的DatagramPacket中(实际上是读入该
DatagramPacket所封装的字节数组中)。
客户端程序代码如下。
17.4.2使用DatagramSocket发送、接收数据(3)
程序清单:
codes\17\17.4\UdpClient.java
publicclassUdpClient{//定义发送数据报的目的地publicstaticfinalintDEST_PORT=30000;publicstaticfinalStringDEST_IP="127.0.0.1";//定义每个数据报的最大大小为4KBprivatestaticfinalintDATA_LEN=4096;//定义接收网络数据的字节数组byte[]inBuff=newbyte[DATA_LEN];//以指定的字节数组创建准备接收数据的DatagramPacket对象privateDatagramPacketinPacket=newDatagramPacket(inBuff,inBuff.length);//定义一个用于发送的DatagramPacket对象privateDatagramPacketoutPacket=null;publicvoidinit()throwsIOException{try(//创建一个客户端DatagramSocket,使用随机端口DatagramSocketsocket=newDatagramSocket()){//初始化发送用的DatagramSocket,它包含一个长度为0的字节数组outPacket=newDatagramPacket(newbyte[0],0,InetAddress.getByName(DEST_IP),DEST_PORT);//创建键盘输入流Scannerscan=newScanner(System.in);//不断地读取键盘输入while(scan.hasNextLine()){//将键盘输入的一行字符串转换成字节数组byte[]buff=scan.nextLine().getBytes();//设置发送用的DatagramPacket中的字节数据outPacket.setData(buff);//发送数据报socket.send(outPacket);//读取Socket中的数据,读到的数据放在inPacket所封装的字节数组中socket.receive(inPacket);System.out.println(newString(inBuff,0,inPacket.getLength()));}}}publicstaticvoidmain(String[]args)throwsIOException{newUdpClient().init();}}
上面程序中的粗体字代码同样也是使用DatagramSocket发送、接收DatagramPacket的关键代码,这些代码与服务器端代码基本
相似。
而客户端与服务器端的唯一区别在于:
服务器端的IP地址、端口是固定的,所以客户端可以直接将该数据报发送给服务器端,而服务器端则需要根据接收到
的数据报来决定"反馈"数据报的目的地。
读者可能会发现,使用DatagramSocket进行网络通信时,服务器端无须也无法保存每个客户端的状态,客户端把数据报发送到服务器端后,完全有可能立即退出。
但不管客户端是否退出,服务器端都无法知道客户端的状态。
当使用UDP协议时,如果想让一个客户端发送的聊天信息被转发到其他所有的客户端则比较困难,可以考虑在服务器端使用Set集合来保存所有的客户端
信息,每当接收到一个客户端的数据报之后,程序检查该数据报的源SocketAddress是否在Set集合中,如果不在就将该
SocketAddress添加到该Set集合中。
这样又涉及一个问题:
可能有些客户端发送一个数据报之后永久性地退出了程序,但服务器端还将该客户端的
SocketAddress保存在Set集合中……总之,这种方式需要处理的问题比较多,编程比较烦琐。
幸好Java为UDP协议提供了
MulticastSocket类,通过该类可以轻松地实现多点广播。
Socket之UDP套接字
UDP套接字:
UDP套接字的使用是通过DatagramPacket类和DatagramSocket类,客户端和服务器端都是用DatagramPacket类来接收数据,使用DatagramSocket类来发送数据。
UDP客户端:
也是主要执行三个步骤。
1.创建DatagramSocket实例;
2.使用DatagramSocket类的send()和receive()方法发送和接收DatagramPacket实例;
3.最后使用DatagramSocket类的close()方法销毁该套接字。
下面是例子,它主要执行三个步骤,
1.向服务器发送信息;
2.在receive()方法上最多阻塞等待3秒钟,在超时前若没有收到响应,则重发请求(最多重发5次);
3.关闭客户端。
[java]viewplaincopy//UDPEchoClientTimeout.javaimport.DatagramSocket;import.DatagramPacket;import.InetAddress;importjava.io.IOException;importjava.io.InterruptedIOException;publicclassUDPEchoClientTimeout{privatestaticfinalintTIMEOUT=3000;//设置超时为3秒privatestaticfinalintMAXTRIES=5;//最大重发次数5次publicstaticvoidmain(String[]args)throwsIOException{if((args.length<2)||(args.length>3)){//Testforcorrect#ofargsthrownewIllegalArgumentException("Parameter(s):
<Server><Word>[<Port>]");}InetAddressserverAddress=InetAddress.getByName(args[0]);//服务器地址//ConverttheargumentStringtobytesusingthedefaultencoding//发送的信息byte[]bytesToSend=args[1].getBytes();intservPort=(args.length==3)?
Integer.parseInt(args[2]):
7;DatagramSocketsocket=newDatagramSocket();socket.setSoTimeout(TIMEOUT);//设置阻塞时间DatagramPacketsendPacket=newDatagramPacket(bytesToSend,//相当于将发送的信息打包bytesToSend.length,serverAddress,servPort);DatagramPacketreceivePacket=//相当于空的接收包newDatagramPacket(newbyte[bytesToSend.length],bytesToSend.length);inttries=0;//Packetsmaybelost,sowehavetokeeptryingbooleanreceivedResponse=false;do{socket.send(sendPacket);//发送信息try{socket.receive(receivePacket);//接收信息if(!
receivePacket.getAddress().equals(serverAddress)){//ChecksourcethrownewIOException("Receivedpacketfromanunknownsource");}receivedResponse=true;}catch(InterruptedIOExceptione){//当receive不到信息或者receive时间超过3秒时,就向服务器重发请求tries+=1;System.out.println("Timedout,"+(MAXTRIES-tries)+"moretries...");}}while((!
receivedResponse)&&(tries<MAXTRIES));if(receivedResponse){System.out.println("Received:
"+newString(receivePacket.getData()));}else{System.out.println("Noresponse--givingup.");}socket.close();}}例子只是简单的向指定的服务器发送信息,并将发送的信息由服务器返回给指定客户端。
UDP服务器端:
典型的UDP服务器要执行三个步骤,
1.创建一个指定了本地端口的DatagramSocket实例;
2.使用DatagramSocket的receive()方法接收一个来自客户端的DatagramPacket实例,而这个DatagramPacket实例在客户端创建时就包含了客户端的地址,这样我们就知道回复信息要发送到哪里了;
3.使用DatagramSocket类的send()和receive()方法来发送和接收DatagramPacket实例。
下面是例子
[java]viewplaincopy//UDPEchoServer.javaimportjava.io.IOException;import.DatagramPacket;import.DatagramSocket;publicclassUDPEchoServer{privatestaticfinalintECHOMAX=255;//发送或接收的信息最大字节数publicstaticvoidmain(String[]args)throwsIOException{if(args.length!
=1){//TestforcorrectargumentlistthrownewIllegalArgumentException("Parameter(s):
<Port>");}intservPort=Integer.parseInt(args[0]);DatagramSocketsocket=newDatagramSocket(servPort);DatagramPacketpacket=newDatagramPacket(newbyte[ECHOMAX],ECHOMAX);while(true){//不断接收来自客户端的信息及作出相应的相应socket.receive(packet);//ReceivepacketfromclientSystem.out.println("Handlingclien
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 DatagramSocket 发送 接收 数据 Socket UDP 套接