《Java TCP/IP Socket编程》读书笔记(四)
2.3 UDP套接字
UDP协议提供了一种不同于TCP协议的端对端服务,实际上UDP只实现了两个功能:1)、 在IP协议的基础上添加了另一层地址(端口)2)、对数据传输过程中可能产生的错误进行了检测,并抛弃了已损坏的数据。
UDP套接字与TCP套接字的不同点:
1. UDP协议在使用前不需要进行连接。
2. UDP协议保存边界信息。
2.3.1 UDP客户端
UDP客户端首先向被动等待联系的服务器发送一个数据报文。一个UDP报文要执行以下三步。
1. 创建一个DatagramSocket实例,可以选择对本地地址和端口号进行设置。 2. 使用DatagramSocket类的send() 和 receive()方法来发送和接收DatagramPacket实例,进行通信。 3. 通信完成后,使用DatagramSocket类的close()方法来销毁该套接字。 |
DatagramSocket创建的时候不需要指定目的地址。因为UDP通信前不需要进行连接。每个数据报可以发送到或者接收于不同的目的地址。
UDP协议的一个后果是会出现数据报文丢失。为了避免这个问题,可以设置最大阻塞时间。
下面一个UDP客户端的例子
package com.suifeng.tcpip.chapter2; import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; /** * UDP 客户端 * * @author Suifeng * */ public class UDPEchoClientTimeout { // 超时时间 private static final int TIMEOUT = 3000; // 最大连接次数 private static final int MAXTRIES = 5; public static void main(String[] args) throws IOException { if (args.length < 2 || args.length > 3) { throw new IllegalArgumentException( "Paramters:<Server> <Word> [<Port>]"); } // 服务器地址 InetAddress serverAddress = InetAddress.getByName(args[0]); // 要发送的数据 byte[] byteToSend = args[1].getBytes(); // 服务器端口 int serverPort = (args.length == 3) ? Integer.parseInt(args[2]) : 7; // UDP客户端 DatagramSocket socket = new DatagramSocket(); // 接收数据阻塞时间 socket.setSoTimeout(TIMEOUT); System.out.println("UDP 客户端已建立...."); // 发送数据报包 DatagramPacket sendPacket = new DatagramPacket(byteToSend, byteToSend.length, serverAddress, serverPort); // 接收的数据报包 DatagramPacket receiverPacket = new DatagramPacket( new byte[byteToSend.length], byteToSend.length); // 尝试接收次数 int tries = 0; boolean receivedResponse = false; do { System.out.println("向服务区端发送数据...."); // 向服务器端发送数据 socket.send(sendPacket); try { System.out.println("接收从服务器端返回的数据..."); // 从服务器端接收数据 socket.receive(receiverPacket); if (!receiverPacket.getAddress().equals(serverAddress)) { throw new IOException( "Received packet from an unknown source"); } receivedResponse = true; } catch (InterruptedIOException e) { tries++; System.out.println("Timed out," + (MAXTRIES - tries) + " more ties"); } } while ((!receivedResponse) && (tries < MAXTRIES)); if(receivedResponse) { System.out.println("Received:"+new String(receiverPacket.getData())); } else { System.out.println("No response--giving up!"); } } }
2.3.2 UDP服务器端
与TCP服务器一样,UDP服务器是建一个几个通信终端,并被动等待客户端发起连接。
典型的UDP服务器执行需要以下三步。
1. 创建一个DatagramSocket实例,指定本地端口号,并可以选择指定本地地址。此时,服务器已经准备好从任何客户端接收数据报文。 2. 使用DatagramSocket类的receive()方法来接收一个DatagramPacket实例。当receive()方法返回时,数据报文就包含了客户端的地址,这样我们就知道了回复信息应该发送到什么地方。 3. 使用DatagramSocket类的send() 和receive()方法来发送和接收DatagramPackets实例,进行通信。 |
下面的例子是使用UDP将客户端发送过来的消息会发给客户端的。
package com.suifeng.tcpip.chapter2; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; /** * UDP服务器端 * * @author Suifeng * */ public class UDPEchoServer { // 最大显示数组字节数 private static final int ECHO_MAX = 255; public static void main(String[] args) throws IOException { if (args.length != 1) { throw new IllegalArgumentException("Parameter:<Port>"); } // 服务器端口 int serverPort = Integer.parseInt(args[0]); // 服务器端 DatagramSocket socket = new DatagramSocket(serverPort); // 数据报 DatagramPacket packet = new DatagramPacket(new byte[ECHO_MAX], ECHO_MAX); System.out.println("UDP服务器已启动...."); while (true) { System.out.println("正在等待客户端发送数据...."); // 接收客户端发送的数据(阻塞) socket.receive(packet); System.out.println("Handing client at " + packet.getAddress().getHostAddress() + " on port " + packet.getPort()); System.out.println("Received Data:"+new String(packet.getData())); // 向客户端发送数据(这里只是将数据包进行转发--透传,没有进行任何处理) socket.send(packet); // 重置缓存区大小 packet.setLength(ECHO_MAX); } } }
先启用服务器端,监听39393端口
在启用客户端发送数据
再查看服务器端