网络编程
ServerSocketChannel
和ServerSocket
都可以在Java中创建服务器端的套接字监听并接受客户端的连接,但它们属于Java两种不同的I/O模型。
ServerSocket (传统的阻塞I/O)
ServerSocket
是Java原始I/O(也称为阻塞I/O)的一部分。当使用ServerSocket
时:
- 阻塞操作:
accept()
方法会阻塞,直到一个连接建立,然后返回一个Socket
对象用于与客户端通信。 - 线程模型:通常每个连接都需要一个线程来处理,这可能在高负载时导致资源使用效率低下。
- 简单性:
ServerSocket
的API简单,适用于连接数较少且对性能要求不高的应用。
下面是使用ServerSocket
的一个简单示例:
java
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket clientSocket = serverSocket.accept(); // 阻塞直到新连接到来
// 使用clientSocket与客户端通信...
}
ServerSocketChannel (非阻塞I/O,Java NIO)
ServerSocketChannel
是Java新I/O(NIO)的一部分。与ServerSocket
相比,ServerSocketChannel
提供了更多的灵活性:
- 非阻塞操作:可以配置为非阻塞模式,在此模式下
accept()
方法会立即返回,不论是否有连接,都可以让线程执行其他任务。 - 选择器(Selectors):可以与
Selector
一起使用,一个线程可以管理多个Channel
的I/O操作。 - 性能:在处理大量连接时,
ServerSocketChannel
配合选择器使用可以提供更高的性能。
下面是使用ServerSocketChannel
的一个简单示例:
java
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 非阻塞模式
serverChannel.socket().bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
// 处理I/O事件...
}
在实践中,你会选择使用ServerSocket
还是ServerSocketChannel
,这取决于你的具体需求。如果你的应用需要处理大量并发连接,并希望通过单个或少数几个线程来管理这些连接,那么ServerSocketChannel
可能是更好的选择。如果你的应用连接数不多,逻辑简单,使用ServerSocket
的传统阻塞模型可能会更容易实现。
java
package com.jasper.nio;
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 NIOServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
// 配置非阻塞
serverSocket.configureBlocking(false);
serverSocket.bind(new InetSocketAddress("localhost", 8080));
//通道注册到选择器上
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true){
//选择一组键 其相应的通道已经准备好io操作
selector.select();
// 返回已选择键集
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if (key.isAcceptable()){
//接受连接
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 注册读操作
client.register(selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
//读数据
SocketChannel client = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = client.read(buffer);
if (read > 0) {
buffer.flip();
//返回当前位置和限制之间的元素数
byte[] bytes = new byte[buffer.remaining()];
// 从缓冲区读取字节到字节数组
buffer.get(bytes);
String message = new String(bytes);
System.out.println("Message received: " + message);
// 响应客户端
ByteBuffer writeBuffer = ByteBuffer.wrap(("Echo: " + message).getBytes());
client.write(writeBuffer);
} else if (read < 0) {
// 结束通道
client.close();
}
}
// 移除处理过的键
iterator.remove();
}
}
}
}
java
package com.jasper.nio;
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.Set;
public class NIOClientWithSelector {
public static void main(String[] args) throws Exception {
// 打开选择器
Selector selector = Selector.open();
// 打开一个SocketChannel并配置为非阻塞
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
// 连接到服务器地址和端口
socketChannel.connect(new InetSocketAddress("localhost", 8080));
// 将通道注册到选择器上,对连接完成事件感兴趣
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while (true) {
// 等待事件
selector.select();
// 获取事件集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isConnectable()) {
handleConnect(key);
} else if (key.isReadable()) {
handleRead(key);
}
}
}
}
private static void handleConnect(SelectionKey key) throws Exception {
SocketChannel channel = (SocketChannel) key.channel();
// 完成连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
// 注册读事件
channel.register(key.selector(), SelectionKey.OP_READ);
// 发送消息到服务器
String message = "Hello, Server!";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
while (buffer.hasRemaining()) {
channel.write(buffer);
}
}
private static void handleRead(SelectionKey key) throws Exception {
SocketChannel channel = (SocketChannel) key.channel();
// 读取服务器响应
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = channel.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String response = new String(bytes);
System.out.println("Message from server: " + response);
// 关闭连接
channel.close();
key.selector().close();
System.exit(0); // 正常退出程序
}
}
}