一、UDP Socket基础概念
- DatagramSocket :代表操作系统中的socket文件 ,类似对网卡的抽象。创建该对象就是在操作系统中打开一个socket文件。通过它写入数据是经网卡发送数据,读取数据是经网卡接收数据。
- 构造方法:
- DatagramSocket() :创建一个UDP数据报套接字,绑定到本机任意一个随机端口(一般用于客户端)。
- DatagramSocket(int port) :创建一个UDP数据报套接字,绑定到本机指定的端口(一般用于服务器)。
- 关键方法:
- void receive(DatagramPacket p) :从此套接字接收数据报(如果无数据接收,该方法会阻塞等待) 。
- void send(DatagramPacket p) :从此套接字发送数据报包(不会阻塞等待,直接发送) 。
- void close() :关闭此数据报套接字。
- DatagramPacket :代表一个UDP数据报,是UDP通信时的基本传输单位,本质是包装了字节数组。
- 构造方法:
- DatagramPacket(byte[] buf, int length) :构造一个用于接收数据报的对象,接收的数据放在 buf 数组中,接收长度为 length 。
- DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) :构造一个用于发送数据报的对象,数据从 buf 数组的 offset 位置开始,长度为 length ,发送到指定 address (IP地址)和 port (端口号) 。
- 关键方法:
- InetAddress getAddress() :从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 。
- int getPort() :从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号 。
- byte[] getData() :获取数据报中的数据 。
二、网络通信基本流程
- 客户端:主动发起通信,流程一般为从控制台读取用户输入内容,构造UDP请求并发送给服务器,然后从服务器读取响应,最后把响应结果显示到控制台上。
- 服务器:被动接收通信,基本流程如下代码所示:
while (true) {
// 1. 创建一个空白的 DatagramPacket 对象
DatagramPacket reqPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(reqPacket);
// 2. 根据请求计算响应
String request = new String(reqPacket.getData(), 0, reqPacket.getLength());
String response = process(request);
// 3. 把响应写回到客户端
DatagramPacket respPacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
reqPacket.getSocketAddress());
socket.send(respPacket);
// 4. 打印日志
System.out.printf("[%s:%d] req: %s, resp: %s\n",
reqPacket.getAddress(), reqPacket.getPort(), request, response);
}
上述代码中,服务器通过死循环持续处理客户端请求。 receive 方法会阻塞等待客户端请求,收到请求后解析数据,计算响应,再将响应构造为 DatagramPacket 发回客户端,并打印相关日志。其中 process 方法需根据具体业务逻辑实现,这里省略具体实现。
三、IP地址与端口相关
- 回环IP(127.0.0.1):表示主机自身。当服务器和客户端在同一主机上时,无论主机真实IP是什么,都可通过127.0.0.1访问服务器。
- 端口占用问题:一个端口同一时刻只能被一个进程绑定。若第二个进程尝试绑定已被占用的端口,会抛出 Address already in use: bind 异常。可使用 netstat -ano | findstr "端口号" 命令查看端口占用情况。
四、客户端与服务器创建要点
- 客户端:构造方法需填写服务器的IP和端口,创建 socket 对象时通常无需指定端口号,操作系统会自动分配空闲端口,指定固定端口可能与其他程序冲突。示例代码:
public EchoClient(String serverIp, int serverPort) {
this.serverIp = serverIp;
this.serverPort = serverPort;
socket = new DatagramSocket();
}
- 服务器:只需指定自己监听的端口,通过收到的请求确定发给谁。示例代码:
public EchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
五、其他拓展
- 云计算相关:复杂的请求计算响应过程可借助多主机、云服务器等引入更多硬件资源来处理。提到了一些吃资源的游戏(如《戴森球计划》等) ,以及虚拟机和云服务器对比,云服务器有外网IP,便于项目展示等。
- 网络编程后续要点:如将服务器程序打成jar包、Linux服务器操作、开启云服务器防火墙等内容会在后续JavaEE进阶、maven、Linux章节详细介绍。