1. 概述
在这篇文章中,我们将探讨使用Java进行网络通信,特别是在用户数据报协议(User Datagram Protocol,简称UDP)上。
UDP是一种通信协议,它通过网络传输独立的数据包,不保证它们一定能送达,也不保证接收顺序。
互联网上的大部分通信通常通过传输控制协议(Transmission Control Protocol,简称TCP)进行,但UDP也有其特定的应用场景,我们将在下一节中讨论。
2. 为什么使用UDP?
UDP与更常见的TCP有很大的不同。在考虑UDP表面级别的缺点之前,重要的是理解其较低的开销可以使其比TCP快得多。
除了速度,我们还需要记住,有些类型的通信并不需要TCP的可靠性,而是更重视低延迟。视频是一个可能从使用UDP而不是TCP中受益的应用的一个例子。
3. 构建UDP应用程序
构建UDP应用程序与构建TCP系统非常相似;唯一的区别在于客户端和服务器之间不需要建立点对点连接。
设置也非常直观。Java内置了支持UDP的网络功能,它位于java.net
包中。因此,为了通过UDP执行网络操作,我们只需要导入java.net
包中的类:java.net.DatagramSocket
和java.net.DatagramPacket
。
在接下来的章节中,我们将学习如何设计通过UDP通信的应用程序;我们将使用流行的回声协议来构建这个应用。
首先,我们将创建一个回声服务器,它会将接收到的任何消息发送回去,然后是发送任意消息到服务器的回声客户端,最后我们将测试应用程序以确保一切正常工作。
4. 服务器
在UDP通信中,一个单独的消息被封装在一个DatagramPacket
中,通过DatagramSocket
发送。
让我们从创建一个简单的服务器开始:
public class EchoServer extends Thread {
private DatagramSocket socket;
private boolean running;
private byte[] buf = new byte[256];
public EchoServer() {
socket = new DatagramSocket(4445);
}
public void run() {
running = true;
while (running) {
DatagramPacket packet
= new DatagramPacket(buf, buf.length);
socket.receive(packet);
InetAddress address = packet.getAddress();
int port = packet.getPort();
packet = new DatagramPacket(buf, buf.length, address, port);
String received
= new String(packet.getData(), 0, packet.getLength());
if (received.equals("end")) {
running = false;
continue;
}
socket.send(packet);
}
socket.close();
}
}
我们创建一个全局的DatagramSocket
,在整个过程中用于发送数据包,一个字节数组来包裹我们的消息,以及一个名为running
的状态变量。
为了简化,服务器继承自Thread
,因此我们将所有代码都放在run
方法中实现。
在run
方法中,我们创建一个循环,只要running
没有因错误或客户端发送的终止消息而变为false
,就一直运行。
在循环顶部,我们实例化一个DatagramPacket
来接收传入的消息。
接下来,我们调用socket的receive
方法。此方法会阻塞直到有消息到达,并将消息存储在传递给它的DatagramPacket
的字节数组中。
接收消息后,我们获取客户端的地址和端口,因为我们将响应发送回客户端。
然后,我们为向客户端发送消息创建一个DatagramPacket
。注意发送包的签名与接收包的不同。这个包也需要我们指定发送的目标客户端的地址和端口。
5. 客户端
现在让我们创建一个简单的客户端来匹配这个新服务器:
public class EchoClient {
private DatagramSocket socket;
private InetAddress address;
private byte[] buf;
public EchoClient() {
socket = new DatagramSocket();
address = InetAddress.getByName("localhost");
}
public String sendEcho(String msg) {
buf = msg.getBytes();
DatagramPacket packet
= new DatagramPacket(buf, buf.length, address, 4445);
socket.send(packet);
packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
String received = new String(
packet.getData(), 0, packet.getLength());
return received;
}
public void close() {
socket.close();
}
}
代码与服务器的大致相同。我们有全局的DatagramSocket
和服务器的地址。我们在构造函数中初始化这些变量。
我们有一个单独的方法,用于向服务器发送消息并返回响应。
首先,我们将字符串消息转换为字节数组,然后为发送消息创建一个DatagramPacket
。
接着,我们发送消息。发送后,我们将DatagramPacket
立即转换为接收模式。
当回声到达时,我们将字节转换回字符串并返回。
6. 测试
在UDPTest.java
类中,我们简单地创建一个测试来检查我们的两个应用程序的回声功能:
public class UDPTest {
EchoClient client;
@Before
public void setup(){
new EchoServer().start();
client = new EchoClient();
}
@Test
public void whenCanSendAndReceivePacket_thenCorrect() {
String echo = client.sendEcho("hello server");
assertEquals("hello server", echo);
echo = client.sendEcho("server is working");
assertFalse(echo.equals("hello server"));
}
@After
public void tearDown() {
client.sendEcho("end");
client.close();
}
}
在setup
方法中,我们启动服务器并创建客户端。而在tearDown
方法中,我们向服务器发送一个终止消息,以便它关闭,同时我们也关闭客户端。
7. 总结
在这篇文章中,我们了解了用户数据报协议,并成功构建了自己的客户端-服务器应用程序,它们通过UDP进行通信。
要获取本文中使用的示例的完整源代码,您可以查看GitHub项目。