独立启动 UDPServer
tio-boot 内置了 UDPServer,开发者口可以通过两种方式启动
- 独立方式 UDPServer 占用单独的端口
- 联合方式 UDPServer 和其他协议共用一个端口
操作系统会自动区分 TCP 和 UDP
TCP 和 UDP 的区分是由操作系统的网络协议栈进行的。当数据包到达网络接口时,操作系统会检查数据包的协议头信息来确定它是 TCP 数据包还是 UDP 数据包。然后,根据这个信息,操作系统将数据包路由到相应的处理程序或应用程序。
TCP(传输控制协议)和 UDP(用户数据报协议)都建立在 IP(互联网协议)之上,但它们在传输层提供不同的服务模型:
TCP 提供一种可靠的数据传输服务,它确保数据准确无误地从发送方传输到接收方。TCP 通过序号、确认回应、重传机制、流量控制等方式来保证这种可靠性。由于这些特性,TCP 适用于要求高可靠性的应用,如网页浏览、文件传输、电子邮件等。
UDP 提供一种无连接的服务,允许数据以数据报文的形式发送,而不保证传输的可靠性、顺序或数据完整性。UDP 的这种简单性使其成为对实时性要求高(如在线游戏、语音或视频会议等)和/或对系统资源使用敏感的应用程序的理想选择。
当操作系统收到一个数据包时,它会检查 IP 头部中的协议字段来确定上层使用的是哪个协议(例如,TCP 或 UDP)。然后,它会检查数据包的端口号,并将数据包传递给在该端口监听的应用程序。如果是 TCP 数据包,它还会处理与 TCP 连接相关的各种控制消息,如 SYN、ACK 等。对于 UDP,由于其无连接的特性,操作系统的处理相对简单。
这种区分和处理都是透明的,通常对应用程序开发者和用户而言是不可见的。操作系统和网络协议栈负责确保网络通信的正确性和效率,而应用程序可以通过标准的网络接口(如套接字)来进行通信,无需关心底层的复杂性。
使用 Java 代码启动 TCP 端口和 UDP 端口
在 Java 中,要同时在同一个端口上处理 TCP 和 UDP,你需要分别为 TCP 和 UDP 创建不同的服务端套接字,并且这两个套接字监听同一个端口号。通常,你会为 TCP 使用ServerSocket
类,并为 UDP 使用DatagramSocket
类。
下面是一个简单的示例,展示了如何同时监听同一端口上的 TCP 和 UDP 请求:
package org.tio.showcase.udp.demo;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpUdpServer {
private static final int PORT = 12345; // 选择一个端口号
public static void main(String[] args) throws IOException {
// 创建并启动TCP服务器线程
Thread tcpThread = new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("TCP Server is running on port " + PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
// 处理TCP连接...
System.out.println("TCP Connection established");
// 可以为每个连接创建新线程或使用线程池来处理
}
} catch (IOException e) {
e.printStackTrace();
}
});
// 创建并启动UDP服务器线程
Thread udpThread = new Thread(() -> {
try (DatagramSocket datagramSocket = new DatagramSocket(PORT)) {
System.out.println("UDP Server is running on port " + PORT);
byte[] receiveData = new byte[1024];
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
datagramSocket.receive(receivePacket);
// 处理UDP包...
System.out.println("UDP Packet received");
// 可以在这里处理数据或者创建新线程进行处理
}
} catch (IOException e) {
e.printStackTrace();
}
});
tcpThread.start(); // 启动TCP服务器线程
udpThread.start(); // 启动UDP服务器线程
}
}
在这个例子中:
- 对于 TCP,我们创建了一个
ServerSocket
,它在指定的端口上监听传入的 TCP 连接请求。每当接受一个连接时,accept
方法会返回一个新的Socket
实例,用于与客户端通信。 - 对于 UDP,我们创建了一个
DatagramSocket
,它可以在指定的端口上接收 UDP 数据包。每次调用receive
方法都会从套接字的队列中取出一个数据包,我们可以处理这个数据包。
两个服务器(TCP 和 UDP)都在自己的线程中运行,这样它们就可以并行处理数据,并独立地接收和处理各自协议的数据。记住在实际应用中,你需要适当处理异常和多线程同步等问题。
独立启动 udp 服务
本文档提供了一个简单 UDP 服务器的实现方法
1. 主程序入口 (Main.java
)
这个方法会自动扫描带有@AComponentScan
注解的组件并进行初始化。
package demo.udp;
import com.litongjava.hotswap.wrapper.tio.boot.TioApplicationWrapper;
import com.litongjava.jfinal.aop.annotation.AComponent;
import com.litongjava.jfinal.aop.annotation.AComponentScan;
@AComponentScan
public class Main {
public static void main(String[] args) {
long start = System.currentTimeMillis();
TioApplicationWrapper.run(Main.class, args);
long end = System.currentTimeMillis();
System.out.println((end - start) + "(ms)");
}
}
2. UDP 消息处理 (DemoUdpHandler.java
)
该类负责接收和处理 UDP 消息。它读取数据包内容,记录发送者信息,并将收到的消息回发给发送者。
package demo.udp.handler;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import com.litongjava.tio.core.Node;
import com.litongjava.tio.core.udp.UdpPacket;
import com.litongjava.tio.core.udp.intf.UdpHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DemoUdpHandler implements UdpHandler {
/**
* 处理udp消息
*/
public void handler(UdpPacket udpPacket, DatagramSocket datagramSocket) {
byte[] data = udpPacket.getData();
String msg = new String(data);
Node remote = udpPacket.getRemote();
log.info("收到来自{}的消息:【{}】", remote, msg);
DatagramPacket datagramPacket = new DatagramPacket(data, data.length,
new InetSocketAddress(remote.getIp(), remote.getPort()));
try {
datagramSocket.send(datagramPacket);
} catch (Throwable e) {
log.error(e.toString(), e);
}
}
}
3. UDP 服务器配置 (UdpServerConfig.java
)
这个类用于配置和启动 UDP 服务器。它创建了一个UdpServerConf
实例,指定服务器的端口和处理器,然后启动服务器。
import java.net.SocketException;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.tio.core.udp.UdpServer;
import com.litongjava.tio.core.udp.UdpServerConf;
import demo.udp.handler.DemoUdpHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@AConfiguration
public class UdpServerConfig {
@Initialization
public void config() {
DemoUdpHandler fpmsUdpHandler = new DemoUdpHandler();
UdpServerConf udpServerConf = new UdpServerConf(3000, fpmsUdpHandler, 5000);
UdpServer udpServer;
try {
udpServer = new UdpServer(udpServerConf);
udpServer.start();
log.info("udp started");
} catch (SocketException e) {
e.printStackTrace();
}
}
}
Udp 客户端
tio-boot 同样提供了 UDP 客户端,你可以使用 UDP 客户端给上面的服务发送数据
package demo.udp.client;
import com.litongjava.tio.core.udp.UdpClient;
import com.litongjava.tio.core.udp.UdpClientConf;
public class UdpClientDemo {
public static void main(String[] args) {
UdpClientConf udpClientConf = new UdpClientConf("127.0.0.1", 3000, 5000);
UdpClient udpClient = new UdpClient(udpClientConf);
udpClient.start();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String str = i + "、" + "hello";
udpClient.send(str.getBytes());
}
long end = System.currentTimeMillis();
long iv = end - start;
System.out.println("耗时:" + iv + "ms");
}
}
测试代码地址
https://github.com/litongjava/java-ee-tio-boot-study/tree/main/tio-boot-latest-study/tio-boot-udp-demo01