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端口
在启用客户端发送数据
再查看服务器端
