活动介绍

开启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 则为我们提供了高效的网络通信解决方案,包括缓冲区、通道和异步通信等特性。在实际开发中,我们可以根据具体需求选择合适的网络编程技术来构建高性能、可扩展的网络应用程序。
corwn 最低0.47元/天 解锁专栏
赠100次下载
点击查看下一篇
profit 400次 会员资源下载次数
profit 300万+ 优质博客文章
profit 1000万+ 优质下载资源
profit 1000万+ 优质文库回答
复制全文

相关推荐

李_涛

知名公司架构师
拥有多年在大型科技公司的工作经验,曾在多个大厂担任技术主管和架构师一职。擅长设计和开发高效稳定的后端系统,熟练掌握多种后端开发语言和框架,包括Java、Python、Spring、Django等。精通关系型数据库和NoSQL数据库的设计和优化,能够有效地处理海量数据和复杂查询。
最低0.47元/天 解锁专栏
赠100次下载
百万级 高质量VIP文章无限畅学
千万级 优质资源任意下载
千万级 优质文库回答免费看
立即解锁

专栏目录

最新推荐

机械臂三维模型的材料选择与应用:材质决定命运,选对材料赢未来

![机械臂三维模型的材料选择与应用:材质决定命运,选对材料赢未来](https://blogs.sw.siemens.com/wp-content/uploads/sites/2/2023/12/Inverse-Kinematics-1024x466.png) # 摘要 机械臂作为先进制造和自动化系统的重要组成部分,其三维模型设计和材料选择对提高机械臂性能与降低成本至关重要。本文从基础理论出发,探讨了机械臂三维模型设计的基本原则,以及材料选择对于机械臂功能和耐久性的关键作用。通过对聚合物、金属和复合材料在实际机械臂应用案例的分析,本文阐述了不同材料的特性和应用实例。同时,提出了针对机械臂材料

在线票务系统解析:功能、流程与架构

### 在线票务系统解析:功能、流程与架构 在当今数字化时代,在线票务系统为观众提供了便捷的购票途径。本文将详细解析一个在线票务系统的各项特性,包括系统假设、范围限制、交付计划、用户界面等方面的内容。 #### 系统假设与范围限制 - **系统假设** - **Cookie 接受情况**:互联网用户不强制接受 Cookie,但预计大多数用户会接受。 - **座位类型与价格**:每场演出的座位分为一种或多种类型,如高级预留座。座位类型划分与演出相关,而非个别场次。同一演出同一类型的座位价格相同,但不同场次的价格结构可能不同,例如日场可能比晚场便宜以吸引家庭观众。 -

响应式Spring开发:从错误处理到路由配置

### 响应式Spring开发:从错误处理到路由配置 #### 1. Reactor错误处理方法 在响应式编程中,错误处理是至关重要的。Project Reactor为其响应式类型(Mono<T> 和 Flux<T>)提供了六种错误处理方法,下面为你详细介绍: | 方法 | 描述 | 版本 | | --- | --- | --- | | onErrorReturn(..) | 声明一个默认值,当处理器中抛出异常时发出该值,不影响数据流,异常元素用默认值代替,后续元素正常处理。 | 1. 接收要返回的值作为参数<br>2. 接收要返回的值和应返回默认值的异常类型作为参数<br>3. 接收要返回

【电路设计揭秘】:5个技巧彻底理解电路图的奥秘

![【电路设计揭秘】:5个技巧彻底理解电路图的奥秘](https://electronics.koncon.nl/wp-content/uploads/2020/09/all_components-1-1024x506.jpg) # 摘要 电路图与电路设计是电子工程领域的基石,本文全面概述了电路图的基础知识、核心理论以及设计实践技巧。从电路图基础知识开始,逐步深入到电路设计的核心理论,包括基本电路元件特性、电路理论基础和仿真软件应用。在实践技巧方面,本文介绍了电路图绘制、测试与调试、PCB设计与制造的关键点。进一步探讨了模拟电路与数字电路的区别及应用、电源电路设计优化、微控制器的电路设计应用

【Nokia 5G核心网运维自动化】:提升效率与降低错误率的6大策略

![5g核心网和关键技术和功能介绍-nokia.rar](https://www.viavisolutions.com/sites/default/files/images/diagram-sba.png) # 摘要 随着5G技术的快速发展,其核心网运维面临一系列新的挑战。本文首先概述了5G核心网运维自动化的必要性,然后详细分析了Nokia 5G核心网架构及其运维挑战,包括组件功能、架构演变以及传统运维的局限性。接着,文章探讨了自动化策略的基础理论与技术,包括自动化工具的选择和策略驱动的自动化设计。重点介绍了Nokia 5G核心网运维自动化策略实践,涵盖网络部署、故障诊断与性能优化的自动化实

并发编程:多语言实践与策略选择

### 并发编程:多语言实践与策略选择 #### 1. 文件大小计算的并发实现 在并发计算文件大小的场景中,我们可以采用数据流式方法。具体操作如下: - 创建两个 `DataFlowQueue` 实例,一个用于记录活跃的文件访问,另一个用于接收文件和子目录的大小。 - 创建一个 `DefaultPGroup` 来在线程池中运行任务。 ```plaintext graph LR A[创建 DataFlowQueue 实例] --> B[创建 DefaultPGroup] B --> C[执行 findSize 方法] C --> D[执行 findTotalFileS

AWSLambda冷启动问题全解析

### AWS Lambda 冷启动问题全解析 #### 1. 冷启动概述 在 AWS Lambda 中,冷启动是指函数实例首次创建时所经历的一系列初始化步骤。一旦函数实例创建完成,在其生命周期内不会再次经历冷启动。如果在代码中添加构造函数或静态初始化器,它们仅会在函数冷启动时被调用。可以在处理程序类的构造函数中添加显式日志,以便在函数日志中查看冷启动的发生情况。此外,还可以使用 X-Ray 和一些第三方 Lambda 监控工具来识别冷启动。 #### 2. 冷启动的影响 冷启动通常会导致事件处理出现延迟峰值,这也是人们关注冷启动的主要原因。一般情况下,小型 Lambda 函数的端到端延迟

ApacheThrift在脚本语言中的应用

### Apache Thrift在脚本语言中的应用 #### 1. Apache Thrift与PHP 在使用Apache Thrift和PHP时,首先要构建I/O栈。以下是构建I/O栈并调用服务的基本步骤: 1. 将传输缓冲区包装在二进制协议中,然后传递给服务客户端的构造函数。 2. 构建好I/O栈后,打开套接字连接,调用服务,最后关闭连接。 示例代码中的异常捕获块仅捕获Apache Thrift异常,并将其显示在Web服务器的错误日志中。 PHP错误通常在Web服务器的上下文中在服务器端表现出来。调试PHP程序的基本方法是检查Web服务器的错误日志。在Ubuntu 16.04系统中

Clojure多方法:定义、应用与使用场景

### Clojure 多方法:定义、应用与使用场景 #### 1. 定义多方法 在 Clojure 中,定义多方法可以使用 `defmulti` 函数,其基本语法如下: ```clojure (defmulti name dispatch-fn) ``` 其中,`name` 是新多方法的名称,Clojure 会将 `dispatch-fn` 应用于方法参数,以选择多方法的特定实现。 以 `my-print` 为例,它接受一个参数,即要打印的内容,我们希望根据该参数的类型选择特定的实现。因此,`dispatch-fn` 需要是一个接受一个参数并返回该参数类型的函数。Clojure 内置的

编程中的数组应用与实践

### 编程中的数组应用与实践 在编程领域,数组是一种非常重要的数据结构,它可以帮助我们高效地存储和处理大量数据。本文将通过几个具体的示例,详细介绍数组在编程中的应用,包括图形绘制、随机数填充以及用户输入处理等方面。 #### 1. 绘制数组图形 首先,我们来创建一个程序,用于绘制存储在 `temperatures` 数组中的值的图形。具体操作步骤如下: 1. **创建新程序**:选择 `File > New` 开始一个新程序,并将其保存为 `GraphTemps`。 2. **定义数组和画布大小**:定义一个 `temperatures` 数组,并设置画布大小为 250 像素×250 像