开启Java网络编程之旅
立即解锁
发布时间: 2025-08-19 00:15:55 阅读量: 1 订阅数: 5 

### 开启 Java 网络编程之旅
#### 1. 网络编程基础
在当今互联网时代,应用程序的网络连接能力愈发重要。Java 为网络编程提供了丰富的支持,下面我们来深入了解 Java 网络编程的基础内容。
##### 1.1 网络寻址
网络寻址可以使用 `InetAddress` 类,它能帮助我们处理 IP 地址相关的操作。例如,我们可以通过以下代码获取本地主机的 `InetAddress` 对象:
```java
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressExample {
public static void main(String[] args) {
try {
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本地主机名: " + localHost.getHostName());
System.out.println("本地主机 IP 地址: " + localHost.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
```
这段代码中,`InetAddress.getLocalHost()` 方法用于获取本地主机的 `InetAddress` 对象,然后通过 `getHostName()` 和 `getHostAddress()` 方法分别获取主机名和 IP 地址。
##### 1.2 NIO 支持
Java 的 NIO(New I/O)包为网络编程提供了高效的缓冲区和通道支持。通过 `URLConnection` 类可以与网络资源建立连接,以下是使用 `URLConnection` 类的示例:
```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionExample {
public static void main(String[] args) {
try {
URL url = new URL("https://www.example.com");
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
上述代码中,首先创建一个 `URL` 对象,然后通过 `openConnection()` 方法打开连接,接着使用 `BufferedReader` 读取连接的输入流。
我们还可以结合缓冲区和通道来使用 `URLConnection` 类,以提高数据传输效率。
##### 1.3 客户端/服务器架构
客户端/服务器架构是网络编程中常见的架构模式。下面我们来创建一个简单的回声服务器和客户端。
**创建简单回声服务器**
```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleEchoServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
System.out.println("服务器已启动,等待客户端连接...");
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接");
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,首先创建一个 `ServerSocket` 对象并指定端口号 12345,然后使用 `accept()` 方法等待客户端连接。当客户端连接后,通过 `BufferedReader` 读取客户端发送的消息,并使用 `PrintWriter` 将消息原样返回给客户端。
**创建简单回声客户端**
```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class SimpleEchoClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) {
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("服务器响应: " + in.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,创建一个 `Socket` 对象并连接到服务器的指定地址和端口。然后通过 `PrintWriter` 向服务器发送消息,并使用 `BufferedReader` 读取服务器的响应。
我们还可以使用 Java 8 的特性来支持回声服务器和客户端,例如使用 Lambda 表达式和流 API 来简化代码。
#### 2. UDP 和多播
UDP(User Datagram Protocol)是一种无连接的传输协议,它提供了一种不太可靠但更高效的通信方式。多播则允许将消息同时发送给多个接收者。
##### 2.1 创建多播服务器
```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastServer {
public static void main(String[] args) {
try (MulticastSocket socket = new MulticastSocket(12345)) {
InetAddress group = InetAddress.getByName("230.0.0.1");
socket.joinGroup(group);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("接收到的消息: " + message);
socket.leaveGroup(group);
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,首先创建一个 `MulticastSocket` 对象并绑定到指定端口,然后加入多播组。接着创建一个 `DatagramPacket` 对象用于接收消息,最后使用 `receive()` 方法接收消息。
##### 2.2 创建多播客户端
```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class MulticastClient {
public static void main(String[] args) {
try (MulticastSocket socket = new MulticastSocket()) {
InetAddress group = InetAddress.getByName("230.0.0.1");
String message = "Hello, multicast!";
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, 12345);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,创建一个 `MulticastSocket` 对象,然后将消息封装成 `DatagramPacket` 对象并发送到指定的多播组和端口。
以下是 UDP 多播通信的流程图:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(创建 MulticastSocket):::process
B --> C{客户端还是服务器?}:::decision
C -->|客户端| D(加入多播组):::process
D --> E(发送消息):::process
E --> F(离开多播组):::process
C -->|服务器| G(创建 DatagramPacket):::process
G --> H(接收消息):::process
H --> I(离开多播组):::process
F --> J([结束]):::startend
I --> J
```
#### 3. 网络可扩展性
随着服务器负载的增加,系统需要具备可扩展性来应对更多的请求。多线程是实现服务器可扩展性的一种常用方法。
##### 3.1 创建线程服务器
```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ThreadedServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
System.out.println("线程服务器已启动,等待客户端连接...");
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
```
在这个代码中,`ThreadedServer` 类创建一个 `ServerSocket` 对象并监听指定端口。当有客户端连接时,创建一个新的线程来处理该客户端的请求。`ClientHandler` 类实现了 `Runnable` 接口,用于处理客户端的具体请求。
##### 3.2 使用线程服务器
客户端代码与之前的简单回声客户端代码类似,只需要连接到线程服务器的地址和端口即可。
以下是线程服务器处理客户端请求的流程图:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A([开始]):::startend --> B(创建 ServerSocket):::process
B --> C(等待客户端连接):::process
C --> D(接受客户端连接):::process
D --> E(创建新线程处理请求):::process
E --> F(处理客户端请求):::process
F --> G(关闭客户端连接):::process
G --> C
```
#### 4. 网络安全
网络安全是网络编程中至关重要的一部分。Java 提供了多种安全机制来保护网络通信,例如 SSL(Secure Sockets Layer)和 TLS(Transport Layer Security)。
##### 4.1 创建 SSL 服务器
```java
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class SSLServer {
public static void main(String[] args) {
try {
SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(12345);
System.out.println("SSL 服务器已启动,等待客户端连接...");
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,首先获取 `SSLServerSocketFactory` 对象,然后使用它创建 `SSLServerSocket` 对象并监听指定端口。当有客户端连接时,接受连接并处理客户端的请求。
##### 4.2 创建 SSL 客户端
```java
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class SSLClient {
public static void main(String[] args) {
try {
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket("localhost", 12345);
PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("服务器响应: " + in.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,获取 `SSLSocketFactory` 对象,然后使用它创建 `SSLSocket` 对象并连接到服务器。接着通过 `PrintWriter` 向服务器发送消息,并使用 `BufferedReader` 读取服务器的响应。
##### 4.3 生成安全密钥
生成安全密钥可以使用 `keytool` 工具,以下是生成密钥的步骤:
1. 打开命令行工具。
2. 执行以下命令生成密钥库:
```
keytool -genkeypair -alias mykey -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650
```
3. 按照提示输入密钥库密码、个人信息等。
通过以上步骤,我们就可以生成一个安全的密钥库,用于 SSL 通信。
以下是 SSL 通信的流程图:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A([开始]):::startend --> B(创建 SSLServerSocket 或 SSLSocket):::process
B --> C(建立 SSL 连接):::process
C --> D(进行安全通信):::process
D --> E(关闭连接):::process
E --> F([结束]):::startend
```
通过以上内容,我们对 Java 网络编程的基础、UDP 和多播、可扩展性以及安全等方面有了更深入的了解。在实际开发中,我们可以根据具体需求选择合适的网络编程技术来构建高效、安全的网络应用程序。
### 开启 Java 网络编程之旅
#### 5. 网络寻址
网络寻址是网络编程中的重要基础,它涉及到如何标识网络中的节点以及如何配置网络属性。
##### 5.1 网络基础
在深入了解网络寻址之前,我们需要先掌握一些网络基础知识,包括网络架构和协议。常见的网络架构有客户端/服务器架构和对等网络架构,而常见的网络协议有 TCP/IP、HTTP 等。
网络架构和协议的关系如下表所示:
| 网络架构 | 常见协议 |
| ---- | ---- |
| 客户端/服务器架构 | TCP/IP、HTTP |
| 对等网络架构 | UDP、P2P 协议 |
##### 5.2 使用 NetworkInterface 类
`NetworkInterface` 类可以帮助我们获取网络接口的相关信息,例如 MAC 地址。以下是获取 MAC 地址的示例代码:
```java
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
public class GetMACAddress {
public static void main(String[] args) {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
byte[] mac = networkInterface.getHardwareAddress();
if (mac != null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
System.out.println("网络接口: " + networkInterface.getName() + ", MAC 地址: " + sb.toString());
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,我们首先使用 `NetworkInterface.getNetworkInterfaces()` 方法获取所有的网络接口,然后遍历每个网络接口,使用 `getHardwareAddress()` 方法获取其 MAC 地址,并将其转换为字符串格式输出。
##### 5.3 网络寻址概念
网络寻址涉及到 URL、URI、URN 等概念。URL(Uniform Resource Locator)用于定位网络上的资源,URI(Uniform Resource Identifier)是一个更通用的概念,用于标识资源,而 URN(Uniform Resource Name)则是一种特殊的 URI,用于永久标识资源。
以下是使用 `URI` 类的示例代码:
```java
import java.net.URI;
import java.net.URISyntaxException;
public class URIExample {
public static void main(String[] args) {
try {
URI uri = new URI("https://www.example.com/path?query=value#fragment");
System.out.println("Scheme: " + uri.getScheme());
System.out.println("Authority: " + uri.getAuthority());
System.out.println("Path: " + uri.getPath());
System.out.println("Query: " + uri.getQuery());
System.out.println("Fragment: " + uri.getFragment());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,我们创建了一个 `URI` 对象,并使用其方法获取 URI 的各个部分。
##### 5.4 IP 地址和 InetAddress 类
`InetAddress` 类用于表示 IP 地址,它可以处理 IPv4 和 IPv6 地址。以下是获取 IP 地址信息的示例代码:
```java
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressInfo {
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getByName("www.example.com");
System.out.println("主机名: " + address.getHostName());
System.out.println("IP 地址: " + address.getHostAddress());
System.out.println("是否可达: " + address.isReachable(5000));
} catch (UnknownHostException | java.io.IOException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,我们使用 `InetAddress.getByName()` 方法获取指定主机名的 `InetAddress` 对象,然后使用其方法获取主机名、IP 地址以及测试其可达性。
#### 6. NIO 对网络编程的支持
Java 的 NIO(New I/O)包为网络编程提供了高效的缓冲区和通道支持,同时还支持异步通信。
##### 6.1 Java NIO 基础
NIO 中的核心概念包括缓冲区(Buffer)和通道(Channel)。缓冲区用于存储数据,而通道则用于数据的读写操作。以下是使用通道和缓冲区实现时间服务器和客户端的示例。
**创建时间服务器**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class TimeServer {
public static void main(String[] args) {
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress(12345));
System.out.println("时间服务器已启动,等待客户端连接...");
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
ByteBuffer buffer = ByteBuffer.allocate(1024);
String currentTime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
buffer.put(currentTime.getBytes());
buffer.flip();
socketChannel.write(buffer);
socketChannel.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,我们创建了一个 `ServerSocketChannel` 对象并绑定到指定端口,然后等待客户端连接。当客户端连接后,获取当前时间并将其存储在 `ByteBuffer` 中,最后将缓冲区的数据写入通道发送给客户端。
**创建时间客户端**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class TimeClient {
public static void main(String[] args) {
try (SocketChannel socketChannel = SocketChannel.open()) {
socketChannel.connect(new InetSocketAddress("localhost", 12345));
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String currentTime = new String(data);
System.out.println("当前时间: " + currentTime);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,我们创建一个 `SocketChannel` 对象并连接到服务器,然后从通道中读取数据并将其转换为字符串输出。
##### 6.2 聊天服务器/客户端应用
我们还可以使用 NIO 实现聊天服务器和客户端应用。以下是聊天服务器和客户端的示例代码。
**聊天服务器**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class ChatServer {
private static final int PORT = 12345;
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
try (Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("聊天服务器已启动,等待客户端连接...");
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("新客户端连接: " + clientChannel.getRemoteAddress());
} else if (key.isReadable()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int bytesRead = clientChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到消息: " + message);
// 广播消息给其他客户端
broadcastMessage(selector, clientChannel, message);
} else if (bytesRead == -1) {
System.out.println("客户端断开连接: " + clientChannel.getRemoteAddress());
clientChannel.close();
}
}
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void broadcastMessage(Selector selector, SocketChannel senderChannel, String message) throws IOException {
Set<SelectionKey> keys = selector.keys();
for (SelectionKey key : keys) {
if (key.isValid() && key.channel() instanceof SocketChannel && key.channel() != senderChannel) {
SocketChannel recipientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
recipientChannel.write(buffer);
}
}
}
}
```
在这个代码中,我们使用 `Selector` 来实现多路复用,监听客户端的连接和消息。当有新客户端连接时,将其注册到 `Selector` 上并监听其读事件。当收到客户端消息时,将消息广播给其他客户端。
**聊天客户端**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
public class ChatClient {
private static final String SERVER_ADDRESS = "localhost";
private static final int PORT = 12345;
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
try (Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open()) {
socketChannel.connect(new InetSocketAddress(SERVER_ADDRESS, PORT));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("已连接到聊天服务器");
// 启动一个线程用于读取用户输入并发送消息
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String message = scanner.nextLine();
try {
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到消息: " + message);
}
}
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,我们同样使用 `Selector` 来监听服务器的消息。同时,启动一个线程用于读取用户输入并发送消息给服务器。
以下是 NIO 聊天服务器/客户端通信的流程图:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(创建 Selector 和 SocketChannel):::process
B --> C(连接到服务器):::process
C --> D(注册读事件到 Selector):::process
D --> E{是否有事件发生?}:::decision
E -->|是| F{事件类型?}:::decision
F -->|读事件| G(读取服务器消息):::process
G --> H(显示消息):::process
F -->|无其他事件| E
E -->|否| I(等待用户输入):::process
I --> J(发送消息给服务器):::process
J --> E
```
##### 6.3 异步套接字通道
NIO 还支持异步套接字通道,以下是创建异步服务器套接字通道服务器和客户端的示例。
**创建异步服务器套接字通道服务器**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
public class AsyncServer {
public static void main(String[] args) {
try (AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress(12345));
System.out.println("异步服务器已启动,等待客户端连接...");
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
serverSocketChannel.accept(null, this);
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
clientChannel.read(buffer).get();
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到消息: " + message);
buffer.clear();
buffer.put("消息已收到".getBytes());
buffer.flip();
clientChannel.write(buffer).get();
clientChannel.close();
} catch (InterruptedException | ExecutionException | IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
}
});
// 保持主线程运行
Thread.sleep(Long.MAX_VALUE);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
```
在这个代码中,我们使用 `AsynchronousServerSocketChannel` 来实现异步服务器。当有客户端连接时,使用 `CompletionHandler` 来处理连接和消息的读写操作。
**创建异步套接字通道客户端**
```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
public class AsyncClient {
public static void main(String[] args) {
try (AsynchronousSocketChannel clientChannel = AsynchronousSocketChannel.open()) {
clientChannel.connect(new InetSocketAddress("localhost", 12345)).get();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello, server!".getBytes());
buffer.flip();
clientChannel.write(buffer).get();
buffer.clear();
clientChannel.read(buffer).get();
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到服务器响应: " + message);
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
```
客户端代码中,使用 `AsynchronousSocketChannel` 连接到服务器,并异步发送和接收消息。
通过以上内容,我们深入了解了 Java 网络编程中的网络寻址和 NIO 支持。网络寻址帮助我们准确标识和定位网络中的资源,而 NIO 则为我们提供了高效的网络通信解决方案,包括缓冲区、通道和异步通信等特性。在实际开发中,我们可以根据具体需求选择合适的网络编程技术来构建高性能、可扩展的网络应用程序。
0
0
复制全文
相关推荐









