当前位置: 首页 > news >正文

Java 网络编程全解析

前言:网络编程的意义与价值

前言:网络编程的意义与价值
在当今互联网时代,网络编程是软件开发的核心技能之一。无论是桌面应用、移动应用还是企业级系统,几乎都需要与网络交互。Java 作为一门跨平台的编程语言,提供了完善的网络编程 API,使得开发者能够轻松实现各种网络通信功能。

本指南将全面覆盖 Java 网络编程的方方面面,从基础的 TCP/IP 协议到高级的 NIO 框架,从简单的 Socket 通信到复杂的分布式系统,辅以大量图解和实例代码,帮助读者构建完整的 Java 网络编程知识体系。

第一章:计算机网络基础

1.1 网络的基本概念

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

1.2 网络协议与分层模型

网络协议是计算机网络中进行数据交换而建立的规则、标准或约定的集合。为了简化网络设计,通常采用分层模型。

1.2.1 OSI 七层模型

OSI(Open Systems Interconnection)七层模型是国际标准化组织(ISO)提出的一个网络通信模型,将网络通信分为七个层次:

  • 物理层(Physical Layer):负责物理介质上的比特流传输
  • 数据链路层(Data Link Layer):负责将比特流封装成帧,提供节点间的可靠传输
  • 网络层(Network Layer):负责数据包的路由和转发
  • 传输层(Transport Layer):负责端到端的可靠数据传输
  • 会话层(Session Layer):负责建立、管理和终止会话
  • 表示层(Presentation Layer):负责数据的格式转换和加密
  • 应用层(Application Layer):直接为应用程序提供服务
1.2.2 TCP/IP 四层模型

TCP/IP 模型是实际应用中广泛使用的网络模型,它将 OSI 七层模型简化为四层:

  • 网络接口层:对应 OSI 的物理层和数据链路层
  • 网络层:对应 OSI 的网络层,主要协议有 IP、ICMP、ARP 等
  • 传输层:对应 OSI 的传输层,主要协议有 TCP、UDP
  • 应用层:对应 OSI 的会话层、表示层和应用层,主要协议有 HTTP、FTP、SMTP 等
1.3 核心网络协议
1.3.1 IP 协议

IP(Internet Protocol)是网络层的核心协议,负责将数据包从源主机发送到目标主机。

主要特点:

  • 无连接:发送数据前不需要建立连接
  • 不可靠:不保证数据的可靠传输
  • 尽最大努力交付:会尽力传输数据,但不保证一定到达

IP 地址是 IP 协议中的重要概念,用于标识网络中的主机。IPv4 地址是 32 位的二进制数,通常表示为四个十进制数(如 192.168.1.1)。IPv6 地址是 128 位的二进制数,用于解决 IPv4 地址耗尽的问题。

1.3.2 TCP 协议

TCP(Transmission Control Protocol)是传输层的协议,提供可靠的、面向连接的数据流传输服务。

TCP 的主要特点:

  • 面向连接:通信前需要建立连接(三次握手),通信结束后需要释放连接(四次挥手)
  • 可靠传输:通过确认机制、重传机制、流量控制和拥塞控制保证数据的可靠传输
  • 面向字节流:将应用层的数据视为字节流,无消息边界

TCP 三次握手过程:

客户端发送 SYN 报文,请求建立连接
服务器收到 SYN 报文后,发送 SYN+ACK 报文,确认客户端的请求并请求建立连接
客户端收到 SYN+ACK 报文后,发送 ACK 报文,确认服务器的请求,连接建立

TCP 四次挥手过程:

客户端发送 FIN 报文,请求释放连接
服务器收到 FIN 报文后,发送 ACK 报文,确认客户端的请求
服务器准备好释放连接后,发送 FIN 报文
客户端收到 FIN 报文后,发送 ACK 报文,确认服务器的请求,连接释放

1.3.3 UDP 协议

UDP(User Datagram Protocol)是传输层的另一种协议,提供不可靠的、无连接的数据报传输服务。

UDP 的主要特点:

  • 无连接:通信前不需要建立连接
  • 不可靠:不保证数据的可靠传输,没有确认机制和重传机制
  • 面向数据报:数据以数据报的形式发送,有消息边界
  • 开销小:相比 TCP,UDP 的头部开销小,传输效率高
1.3.4 应用层协议

应用层协议建立在 TCP 或 UDP 之上,为特定的应用提供服务:

  • HTTP(HyperText Transfer Protocol):用于万维网(WWW)服务,基于 TCP
  • HTTPS:HTTP 的安全版本,使用 SSL/TLS 加密,基于 TCP
  • FTP(File Transfer Protocol):用于文件传输,基于 TCP
  • SMTP(Simple Mail Transfer Protocol):用于发送电子邮件,基于 TCP
  • POP3(Post Office Protocol 3):用于接收电子邮件,基于 TCP
  • DNS(Domain Name System):用于域名解析,通常基于 UDP
  • Telnet:用于远程登录,基于 TCP
  • SSH(Secure Shell):用于安全的远程登录,基于 TCP
1.4 端口与套接字
1.4.1 端口

端口是传输层的概念,用于标识主机上的进程。端口号是一个 16 位的整数,范围是 0-65535。

  • 0-1023:知名端口,用于知名服务(如 HTTP 使用 80 端口,HTTPS 使用 443 端口)
  • 1024-49151:注册端口,用于一些应用程序
  • 49152-65535:动态端口,用于临时分配
1.4.2 套接字

套接字(Socket)是网络编程的抽象概念,用于表示网络中的一个连接端点。在 TCP/IP 协议中,套接字由 IP 地址和端口号组成,即(IP 地址,端口号)。

通过套接字,应用程序可以与远程主机上的应用程序进行通信。

第二章:Java 网络编程基础

2.1 Java 网络编程 API 概述

Java 提供了丰富的网络编程 API,主要位于java.net包中,包括:

  • Socket 和 ServerSocket:用于 TCP 通信
  • DatagramSocket 和 DatagramPacket:用于 UDP 通信
  • URL 和 URLConnection:用于访问 URL 资源
  • InetAddress:用于表示 IP 地址
  • NetworkInterface:用于表示网络接口
  • Proxy:用于表示代理服务器

Java NIO(New IO)提供了更高效的网络编程 API,位于 java.nio 包及其子包中,包括:

  • SocketChannel 和 ServerSocketChannel:用于 TCP 通信的通道
  • DatagramChannel:用于 UDP 通信的通道
  • Selector:用于多路复用
  • Buffer:用于数据缓冲
2.2 InetAddress 类

InetAddress 类用于表示 IP 地址,它没有公共的构造方法,只能通过静态方法获取实例。

常用方法:

  • static InetAddress getByName(String host):根据主机名或 IP 地址字符串获取 InetAddress 实例
  • static InetAddress getLocalHost():获取本地主机的 InetAddress 实例
  • String getHostName():获取主机名
  • String getHostAddress():获取 IP 地址字符串
  • boolean isReachable(int timeout):测试是否可以到达该地址
import java.net.InetAddress;
import java.net.UnknownHostException;public class InetAddressExample {public static void main(String[] args) {try {// 获取本地主机的InetAddress实例InetAddress localHost = InetAddress.getLocalHost();System.out.println("本地主机名: " + localHost.getHostName());System.out.println("本地IP地址: " + localHost.getHostAddress());// 获取指定主机的InetAddress实例InetAddress baidu = InetAddress.getByName("www.baidu.com");System.out.println("\n百度主机名: " + baidu.getHostName());System.out.println("百度IP地址: " + baidu.getHostAddress());// 获取指定IP地址的InetAddress实例InetAddress ipAddr = InetAddress.getByName("127.0.0.1");System.out.println("\nIP地址对应的主机名: " + ipAddr.getHostName());// 测试是否可以到达该地址boolean reachable = baidu.isReachable(5000);System.out.println("\n是否可以到达百度服务器: " + reachable);} catch (Exception e) {e.printStackTrace();}}
}
2.3 TCP 编程

TCP 是一种面向连接的、可靠的传输层协议。Java 中使用 Socket 和 ServerSocket 类实现 TCP 通信。

2.3.1 TCP 通信基本流程

TCP 通信分为服务器端和客户端:

服务器端流程:

  • 创建 ServerSocket 对象,绑定到指定端口
  • 调用 accept () 方法,监听客户端连接,该方法会阻塞直到有客户端连接
  • 获得客户端的 Socket 对象后,通过输入流和输出流与客户端通信
  • 通信结束后,关闭 Socket 和 ServerSocket

客户端流程:

  • 创建 Socket 对象,指定服务器的 IP 地址和端口号,与服务器建立连接
  • 通过输入流和输出流与服务器通信
  • 通信结束后,关闭 Socket
2.3.2 TCP 服务器端示例
import java.io.*;
import java.net.*;public class TCPServer {public static void main(String[] args) {ServerSocket serverSocket = null;try {// 创建ServerSocket,绑定到8888端口serverSocket = new ServerSocket(8888);System.out.println("服务器已启动,等待客户端连接...");// 循环接受客户端连接while (true) {// 监听客户端连接,阻塞方法Socket clientSocket = serverSocket.accept();System.out.println("客户端已连接:" + clientSocket.getInetAddress().getHostAddress());// 为每个客户端创建一个线程处理new Thread(new ClientHandler(clientSocket)).start();}} catch (IOException e) {e.printStackTrace();} finally {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}// 处理客户端请求的线程类static class ClientHandler implements Runnable {private Socket clientSocket;public ClientHandler(Socket socket) {this.clientSocket = socket;}@Overridepublic void run() {BufferedReader in = null;PrintWriter out = null;try {// 获取输入流,读取客户端发送的数据in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));// 获取输出流,向客户端发送数据out = new PrintWriter(clientSocket.getOutputStream(), true);String inputLine;// 读取客户端发送的信息while ((inputLine = in.readLine()) != null) {System.out.println("收到客户端消息:" + inputLine);// 向客户端发送响应out.println("服务器已收到:" + inputLine);// 如果客户端发送"bye",则关闭连接if ("bye".equals(inputLine)) {break;}}System.out.println("客户端已断开连接");} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (in != null) in.close();if (out != null) out.close();if (clientSocket != null) clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}}
}
2.3.3 TCP 客户端示例
import java.io.*;
import java.net.*;public class TCPClient {public static void main(String[] args) {Socket socket = null;BufferedReader in = null;PrintWriter out = null;BufferedReader stdIn = null;try {// 创建Socket,连接到本地的8888端口socket = new Socket("localhost", 8888);System.out.println("已连接到服务器");// 获取输入流,读取服务器发送的数据in = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 获取输出流,向服务器发送数据out = new PrintWriter(socket.getOutputStream(), true);// 读取用户从控制台输入的数据stdIn = new BufferedReader(new InputStreamReader(System.in));String userInput;// 循环读取用户输入并发送给服务器while ((userInput = stdIn.readLine()) != null) {// 向服务器发送数据out.println(userInput);// 读取服务器的响应String response = in.readLine();System.out.println("服务器响应:" + response);// 如果用户输入"bye",则退出循环if ("bye".equals(userInput)) {break;}}} catch (UnknownHostException e) {System.err.println("不知道的主机:localhost");e.printStackTrace();} catch (IOException e) {System.err.println("无法与服务器建立连接");e.printStackTrace();} finally {// 关闭资源try {if (in != null) in.close();if (out != null) out.close();if (stdIn != null) stdIn.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}
}
2.3.4 TCP 通信的注意事项
  • 多线程处理:服务器端需要使用多线程处理多个客户端的连接,否则一个客户端的连接会阻塞其他客户端。
  • 流的关闭顺序:通常先关闭输出流,再关闭输入流,最后关闭 Socket。
  • 异常处理:网络通信中可能会出现各种异常,需要妥善处理。
  • 编码问题:在使用字符流时,需要注意编码问题,避免出现乱码。
  • 缓冲区刷新:使用缓冲流时,需要注意及时刷新缓冲区,确保数据被发送出去。
2.4 UDP 编程

UDP 是一种无连接的、不可靠的传输层协议。Java 中使用 DatagramSocket 和 DatagramPacket 类实现 UDP 通信。

2.4.1 UDP 通信基本流程

UDP 通信也分为服务器端和客户端:

服务器端流程:

  • 创建 DatagramSocket 对象,绑定到指定端口
  • 创建 DatagramPacket 对象,用于接收数据
  • 调用 receive () 方法接收数据,该方法会阻塞直到收到数据
  • 处理收到的数据
  • 如果需要响应,创建新的 DatagramPacket 对象,调用 send () 方法发送响应
  • 通信结束后,关闭 DatagramSocket

客户端流程:

  • 创建 DatagramSocket 对象
  • 创建 DatagramPacket 对象,包含要发送的数据、服务器的 IP 地址和端口号
  • 调用 send () 方法发送数据
  • 如果需要接收响应,创建新的 DatagramPacket 对象,调用 receive () 方法接收响应
  • 通信结束后,关闭 DatagramSocket
2.4.2 UDP 服务器端示例
import java.net.*;
import java.io.*;public class UDPServer {public static void main(String[] args) {DatagramSocket socket = null;try {// 创建DatagramSocket,绑定到8888端口socket = new DatagramSocket(8888);System.out.println("UDP服务器已启动,等待数据...");// 创建接收数据的缓冲区byte[] receiveBuffer = new byte[1024];// 循环接收数据while (true) {// 创建DatagramPacket用于接收数据DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);// 接收数据,阻塞方法socket.receive(receivePacket);// 解析收到的数据String data = new String(receivePacket.getData(), 0, receivePacket.getLength());InetAddress clientAddress = receivePacket.getAddress();int clientPort = receivePacket.getPort();System.out.println("收到来自 " + clientAddress.getHostAddress() + ":" + clientPort + " 的数据:" + data);// 如果收到"bye",则退出循环if ("bye".equals(data)) {break;}// 准备响应数据String response = "服务器已收到:" + data;byte[] sendBuffer = response.getBytes();// 创建DatagramPacket用于发送响应DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, clientAddress, clientPort);// 发送响应socket.send(sendPacket);}System.out.println("服务器关闭");} catch (IOException e) {e.printStackTrace();} finally {if (socket != null) {socket.close();}}}
}
2.4.3 UDP 客户端示例
import java.net.*;
import java.io.*;public class UDPClient {public static void main(String[] args) {DatagramSocket socket = null;BufferedReader stdIn = null;try {// 创建DatagramSocket,不指定端口,由系统分配socket = new DatagramSocket();// 获取服务器地址InetAddress serverAddress = InetAddress.getByName("localhost");int serverPort = 8888;// 读取用户从控制台输入的数据stdIn = new BufferedReader(new InputStreamReader(System.in));String userInput;// 循环读取用户输入并发送给服务器while ((userInput = stdIn.readLine()) != null) {// 准备发送的数据byte[] sendBuffer = userInput.getBytes();// 创建DatagramPacket用于发送数据DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, serverAddress, serverPort);// 发送数据socket.send(sendPacket);// 如果用户输入"bye",则退出循环if ("bye".equals(userInput)) {break;}// 创建接收响应的缓冲区byte[] receiveBuffer = new byte[1024];// 创建DatagramPacket用于接收响应DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);// 接收响应,阻塞方法socket.receive(receivePacket);// 解析响应数据String response = new String(receivePacket.getData(), 0, receivePacket.getLength());System.out.println("服务器响应:" + response);}} catch (UnknownHostException e) {System.err.println("不知道的主机:localhost");e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (socket != null) {socket.close();}if (stdIn != null) {try {stdIn.close();} catch (IOException e) {e.printStackTrace();}}}}
}
2.4.4 UDP 通信的注意事项
  • 数据报大小限制:UDP 数据报的大小有限制,通常建议不超过 512 字节,否则可能会被分片。
  • 不可靠性:UDP 不保证数据的可靠传输,可能会丢失、重复或乱序,需要应用层自己处理这些问题。
  • 无连接:UDP 通信不需要建立连接,每个数据报都是独立的。
  • 广播和多播:UDP 支持广播和多播,这在某些场景下非常有用。
2.5 URL 和 URLConnection

Java 提供了 URL 和 URLConnection 类,用于访问互联网上的资源。

2.5.1 URL 类

URL(Uniform Resource Locator)表示统一资源定位符,用于标识互联网上的资源。

URL 的格式通常为:协议://主机名:端口/路径?查询参数#片段

常用方法:

  • String getProtocol():获取协议名称
  • String getHost():获取主机名
  • int getPort():获取端口号
  • String getPath():获取路径
  • String getQuery():获取查询参数
  • URLConnection openConnection():打开与该 URL 的连接
2.5.2 URLConnection 类

URLConnection 是一个抽象类,表示与 URL 所指向资源的连接。它的子类 HttpURLConnection 用于 HTTP 连接。

常用方法:

  • void setRequestMethod(String method):设置请求方法(GET、POST 等)
  • void setDoInput(boolean doInput):设置是否允许输入
  • void setDoOutput(boolean doOutput):设置是否允许输出
  • void setRequestProperty(String key, String value):设置请求头
  • InputStream getInputStream():获取输入流,用于读取响应
  • OutputStream getOutputStream():获取输出流,用于发送数据
  • int getResponseCode():获取响应码
  • String getResponseMessage():获取响应消息
  • Map<String, List> getHeaderFields():获取响应头
2.5.3 使用 URL 和 URLConnection 访问网络资源
import java.net.*;
import java.io.*;
import java.util.*;public class URLExample {public static void main(String[] args) {try {// 创建URL对象URL url = new URL("https://www.baidu.com");System.out.println("协议: " + url.getProtocol());System.out.println("主机名: " + url.getHost());System.out.println("端口: " + url.getPort());System.out.println("路径: " + url.getPath());// 打开连接URLConnection connection = url.openConnection();// 设置连接超时connection.setConnectTimeout(5000);// 设置读取超时connection.setReadTimeout(5000);// 获取响应头Map<String, List<String>> headers = connection.getHeaderFields();System.out.println("\n响应头:");for (Map.Entry<String, List<String>> entry : headers.entrySet()) {String key = entry.getKey();for (String value : entry.getValue()) {System.out.println(key + ": " + value);}}// 如果是HTTP连接,可以获取更多信息if (connection instanceof HttpURLConnection) {HttpURLConnection httpConnection = (HttpURLConnection) connection;System.out.println("\n响应码: " + httpConnection.getResponseCode());System.out.println("响应消息: " + httpConnection.getResponseMessage());}// 读取响应内容try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"))) {String line;StringBuilder content = new StringBuilder();while ((line = in.readLine()) != null) {content.append(line).append("\n");}System.out.println("\n响应内容前100个字符:");if (content.length() > 100) {System.out.println(content.substring(0, 100));} else {System.out.println(content.toString());}}// 断开连接if (connection instanceof HttpURLConnection) {((HttpURLConnection) connection).disconnect();}} catch (MalformedURLException e) {System.err.println("URL格式错误");e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
}
2.5.4 使用 HttpURLConnection 发送 POST 请求
import java.net.*;
import java.io.*;
import java.nio.charset.StandardCharsets;public class HTTPPostExample {public static void main(String[] args) {HttpURLConnection connection = null;OutputStream os = null;BufferedReader in = null;try {// 创建URL对象URL url = new URL("https://httpbin.org/post");// 打开连接connection = (HttpURLConnection) url.openConnection();// 设置请求方法为POSTconnection.setRequestMethod("POST");// 允许输入输出connection.setDoInput(true);connection.setDoOutput(true);// 设置请求头connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");connection.setRequestProperty("User-Agent", "Mozilla/5.0");// 设置连接超时和读取超时connection.setConnectTimeout(5000);connection.setReadTimeout(5000);// 准备POST数据String postData = "name=张三&age=25&city=北京";byte[] postDataBytes = postData.getBytes(StandardCharsets.UTF_8);// 发送POST数据os = connection.getOutputStream();os.write(postDataBytes);os.flush();// 获取响应码int responseCode = connection.getResponseCode();System.out.println("响应码: " + responseCode);// 读取响应内容in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));String line;StringBuilder response = new StringBuilder();while ((line = in.readLine()) != null) {response.append(line);}System.out.println("响应内容: " + response.toString());} catch (MalformedURLException e) {System.err.println("URL格式错误");e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (os != null) os.close();if (in != null) in.close();if (connection != null) connection.disconnect();} catch (IOException e) {e.printStackTrace();}}}
}

第三章:Java NIO 网络编程

3.1 NIO 概述

NIO(New IO)是 Java 1.4 引入的新 IO 模型,与传统的 IO(也称为 BIO,Blocking IO)相比,NIO 提供了更高效的 IO 操作方式。

NIO 的核心组件:

  • 通道(Channel):类似于流,但可以双向操作,既可以读也可以写
  • 缓冲区(Buffer):用于存储数据,所有数据的读写都必须通过缓冲区
  • 选择器(Selector):用于多路复用,可以同时监控多个通道的事件

NIO 的主要特点:

  • 非阻塞 IO:通道可以设置为非阻塞模式,避免线程阻塞在 IO 操作上
  • 多路复用:一个线程可以处理多个通道的 IO 操作
  • 面向缓冲区:数据的读写都通过缓冲区,提高了 IO 效率
    3.2 缓冲区(Buffer)
    Buffer 是一个抽象类,用于存储数据。它有多个子类,对应不同的数据类型:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。

Buffer 的核心属性:

  • capacity:缓冲区的容量,创建后不可改变
  • position:下一个要读写的位置
  • limit:缓冲区的限制,不能读写超过 limit 的数据
  • mark:标记位置,用于后续的 reset 操作

Buffer 的常用方法:

flip():将缓冲区从写模式切换到读模式,设置 limit=position,position=0
rewind():重置 position 为 0,用于重新读取缓冲区的数据
clear():清空缓冲区,设置 position=0,limit=capacity,但不实际清除数据
compact():将未读取的数据移到缓冲区的开头,设置 position 为未读取数据的长度,limit=capacity
mark():标记当前 position
reset():将 position 重置为 mark 标记的位置
remaining():返回 remaining = limit - position,即还可以读写的数据量

import java.nio.ByteBuffer;public class BufferExample {public static void main(String[] args) {// 创建一个容量为10的ByteBufferByteBuffer buffer = ByteBuffer.allocate(10);System.out.println("初始状态: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 向缓冲区写入数据byte[] data = "Hello".getBytes();buffer.put(data);System.out.println("写入数据后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 切换到读模式buffer.flip();System.out.println("flip后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 读取缓冲区的数据byte[] readData = new byte[buffer.remaining()];buffer.get(readData);System.out.println("读取的数据: " + new String(readData));System.out.println("读取数据后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 重置position,重新读取buffer.rewind();System.out.println("rewind后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 读取前两个字节byte[] partData = new byte[2];buffer.get(partData);System.out.println("读取的部分数据: " + new String(partData));System.out.println("读取部分数据后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 压缩缓冲区,将未读取的数据移到开头buffer.compact();System.out.println("compact后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 继续写入数据buffer.put("World".getBytes());System.out.println("继续写入数据后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 切换到读模式buffer.flip();System.out.println("再次flip后: capacity=" + buffer.capacity() + ", position=" + buffer.position() + ", limit=" + buffer.limit());// 读取所有数据byte[] allData = new byte[buffer.remaining()];buffer.get(allData);System.out.println("最终读取的数据: " + new String(allData));}
}
3.3 通道(Channel)

Channel 是一个接口,用于 IO 操作。与流不同,通道可以双向操作,并且可以异步读写。

常用的 Channel 实现类:

  • FileChannel:用于文件 IO
  • SocketChannel:用于 TCP 客户端
  • ServerSocketChannel:用于 TCP 服务器端
  • DatagramChannel:用于 UDP
  • Pipe.SinkChannel 和 Pipe.SourceChannel:用于线程间通信

Channel 的常用方法:

open():打开通道
close():关闭通道
read(Buffer dst):从通道读取数据到缓冲区
write(Buffer src):从缓冲区写入数据到通道
configureBlocking(boolean block):设置通道为阻塞或非阻塞模式

3.4 选择器(Selector)

Selector 是 NIO 中的多路复用器,它可以同时监控多个通道的事件,使得一个线程可以处理多个通道的 IO 操作。

常用的事件:

  • SelectionKey.OP_ACCEPT:接受连接事件,用于 ServerSocketChannel
  • SelectionKey.OP_CONNECT:连接就绪事件,用于 SocketChannel
  • SelectionKey.OP_READ:读就绪事件,通道中有数据可以读取
  • SelectionKey.OP_WRITE:写就绪事件,通道可以写入数据

Selector 的使用流程:

创建 Selector 对象
将通道注册到 Selector 上,并指定感兴趣的事件
调用 Selector 的 select () 方法,等待事件发生
获取就绪的事件集合
处理每个事件
重复步骤 3-5

Selector使用示例:

import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.*;public class SelectorServer {public static void main(String[] args) {Selector selector = null;ServerSocketChannel serverSocketChannel = null;try {// 创建Selectorselector = Selector.open();// 创建ServerSocketChannelserverSocketChannel = ServerSocketChannel.open();// 绑定到8888端口serverSocketChannel.socket().bind(new InetSocketAddress(8888));// 设置为非阻塞模式serverSocketChannel.configureBlocking(false);// 将ServerSocketChannel注册到Selector,关注ACCEPT事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("NIO服务器已启动,监听端口8888...");while (true) {// 等待事件发生,返回就绪的通道数量int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 获取就绪的事件集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();// 处理完事件后,需要从集合中移除,避免重复处理iterator.remove();// 处理接受连接事件if (key.isAcceptable()) {handleAccept(key, selector);}// 处理读事件if (key.isReadable()) {handleRead(key);}// 处理写事件(本例中不处理)if (key.isWritable()) {// 处理写事件...}}}} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (selector != null) {selector.close();}if (serverSocketChannel != null) {serverSocketChannel.close();}} catch (IOException e) {e.printStackTrace();}}}// 处理接受连接事件private static void handleAccept(SelectionKey key, Selector selector) throws IOException {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();// 接受客户端连接SocketChannel socketChannel = serverSocketChannel.accept();if (socketChannel == null) {return;}System.out.println("客户端已连接:" + socketChannel.getRemoteAddress());// 设置为非阻塞模式socketChannel.configureBlocking(false);// 将SocketChannel注册到Selector,关注READ事件socketChannel.register(selector, SelectionKey.OP_READ);// 向客户端发送欢迎消息String welcomeMsg = "欢迎连接到NIO服务器!\n";ByteBuffer buffer = ByteBuffer.wrap(welcomeMsg.getBytes(StandardCharsets.UTF_8));socketChannel.write(buffer);}// 处理读事件private static void handleRead(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 读取数据int bytesRead = socketChannel.read(buffer);if (bytesRead == -1) {// 客户端关闭连接System.out.println("客户端已断开连接:" + socketChannel.getRemoteAddress());key.cancel();socketChannel.close();return;}// 切换到读模式buffer.flip();// 解析数据byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data, StandardCharsets.UTF_8);System.out.println("收到来自 " + socketChannel.getRemoteAddress() + " 的消息:" + message);// 如果客户端发送"bye",则关闭连接if ("bye\n".equals(message) || "bye\r\n".equals(message)) {String byeMsg = "再见!\n";ByteBuffer byeBuffer = ByteBuffer.wrap(byeMsg.getBytes(StandardCharsets.UTF_8));socketChannel.write(byeBuffer);System.out.println("关闭与 " + socketChannel.getRemoteAddress() + " 的连接");key.cancel();socketChannel.close();return;}// 发送响应String response = "服务器已收到:" + message;ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));socketChannel.write(responseBuffer);}
}

NIO客户端示例:

import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.io.*;public class NIOClient {public static void main(String[] args) {SocketChannel socketChannel = null;BufferedReader stdIn = null;try {// 创建SocketChannelsocketChannel = SocketChannel.open();// 设置为非阻塞模式socketChannel.configureBlocking(false);// 连接服务器InetSocketAddress serverAddress = new InetSocketAddress("localhost", 8888);socketChannel.connect(serverAddress);// 创建SelectorSelector selector = Selector.open();// 将SocketChannel注册到Selector,关注CONNECT和READ事件socketChannel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);// 读取用户输入stdIn = new BufferedReader(new InputStreamReader(System.in));boolean running = true;while (running) {// 等待事件发生selector.select();// 获取就绪的事件集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();// 处理连接事件if (key.isConnectable()) {if (socketChannel.finishConnect()) {System.out.println("已连接到服务器");// 连接成功后,只关注READ事件key.interestOps(SelectionKey.OP_READ);// 提示用户输入System.out.println("请输入消息(输入bye退出):");} else {// 连接失败key.cancel();running = false;}}// 处理读事件if (key.isReadable()) {ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = socketChannel.read(buffer);if (bytesRead == -1) {// 服务器关闭连接System.out.println("服务器已关闭连接");key.cancel();socketChannel.close();running = false;break;}// 解析服务器响应buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("服务器响应:" + new String(data, StandardCharsets.UTF_8));// 如果收到服务器的再见消息,退出if (new String(data, StandardCharsets.UTF_8).contains("再见")) {running = false;break;}// 提示用户输入System.out.println("请输入消息(输入bye退出):");}}// 如果连接已关闭,退出循环if (!running) {break;}// 检查是否有用户输入if (stdIn.ready()) {String userInput = stdIn.readLine();if (userInput != null) {// 向服务器发送消息userInput += "\n";ByteBuffer buffer = ByteBuffer.wrap(userInput.getBytes(StandardCharsets.UTF_8));socketChannel.write(buffer);// 如果用户输入bye,准备退出if ("bye".equals(userInput.trim())) {running = false;}}}}} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (socketChannel != null) {socketChannel.close();}if (stdIn != null) {stdIn.close();}} catch (IOException e) {e.printStackTrace();}}}
}
3.5 NIO 与 BIO 的对比

在这里插入图片描述

第四章:高级网络编程技术

4.1 异步 IO(AIO)

AIO(Asynchronous IO)是 Java 7 引入的异步 IO 模型,它与 NIO 的主要区别在于:NIO 是基于事件驱动的,需要轮询事件;而 AIO 是基于回调的,当 IO 操作完成后,会自动调用回调函数。

AIO 的核心类:

  • AsynchronousSocketChannel:用于 TCP 客户端的异步通道
  • AsynchronousServerSocketChannel:用于 TCP 服务器端的异步通道
  • AsynchronousDatagramChannel:用于 UDP 的异步通道
  • CompletionHandler:用于处理 IO 操作完成后的回调

AIO服务器示例:

import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Future;public class AIOServer {public static void main(String[] args) {try {// 创建AsynchronousServerSocketChannelAsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();// 绑定到8888端口serverSocketChannel.bind(new InetSocketAddress(8888));System.out.println("AIO服务器已启动,监听端口8888...");// 接受客户端连接serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {@Overridepublic void completed(AsynchronousSocketChannel socketChannel, Object attachment) {try {System.out.println("客户端已连接:" + socketChannel.getRemoteAddress());// 继续接受其他客户端连接serverSocketChannel.accept(null, this);// 向客户端发送欢迎消息String welcomeMsg = "欢迎连接到AIO服务器!\n";ByteBuffer buffer = ByteBuffer.wrap(welcomeMsg.getBytes(StandardCharsets.UTF_8));Future<Integer> writeFuture = socketChannel.write(buffer);writeFuture.get(); // 等待写入完成// 准备读取客户端数据ByteBuffer readBuffer = ByteBuffer.allocate(1024);readBuffer.clear();// 读取客户端数据socketChannel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer bytesRead, ByteBuffer buffer) {try {if (bytesRead == -1) {// 客户端关闭连接System.out.println("客户端已断开连接:" + socketChannel.getRemoteAddress());socketChannel.close();return;}// 解析数据buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data, StandardCharsets.UTF_8);System.out.println("收到来自 " + socketChannel.getRemoteAddress() + " 的消息:" + message);// 如果客户端发送"bye",则关闭连接if ("bye\n".equals(message) || "bye\r\n".equals(message)) {String byeMsg = "再见!\n";ByteBuffer byeBuffer = ByteBuffer.wrap(byeMsg.getBytes(StandardCharsets.UTF_8));socketChannel.write(byeBuffer).get();System.out.println("关闭与 " + socketChannel.getRemoteAddress() + " 的连接");socketChannel.close();return;}// 发送响应String response = "服务器已收到:" + message;ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));socketChannel.write(responseBuffer).get();// 继续读取客户端数据buffer.clear();socketChannel.read(buffer, buffer, this);} catch (Exception e) {e.printStackTrace();try {socketChannel.close();} catch (Exception ex) {ex.printStackTrace();}}}@Overridepublic void failed(Throwable exc, ByteBuffer buffer) {exc.printStackTrace();try {socketChannel.close();} catch (Exception e) {e.printStackTrace();}}});} catch (Exception e) {e.printStackTrace();}}@Overridepublic void failed(Throwable exc, Object attachment) {exc.printStackTrace();}});// 保持服务器运行Thread.sleep(Integer.MAX_VALUE);} catch (Exception e) {e.printStackTrace();}}
}

AIO客户端示例:

import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Future;
import java.io.*;public class AIOClient {public static void main(String[] args) {try {// 创建AsynchronousSocketChannelAsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();// 连接服务器Future<Void> connectFuture = socketChannel.connect(new InetSocketAddress("localhost", 8888));connectFuture.get(); // 等待连接完成System.out.println("已连接到服务器");// 读取服务器响应ByteBuffer readBuffer = ByteBuffer.allocate(1024);Future<Integer> readFuture = socketChannel.read(readBuffer);int bytesRead = readFuture.get();if (bytesRead > 0) {readBuffer.flip();byte[] data = new byte[readBuffer.remaining()];readBuffer.get(data);System.out.println("服务器响应:" + new String(data, StandardCharsets.UTF_8));}// 读取用户输入BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));// 启动一个线程读取服务器响应new Thread(() -> {try {ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) {buffer.clear();Future<Integer> future = socketChannel.read(buffer);int bytes = future.get();if (bytes == -1) {System.out.println("服务器已关闭连接");System.exit(0);}buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("服务器响应:" + new String(data, StandardCharsets.UTF_8));// 如果收到服务器的再见消息,退出if (new String(data, StandardCharsets.UTF_8).contains("再见")) {System.exit(0);}System.out.println("请输入消息(输入bye退出):");}} catch (Exception e) {e.printStackTrace();}}).start();// 发送用户输入到服务器System.out.println("请输入消息(输入bye退出):");String userInput;while ((userInput = stdIn.readLine()) != null) {userInput += "\n";ByteBuffer buffer = ByteBuffer.wrap(userInput.getBytes(StandardCharsets.UTF_8));socketChannel.write(buffer).get();if ("bye".equals(userInput.trim())) {socketChannel.close();break;}}} catch (Exception e) {e.printStackTrace();}}
}
4.2 网络编程中的设计模式

在网络编程中,常用的设计模式有:

4.2.1 工厂模式(Factory Pattern)

工厂模式用于创建对象,隐藏对象的创建细节。在网络编程中,可以使用工厂模式创建不同类型的连接或协议处理器。

工厂模式示例:

import java.net.Socket;// 连接接口
interface Connection {void send(String data) throws Exception;String receive() throws Exception;void close() throws Exception;
}// TCP连接实现
class TcpConnection implements Connection {private Socket socket;// 其他成员变量和构造方法...@Overridepublic void send(String data) throws Exception {// 实现TCP发送逻辑}@Overridepublic String receive() throws Exception {// 实现TCP接收逻辑return null;}@Overridepublic void close() throws Exception {// 实现关闭逻辑}
}// UDP连接实现
class UdpConnection implements Connection {// 实现类似TcpConnection的方法...@Overridepublic void send(String data) throws Exception {// 实现UDP发送逻辑}@Overridepublic String receive() throws Exception {// 实现UDP接收逻辑return null;}@Overridepublic void close() throws Exception {// 实现关闭逻辑}
}// 连接工厂
class ConnectionFactory {public static Connection createConnection(String type, String host, int port) throws Exception {if ("tcp".equalsIgnoreCase(type)) {return new TcpConnection(); // 实际中需要传入host和port} else if ("udp".equalsIgnoreCase(type)) {return new UdpConnection(); // 实际中需要传入host和port} else {throw new IllegalArgumentException("不支持的连接类型: " + type);}}
}// 使用工厂模式
public class FactoryPatternExample {public static void main(String[] args) {try {Connection tcpConnection = ConnectionFactory.createConnection("tcp", "localhost", 8888);Connection udpConnection = ConnectionFactory.createConnection("udp", "localhost", 8888);// 使用连接...tcpConnection.send("Hello TCP");udpConnection.send("Hello UDP");// 关闭连接...tcpConnection.close();udpConnection.close();} catch (Exception e) {e.printStackTrace();}}
}
4.2.2 观察者模式(Observer Pattern)

观察者模式用于对象间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会收到通知并自动更新。在网络编程中,可以使用观察者模式处理网络事件。

4.2.3 装饰器模式(Decorator Pattern)

装饰器模式用于动态地给对象添加额外的职责。在网络编程中,可以使用装饰器模式为网络连接添加日志、压缩、加密等功能。

装饰器模式示例:

// 连接接口(与工厂模式中的相同)
interface Connection {void send(String data) throws Exception;String receive() throws Exception;void close() throws Exception;
}// 基础连接实现(如TCP连接)
class BasicConnection implements Connection {// 实现方法...@Overridepublic void send(String data) throws Exception {System.out.println("发送数据: " + data);}@Overridepublic String receive() throws Exception {return "收到的数据";}@Overridepublic void close() throws Exception {System.out.println("关闭连接");}
}// 装饰器抽象类
abstract class ConnectionDecorator implements Connection {protected Connection connection;public ConnectionDecorator(Connection connection) {this.connection = connection;}@Overridepublic void send(String data) throws Exception {connection.send(data);}@Overridepublic String receive() throws Exception {return connection.receive();}@Overridepublic void close() throws Exception {connection.close();}
}// 日志装饰器
class LoggingConnectionDecorator extends ConnectionDecorator {public LoggingConnectionDecorator(Connection connection) {super(connection);}@Overridepublic void send(String data) throws Exception {System.out.println("日志: 准备发送数据");super.send(data);System.out.println("日志: 数据发送完成");}@Overridepublic String receive() throws Exception {System.out.println("日志: 准备接收数据");String data = super.receive();System.out.println("日志: 数据接收完成");return data;}
}// 加密装饰器
class EncryptingConnectionDecorator extends ConnectionDecorator {public EncryptingConnectionDecorator(Connection connection) {super(connection);}@Overridepublic void send(String data) throws Exception {// 加密数据String encryptedData = encrypt(data);super.send(encryptedData);}@Overridepublic String receive() throws Exception {String encryptedData = super.receive();// 解密数据return decrypt(encryptedData);}private String encrypt(String data) {// 简单的加密实现return new StringBuilder(data).reverse().toString();}private String decrypt(String data) {// 简单的解密实现return new StringBuilder(data).reverse().toString();}
}// 使用装饰器模式
public class DecoratorPatternExample {public static void main(String[] args) {try {// 创建基础连接Connection connection = new BasicConnection();// 添加日志功能Connection loggingConnection = new LoggingConnectionDecorator(connection);// 添加加密功能Connection encryptedConnection = new EncryptingConnectionDecorator(loggingConnection);// 使用装饰后的连接encryptedConnection.send("Hello World");String data = encryptedConnection.receive();System.out.println("解密后的数据: " + data);encryptedConnection.close();} catch (Exception e) {e.printStackTrace();}}
}
4.3 网络安全

网络安全是网络编程中非常重要的方面,常见的网络安全技术包括:

4.3.1 加密与解密

加密是保护数据安全的重要手段,Java 提供了丰富的加密 API,位于 javax.crypto 包中。

常见的加密算法:

  • 对称加密:DES、AES 等,加密和解密使用相同的密钥
  • 非对称加密:RSA 等,加密和解密使用不同的密钥(公钥和私钥)
  • 哈希算法:MD5、SHA 等,用于生成数据的哈希值,不能解密
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;public class EncryptionExample {// 加密算法private static final String ALGORITHM = "AES";// 密钥长度private static final int KEY_SIZE = 128;public static void main(String[] args) {try {// 生成密钥SecretKey secretKey = generateKey();System.out.println("生成的密钥: " + Base64.getEncoder().encodeToString(secretKey.getEncoded()));// 要加密的数据String data = "Hello, 这是一段需要加密的数据!";System.out.println("原始数据: " + data);// 加密数据String encryptedData = encrypt(data, secretKey);System.out.println("加密后的数据: " + encryptedData);// 解密数据String decryptedData = decrypt(encryptedData, secretKey);System.out.println("解密后的数据: " + decryptedData);} catch (Exception e) {e.printStackTrace();}}// 生成密钥public static SecretKey generateKey() throws Exception {KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);keyGenerator.init(KEY_SIZE);return keyGenerator.generateKey();}// 加密数据public static String encrypt(String data, SecretKey secretKey) throws Exception {Cipher cipher = Cipher.getInstance(ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] encryptedBytes = cipher.doFinal(data.getBytes("UTF-8"));return Base64.getEncoder().encodeToString(encryptedBytes);}// 解密数据public static String decrypt(String encryptedData, SecretKey secretKey) throws Exception {Cipher cipher = Cipher.getInstance(ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));return new String(decryptedBytes, "UTF-8");}// 根据密钥字节数组获取SecretKeypublic static SecretKey getSecretKey(byte[] keyBytes) {return new SecretKeySpec(keyBytes, ALGORITHM);}
}
4.3.2 SSL/TLS

SSL(Secure Sockets Layer)和 TLS(Transport Layer Security)是用于在网络上提供安全通信的协议。Java 提供了 JSSE(Java Secure Socket Extension)来支持 SSL/TLS。

使用 SSL/TLS 的步骤:

创建 SSLContext 对象
创建 SSLSocketFactory 或 SSLServerSocketFactory
使用工厂创建 SSLSocket 或 SSLServerSocket
使用 SSLSocket 或 SSLServerSocket 进行通信

SSL/TLS示例:

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;public class SSLServer {private static final int PORT = 8888;private static final String KEY_STORE = "server.keystore";private static final String KEY_STORE_PASSWORD = "password";public static void main(String[] args) {SSLServerSocket serverSocket = null;try {// 加载密钥库KeyStore keyStore = KeyStore.getInstance("JKS");keyStore.load(new FileInputStream(KEY_STORE), KEY_STORE_PASSWORD.toCharArray());// 创建密钥管理器工厂KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf.init(keyStore, KEY_STORE_PASSWORD.toCharArray());// 创建SSL上下文SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(kmf.getKeyManagers(), null, null);// 创建SSL服务器套接字工厂SSLServerSocketFactory factory = sslContext.getServerSocketFactory();// 创建SSL服务器套接字serverSocket = (SSLServerSocket) factory.createServerSocket(PORT);serverSocket.setNeedClientAuth(false); // 不需要客户端认证System.out.println("SSL服务器已启动,监听端口" + PORT + "...");while (true) {// 接受客户端连接SSLSocket socket = (SSLSocket) serverSocket.accept();System.out.println("客户端已连接:" + socket.getInetAddress().getHostAddress());// 处理客户端请求new Thread(new SSLClientHandler(socket)).start();}} catch (Exception e) {e.printStackTrace();} finally {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}static class SSLClientHandler implements Runnable {private SSLSocket socket;public SSLClientHandler(SSLSocket socket) {this.socket = socket;}@Overridepublic void run() {BufferedReader in = null;PrintWriter out = null;try {// 获取输入流和输出流in = new BufferedReader(new InputStreamReader(socket.getInputStream()));out = new PrintWriter(socket.getOutputStream(), true);String inputLine;while ((inputLine = in.readLine()) != null) {System.out.println("收到客户端消息:" + inputLine);out.println("服务器已收到:" + inputLine);if ("bye".equals(inputLine)) {break;}}System.out.println("客户端已断开连接");} catch (IOException e) {e.printStackTrace();} finally {try {if (in != null) in.close();if (out != null) out.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}

SSL客户端示例:

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;public class SSLClient {private static final String HOST = "localhost";private static final int PORT = 8888;private static final String TRUST_STORE = "client.truststore";private static final String TRUST_STORE_PASSWORD = "password";public static void main(String[] args) {// 设置信任库System.setProperty("javax.net.ssl.trustStore", TRUST_STORE);System.setProperty("javax.net.ssl.trustStorePassword", TRUST_STORE_PASSWORD);SSLSocket socket = null;BufferedReader in = null;PrintWriter out = null;BufferedReader stdIn = null;try {// 获取SSL套接字工厂SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();// 创建SSL套接字并连接到服务器socket = (SSLSocket) factory.createSocket(HOST, PORT);System.out.println("已连接到SSL服务器");// 获取输入流和输出流in = new BufferedReader(new InputStreamReader(socket.getInputStream()));out = new PrintWriter(socket.getOutputStream(), true);stdIn = new BufferedReader(new InputStreamReader(System.in));String userInput;while ((userInput = stdIn.readLine()) != null) {out.println(userInput);String response = in.readLine();System.out.println("服务器响应:" + response);if ("bye".equals(userInput)) {break;}}} catch (Exception e) {e.printStackTrace();} finally {try {if (in != null) in.close();if (out != null) out.close();if (stdIn != null) stdIn.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}
}

第五章:网络编程框架

5.1 Netty

Netty 是一个高性能、异步事件驱动的 NIO 框架,用于开发可维护的高性能协议服务器和客户端。

Netty 的主要特点:

  • 高性能:基于 NIO,采用多路复用和异步 IO
  • 易于使用:提供了简单易用的 API,隐藏了 NIO 的复杂性
  • 灵活性:支持多种协议和编码解码方式
  • 可靠性:处理了各种边缘情况,保证了网络通信的可靠性
  • 可扩展性:采用模块化设计,易于扩展

Netty 的核心组件:

  • Channel:表示一个网络连接
  • EventLoop:处理 Channel 的 IO 事件
  • ChannelHandler:处理 Channel 的 IO 事件和数据
  • ChannelPipeline:ChannelHandler 的集合,形成一个处理链
  • ByteBuf:Netty 的缓冲区,比 Java NIO 的 ByteBuffer 更强大

Netty服务器示例:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;public class NettyServer {private final int port;public NettyServer(int port) {this.port = port;}public void start() throws Exception {// 创建两个EventLoopGroup:bossGroup用于接受连接,workerGroup用于处理连接EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {// 创建ServerBootstrapServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用NIO的服务器通道.childHandler(new ChannelInitializer<SocketChannel>() { // 设置通道初始化器@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 获取ChannelPipelineChannelPipeline pipeline = ch.pipeline();// 添加编码器和解码器pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());// 添加自定义的处理器pipeline.addLast(new ServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128) // 设置TCP参数.childOption(ChannelOption.SO_KEEPALIVE, true); // 设置TCP参数// 绑定端口并启动服务器ChannelFuture future = bootstrap.bind(port).sync();System.out.println("Netty服务器已启动,监听端口" + port + "...");// 等待服务器关闭future.channel().closeFuture().sync();} finally {// 优雅地关闭EventLoopGroupworkerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}// 自定义的ChannelHandlerpublic static class ServerHandler extends SimpleChannelInboundHandler<String> {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("客户端已连接:" + ctx.channel().remoteAddress());// 发送欢迎消息ctx.writeAndFlush("欢迎连接到Netty服务器!\n");}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println("收到来自 " + ctx.channel().remoteAddress() + " 的消息:" + msg);// 如果客户端发送"bye",则关闭连接if ("bye".equals(msg)) {ctx.writeAndFlush("再见!\n");ctx.close();return;}// 发送响应ctx.writeAndFlush("服务器已收到:" + msg + "\n");}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println("客户端已断开连接:" + ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}public static void main(String[] args) throws Exception {int port = 8888;new NettyServer(port).start();}
}

Netty客户端示例:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;import java.io.BufferedReader;
import java.io.InputStreamReader;public class NettyClient {private final String host;private final int port;public NettyClient(String host, int port) {this.host = host;this.port = port;}public void start() throws Exception {// 创建EventLoopGroupEventLoopGroup group = new NioEventLoopGroup();try {// 创建BootstrapBootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class) // 使用NIO的客户端通道.option(ChannelOption.TCP_NODELAY, true) // 设置TCP参数.handler(new ChannelInitializer<SocketChannel>() { // 设置通道初始化器@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 获取ChannelPipelineChannelPipeline pipeline = ch.pipeline();// 添加编码器和解码器pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());// 添加自定义的处理器pipeline.addLast(new ClientHandler());}});// 连接服务器ChannelFuture future = bootstrap.connect(host, port).sync();Channel channel = future.channel();// 读取用户输入并发送到服务器BufferedReader in = new BufferedReader(new InputStreamReader(System.in));System.out.println("请输入消息(输入bye退出):");while (true) {String line = in.readLine();if (line == null) {continue;}// 发送消息channel.writeAndFlush(line + "\n");// 如果输入bye,等待服务器响应后退出if ("bye".equals(line)) {channel.closeFuture().sync();break;}}} finally {// 优雅地关闭EventLoopGroupgroup.shutdownGracefully();}}// 自定义的ChannelHandlerpublic static class ClientHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println("服务器响应:" + msg);// 如果收到服务器的再见消息,提示用户if (msg.contains("再见")) {System.out.println("连接将关闭");} else {System.out.println("请输入消息(输入bye退出):");}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}public static void main(String[] args) throws Exception {new NettyClient("localhost", 8888).start();}
}
5.2 Apache MINA

Apache MINA 是另一个流行的 NIO 框架,与 Netty 类似,提供了高性能的网络编程能力。

MINA 的主要特点:

  • 基于 NIO,支持阻塞和非阻塞 IO
  • 提供了统一的 API,隐藏了底层 IO 细节
  • 支持多种传输协议(TCP、UDP、串口等)
  • 内置了多种编码器和解码器
  • 支持过滤器链,易于扩展

第六章:网络编程实践与案例

6.1 文件传输

文件传输是网络编程中的常见需求,可以使用 TCP 或 UDP 实现。由于 TCP 提供可靠传输,通常用于文件传输。

文件传输示例:

import java.io.*;
import java.net.*;public class FileServer {public static void main(String[] args) {ServerSocket serverSocket = null;try {// 创建ServerSocket,绑定到8888端口serverSocket = new ServerSocket(8888);System.out.println("文件服务器已启动,等待客户端连接...");while (true) {// 接受客户端连接Socket socket = serverSocket.accept();System.out.println("客户端已连接:" + socket.getInetAddress().getHostAddress());// 处理文件传输new Thread(new FileHandler(socket)).start();}} catch (IOException e) {e.printStackTrace();} finally {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}static class FileHandler implements Runnable {private Socket socket;public FileHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {DataInputStream dis = null;FileOutputStream fos = null;try {// 获取输入流dis = new DataInputStream(socket.getInputStream());// 读取文件名和长度String fileName = dis.readUTF();long fileLength = dis.readLong();System.out.println("准备接收文件:" + fileName + ",大小:" + fileLength + "字节");// 创建文件输出流File file = new File("received_" + fileName);fos = new FileOutputStream(file);// 接收文件内容byte[] buffer = new byte[4096];int bytesRead;long totalRead = 0;while (totalRead < fileLength && (bytesRead = dis.read(buffer)) != -1) {fos.write(buffer, 0, bytesRead);totalRead += bytesRead;// 打印进度double progress = (double) totalRead / fileLength * 100;System.out.printf("接收进度:%.2f%%\r", progress);}System.out.println("\n文件接收完成:" + file.getAbsolutePath());} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (dis != null) dis.close();if (fos != null) fos.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}

文件客户端示例:

import java.io.*;
import java.net.*;public class FileClient {public static void main(String[] args) {String serverHost = "localhost";int serverPort = 8888;String filePath = "example.txt"; // 要发送的文件路径Socket socket = null;DataOutputStream dos = null;FileInputStream fis = null;try {// 创建Socket,连接到服务器socket = new Socket(serverHost, serverPort);System.out.println("已连接到文件服务器");// 获取文件信息File file = new File(filePath);if (!file.exists() || !file.isFile()) {System.err.println("文件不存在或不是一个文件:" + filePath);return;}String fileName = file.getName();long fileLength = file.length();System.out.println("准备发送文件:" + fileName + ",大小:" + fileLength + "字节");// 获取输出流dos = new DataOutputStream(socket.getOutputStream());// 发送文件名和长度dos.writeUTF(fileName);dos.writeLong(fileLength);dos.flush();// 读取文件内容并发送fis = new FileInputStream(file);byte[] buffer = new byte[4096];int bytesRead;long totalSent = 0;while ((bytesRead = fis.read(buffer)) != -1) {dos.write(buffer, 0, bytesRead);totalSent += bytesRead;dos.flush();// 打印进度double progress = (double) totalSent / fileLength * 100;System.out.printf("发送进度:%.2f%%\r", progress);}System.out.println("\n文件发送完成");} catch (UnknownHostException e) {System.err.println("不知道的主机:" + serverHost);e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (dos != null) dos.close();if (fis != null) fis.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}
}
6.2 聊天程序

聊天程序是网络编程的经典案例,通常包括服务器和客户端,支持一对一或群聊功能。

聊天服务器示例:

import java.io.*;
import java.net.*;
import java.util.*;public class ChatServer {// 保存所有连接的客户端private static Set<PrintWriter> clientWriters = new HashSet<>();public static void main(String[] args) {ServerSocket serverSocket = null;try {// 创建ServerSocket,绑定到8888端口serverSocket = new ServerSocket(8888);System.out.println("聊天服务器已启动,等待客户端连接...");while (true) {// 接受客户端连接Socket socket = serverSocket.accept();System.out.println("客户端已连接:" + socket.getInetAddress().getHostAddress());// 添加客户端的输出流到集合PrintWriter out = new PrintWriter(socket.getOutputStream(), true);synchronized (clientWriters) {clientWriters.add(out);}// 处理客户端消息new Thread(new ChatHandler(socket, out)).start();}} catch (IOException e) {e.printStackTrace();} finally {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}// 广播消息给所有客户端public static void broadcast(String message) {synchronized (clientWriters) {for (PrintWriter writer : clientWriters) {writer.println(message);}}}static class ChatHandler implements Runnable {private Socket socket;private PrintWriter out;private String username;public ChatHandler(Socket socket, PrintWriter out) {this.socket = socket;this.out = out;}@Overridepublic void run() {BufferedReader in = null;try {// 获取输入流in = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 读取用户名username = in.readLine();if (username == null) {return;}// 广播用户加入消息broadcast("系统消息:" + username + " 加入了聊天室");System.out.println(username + " 加入了聊天室");// 读取用户消息并广播String message;while ((message = in.readLine()) != null) {System.out.println(username + ":" + message);broadcast(username + ":" + message);}} catch (IOException e) {e.printStackTrace();} finally {// 移除客户端的输出流synchronized (clientWriters) {clientWriters.remove(out);}// 广播用户离开消息if (username != null) {broadcast("系统消息:" + username + " 离开了聊天室");System.out.println(username + " 离开了聊天室");}// 关闭资源try {if (in != null) in.close();if (out != null) out.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}

聊天客户端示例:

import java.io.*;
import java.net.*;public class ChatClient {public static void main(String[] args) {String serverHost = "localhost";int serverPort = 8888;Socket socket = null;PrintWriter out = null;BufferedReader in = null;BufferedReader stdIn = null;try {// 创建Socket,连接到服务器socket = new Socket(serverHost, serverPort);System.out.println("已连接到聊天服务器");// 获取输入输出流out = new PrintWriter(socket.getOutputStream(), true);in = new BufferedReader(new InputStreamReader(socket.getInputStream()));stdIn = new BufferedReader(new InputStreamReader(System.in));// 输入用户名System.out.print("请输入用户名:");String username = stdIn.readLine();out.println(username);// 启动线程读取服务器消息new Thread(new ServerMessageReader(in)).start();// 读取用户输入并发送到服务器System.out.println("请输入消息(输入exit退出):");String userInput;while ((userInput = stdIn.readLine()) != null) {if ("exit".equals(userInput)) {break;}out.println(userInput);}} catch (UnknownHostException e) {System.err.println("不知道的主机:" + serverHost);e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {if (out != null) out.close();if (in != null) in.close();if (stdIn != null) stdIn.close();if (socket != null) socket.close();} catch (IOException e) {e.printStackTrace();}}}// 读取服务器消息的线程static class ServerMessageReader implements Runnable {private BufferedReader in;public ServerMessageReader(BufferedReader in) {this.in = in;}@Overridepublic void run() {try {String message;while ((message = in.readLine()) != null) {System.out.println(message);System.out.print("请输入消息(输入exit退出):");}} catch (IOException e) {// 服务器断开连接时退出System.out.println("\n与服务器的连接已断开");System.exit(0);}}}
}

第七章:网络编程性能优化

7.1 性能瓶颈分析

网络编程中的性能瓶颈可能来自以下几个方面:

  • 网络带宽:网络带宽限制了数据传输的速度
  • IO 操作:磁盘 IO 或网络 IO 可能成为瓶颈
  • CPU:数据处理、加密解密等操作可能消耗大量 CPU 资源
  • 内存:内存不足或内存泄漏可能导致性能下降
  • 线程管理:线程创建和切换的开销可能影响性能
7.2 性能优化策略
7.2.1 减少网络传输量
  • 数据压缩:传输前压缩数据,减少数据量
  • 数据序列化:使用高效的序列化方式(如 Protocol Buffers、Kryo)
  • 批量传输:将多个小数据合并为一个大数据包传输
  • 增量传输:只传输变化的数据,而不是整个数据集
7.2.2 优化 IO 操作
  • 使用 NIO 或 AIO:相比 BIO,NIO 和 AIO 可以处理更多的并发连接
  • 缓冲区优化:使用合适大小的缓冲区,减少 IO 操作次数
  • 异步 IO:使用异步 IO 避免线程阻塞
  • 直接缓冲区:对于频繁使用的缓冲区,使用直接缓冲区减少内存复制
7.2.3 线程管理优化
  • 线程池:使用线程池管理线程,避免频繁创建和销毁线程
  • 合理设置线程池参数:根据 CPU 核心数和任务类型设置合适的线程数
  • 避免线程阻塞:减少线程在 IO 操作上的阻塞时间
  • 使用协程:在支持协程的环境中,使用协程替代线程,减少上下文切换开销
7.2.4 协议优化
  • 使用高效协议:选择合适的协议(如 UDP 用于实时性要求高的场景,TCP 用于可靠性要求高的场景)
  • 协议简化:自定义简洁的协议,减少协议开销
  • 连接复用:使用长连接复用连接,减少连接建立和关闭的开销
  • HTTP/2 或 HTTP/3:对于 HTTP 通信,使用 HTTP/2 或 HTTP/3,支持多路复用和二进制传输
7.2.5 硬件和网络优化
  • 使用高性能网络设备:如千兆网卡、负载均衡器等
  • CDN 加速:对于静态资源,使用 CDN 加速
  • 就近部署:将服务部署在离用户近的地方,减少网络延迟
  • 增加带宽:在必要时增加网络带宽
7.3 性能测试与监控
  • 性能测试工具:使用 JMeter、LoadRunner 等工具进行性能测试
  • 监控工具:使用 JConsole、VisualVM 等工具监控 JVM 性能
  • 日志分析:记录和分析性能相关的日志
  • 指标监控:监控吞吐量、响应时间、错误率等指标
  • 持续优化:根据测试和监控结果,持续优化系统性能

第八章:网络编程常见问题与解决方案

8.1 连接超时
问题:客户端连接服务器时超时,无法建立连接。

可能原因:

服务器未启动或端口未开放
网络不通或防火墙阻止连接
服务器负载过高,无法处理新连接
连接超时时间设置过短

解决方案:

检查服务器是否启动,端口是否正确
检查网络连接和防火墙设置
增加服务器处理能力或优化服务器
适当增加连接超时时间

8.2 数据传输不完整

问题:发送的数据与接收的数据不一致,或数据不完整。

可能原因:

使用 UDP 协议,数据报丢失
缓冲区大小设置不合理
未正确处理 TCP 的粘包问题
编码解码方式不一致

解决方案:

对于可靠传输,使用 TCP 协议
设置合适的缓冲区大小
实现数据分包和粘包处理机制
确保发送端和接收端使用相同的编码解码方式

8.3 内存泄漏

问题:程序运行一段时间后,内存占用不断增加,最终导致内存溢出。

可能原因:

未正确关闭 Socket、流等资源
连接管理不当,导致大量无效连接
缓存未设置过期时间,数据不断累积
线程未正确终止,导致资源无法释放

解决方案:

使用 try-with-resources 确保资源正确关闭
实现连接超时和心跳机制,及时关闭无效连接
为缓存设置合理的过期时间和大小限制
确保线程能够正确终止,避免线程泄漏

8.4 并发问题

问题:多线程环境下,出现数据不一致、死锁等问题。

可能原因:

共享资源未正确同步
锁使用不当,导致死锁或活锁
线程池参数设置不合理
异步操作处理不当

解决方案:

对共享资源进行正确的同步处理
合理设计锁的粒度和顺序,避免死锁
根据实际情况调整线程池参数
正确处理异步操作的回调和结果

8.5 安全问题

问题:网络通信存在安全隐患,如数据泄露、中间人攻击等。

可能原因:

数据未加密传输
未验证服务器或客户端身份
使用不安全的协议或算法
输入未经过滤,存在注入攻击风险

解决方案:

使用 SSL/TLS 加密传输数据
实现身份验证机制
使用安全的协议和加密算法
对输入进行严格验证和过滤

结语

Java 网络编程是 Java 开发中的重要组成部分,从基础的 Socket 编程到高级的 NIO 框架,从简单的文件传输到复杂的分布式系统,Java 提供了丰富的 API 和框架支持。

本指南全面介绍了 Java 网络编程的基础知识、核心技术和高级应用,包括:

  • 计算机网络基础和核心协议
  • Java BIO、NIO 和 AIO 编程
  • 网络安全和加密技术
  • 主流网络编程框架
  • 实际应用案例和性能优化

掌握 Java 网络编程不仅能够帮助我们开发各种网络应用,还能让我们更好地理解分布式系统的工作原理。在实际开发中,我们需要根据具体需求选择合适的技术和框架,同时关注性能、安全和可靠性等方面。

随着互联网技术的不断发展,网络编程也在不断演进,新的协议、框架和技术不断涌现。作为开发者,我们需要不断学习和实践,跟上技术发展的步伐,编写出更高质量的网络应用。


文章转载自:

http://xm5QMXmn.kwdfn.cn
http://EJg3Isqh.kwdfn.cn
http://gk29YqE5.kwdfn.cn
http://aP56U25E.kwdfn.cn
http://1TWwJNtY.kwdfn.cn
http://vt597dpo.kwdfn.cn
http://ggXQX19M.kwdfn.cn
http://aoBvVZVX.kwdfn.cn
http://K2pVEri3.kwdfn.cn
http://kkjaPofH.kwdfn.cn
http://NCiPC0Zt.kwdfn.cn
http://MJvjL2jg.kwdfn.cn
http://qsNkXQ4t.kwdfn.cn
http://P3cl1ckV.kwdfn.cn
http://866NrJU6.kwdfn.cn
http://X4zKbiNN.kwdfn.cn
http://KIIo57iF.kwdfn.cn
http://GYHweeua.kwdfn.cn
http://8iLZTQiZ.kwdfn.cn
http://FGQ8SRWq.kwdfn.cn
http://6f1eDXrz.kwdfn.cn
http://jMu5rkir.kwdfn.cn
http://rAArfpzr.kwdfn.cn
http://ekno8I3V.kwdfn.cn
http://JzC2xgz3.kwdfn.cn
http://qaGts3nj.kwdfn.cn
http://5pbBk5w8.kwdfn.cn
http://Ts259vuP.kwdfn.cn
http://qNWvgz9v.kwdfn.cn
http://uvBoYaTJ.kwdfn.cn
http://www.dtcms.com/a/384787.html

相关文章:

  • GD32VW553-IOT V2开发版【三分钟快速环境搭建教程 VSCode】
  • Docker 与 VSCode 远程容器连接问题深度排查与解决指南
  • 流程图用什么工具做?免费/付费工具对比,附在线制作与下载教程
  • IT运维管理与服务优化
  • javaweb XML DOM4J
  • 用C#生成带特定字节的数据序列(地址从0x0001A000到0x0001C000,步长0x20)
  • 解析预训练:BERT到Qwen的技术演进与应用实践
  • PCB 温度可靠性验证:从行业标准到实测数据
  • 机器人要增加力矩要有那些条件和增加什么
  • MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
  • 6U VPX 板卡设计原理图:616-基于6U VPX XCVU9P+XCZU7EV的双FMC信号处理板卡
  • 【芯片设计-信号完整性 SI 学习 1.2.2 -- 时序裕量(Margin)】
  • Elasticsearch核心概念与Java实战:从入门到精通
  • Flink 内部状态管理:PriorityQueueSet解析
  • ChatBot、Copilot、Agent啥区别
  • LeetCode 热题560.和为k的子数组 (前缀和)
  • 掌握多边形细分建模核心技术:从基础操作到实战技巧详解
  • [特殊字符] Python在CentOS系统执行深度指南
  • 机器人控制器开发(定位——cartographer ros2 使用1)
  • 7 制作自己的遥感机器学习数据集
  • FPGA 40 DAC线缆和光模块带光纤实现40G UDP差异
  • 强化学习【value iterration】【python]
  • 代码随想录算法训练营第四十天|01背包 二维 01背包 一维 416.分割等和子集
  • 力扣:1547. 切棍子的最小成本
  • LeetCode 2962.统计最大元素出现至少K次的子数组
  • ESP8266无法连接Jio路由器分析
  • 傅里叶变换与现代深度学习
  • 【LeetCode】2785. 将字符串中的元音字母排序
  • APIPark:重新定义AI时代的API网关 —— 从100+模型统一接入到企业级应用
  • TENGJUN防水TYPE-C 16PIN连接器技术解析:从结构设计到认证标准的全面解读