
Java 网络编程:TCP 与 UDP 的「通信江湖」(基于UDP回显服务器)
你有没有想过,当你用 Java 写的聊天工具发送消息,或是用客户端给服务端传数据时,背后藏着两个 "通信高手"------TCP 和 UDP,在 Java 网络编程中,TCP(传输控制协议)与 UDP(用户数据报协议)是传输层的两种核心协议,分别适用于不同的数据交互场景。二者在连接机制、传输可靠性、数据格式等方面存在显著差异,希望这看完这篇博客后,能对其有所了解。
此次所讲的处在应用层,两个协议由操作系统提供的一组api=>socket.api,由于TCP和UDP差别还是比较大的,编写代码的时候,也是不同的风格,因此,socket api 提供了两套不同的api。
一、UDP 协议:无连接的高效传输
UDP的核心特征为:无连接,不可靠传输,面向数据报 和全双工
1.核心技术特征
- 无连接:无连接机制,无需建立客户端与服务端的逻辑连接,发送方直接向目标地址发送数据,接收方被动接收数据。也就是对于UDP来说,协议本身,不保存对方的信息。
注意:此连接是抽象概念上的连接,就比如说一对男女朋友去领结婚证,就从男女朋友变成了夫妻,在法律上建立了证明婚姻关系的连接。
- 不可靠传输:UDP不提供数据传输的可靠性保证。这意味着使用UDP发送数据时,不能保证数据一定会到达目的地,也不能保证数据的顺序和完整性,也就是说UDP就只负责传输,数据丢不丢失,与其无关,只要传了就行。
可不可靠传输就是表明,尽可能的将数据包传输送达,切如果出现丢包,能感知到
- 面向数据报:面向数据报,读写数据的时候,以一个数据报为单位(不是字符)
- 全双工:一个通信链路,支持双向通信,能读也能写
全双工看字面确实难以理解,这里以打电话的生活中例子帮助你理解一下:
当你和朋友打电话时,你说出 "今天加班吗?" 的同时,朋友可以直接接话 "不加班,约饭吗?"------ 你发消息(说话)的同时,也能接收朋友的消息(听话);
不会出现 "你必须等朋友说完,才能开始说" 的限制,双向互动是实时且同时的,这就是全双工。
2. Java 中 UDP 的实现
API 介绍
DatagramSocket:
DatagramSocket是UDP socket,⽤于发送和接收UDP数据报。
构造方法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| DatagramSocket() | 创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客⼾端) |
| DatagramSocket(int port) | 创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端⼝(⼀般⽤于服务端) |

方法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待 |
| void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发送) |
| void close() | 关闭此数据报套接字 |

DatagramPacket:
DatagramPacket是UDP Socket发送和接收的数据报,表示一个完整的UDP数据报。
构造方法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| DatagramPacket(byte[] buf, int length) | 构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length) |
| DatagramPacket(byte[] buf, int offset, int length,SocketAddress address) | 构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号 |
UDP数据报的载荷数据,就可以通过构造方法来指定
⽅法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP址发送的数据报中,获取接收端主机IP地址 |
| int getPort() | 从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号 |
| byte[] getData() | 获取数据报中的数据 |
3.构造UDP Echo Server
根据上述api,我们可以构建一个简单的回显服务器
客户端给服务器发一个数据请求,服务器返回一个数据响应,回显服务器就是请求是啥,响应是啥
代码⽰例:
服务器主要思路:
- 读取请求并解析
- 根据请求。计算响应(服务器最关键的逻辑)
- 把响应返回给客户端
- 打印一个日志
java
public class UdpEchoServer {
private DatagramSocket socket=null;
public UdpEchoServer(int port) throws SocketException {
socket=new DatagramSocket(port);
}
public void start() throws IOException {
//启动服务器
System.out.println("服务器启动");
while (true){
//1。读取请求并解析
DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request=new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求。计算响应(服务器最关键的逻辑)
String response =process(request);
DatagramPacket responsePacket=new
//3.把响应返回给客户端
DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);
//4.打印一个日志
System.out.printf("[%s:%d] req:%s, resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server=new UdpEchoServer(9090);
server.start();
}
}
重点细节解析:
回显服务器代码我们思路一下几个步骤来理解:
处理请求的过程,典型的服务器都是分成三个步骤
1.读取数据并请求解析
2.根据请求,计算响应(服务器最关键的逻辑)
此处写的是回显服务器,这个环境就相当于省略了
3...把响应返回给客户端
客户端主要思路:
- 从控制台读取用户输入的内容
- 把请求发给服务器,需要构造DatagramPacket对象
- 发送数据报
- 接受服务器的响应
- 从服务器读取的数据进行解析,打印出来
java
public class UdpEchoClient {
private DatagramSocket socket=null;
//UDP 本身不保存对端的信息,就自己的代码保存一下
private String serverIp;
private int serverPort;
//和服务器不同,此处构造方法是要指定访问的服务器的地址
public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
this.serverIp=serverIp;
this.serverPort=serverPort;
socket=new DatagramSocket();
}
public void start() throws IOException {
Scanner scanner=new Scanner(System.in);
while (true){
//1.从控制台读取用户输入的内容
System.out.println("请输入要发送的内容:");
if (!scanner.hasNext()){
break;
}
String requset=scanner.next();
//2.把请求发给服务器,需要构造DatagramPacket对象
DatagramPacket requestPacket=new DatagramPacket(requset.getBytes(),requset.getBytes().length, InetAddress.getByName(serverIp),serverPort);
//3.发送数据报
socket.send(requestPacket);
//4.接受服务器的响应
DatagramPacket responsePacket =new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//5.从服务器读取的数据进行解析,打印出来
String response=new String(responsePacket.getData(),0,requestPacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
client.start();
}
}
重点细节解析:
测试代码运行结果












