独立启动 UDPServer

tio-boot 内置了 UDPServer,开发者口可以通过两种方式启动

  • 独立方式 UDPServer 占用单独的端口
  • 联合方式 UDPServer 和其他协议共用一个端口

操作系统会自动区分 TCP 和 UDP

TCP 和 UDP 的区分是由操作系统的网络协议栈进行的。当数据包到达网络接口时,操作系统会检查数据包的协议头信息来确定它是 TCP 数据包还是 UDP 数据包。然后,根据这个信息,操作系统将数据包路由到相应的处理程序或应用程序。

TCP(传输控制协议)和 UDP(用户数据报协议)都建立在 IP(互联网协议)之上,但它们在传输层提供不同的服务模型:

  1. TCP 提供一种可靠的数据传输服务,它确保数据准确无误地从发送方传输到接收方。TCP 通过序号、确认回应、重传机制、流量控制等方式来保证这种可靠性。由于这些特性,TCP 适用于要求高可靠性的应用,如网页浏览、文件传输、电子邮件等。

  2. 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 {

  @AInitialization
  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