基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础。

下面看看,Java实现的一个服务端和客户端通信的例子。

NIO模式的基本原理描述如下:

服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理。

客户端在请求与服务端通信时,也可以向服务器端一样注册(比服务端少了一个SelectionKey.OP_ACCEPT操作集合),并通过轮询来处理指定的事件,而不必阻塞。

下面的例子,主要以服务端为例,而客户端只是简单地发送请求数据和读响应数据。

服务端实现,代码如下所示:

package org.shirdrn.java.communications.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;
import java.util.logging.Logger;

/**
* NIO服务端
*
* @author shirdrn
*/
public class NioTcpServer extends Thread {

private static final Logger log = Logger.getLogger(NioTcpServer.class.getName());
private InetSocketAddress inetSocketAddress;
private Handler handler = new ServerHandler();

public NioTcpServer(String hostname, int port) {
inetSocketAddress = new InetSocketAddress(hostname, port);
}

@Override
public void run() {
try {
Selector selector = Selector.open(); // 打开选择器
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打开通道
serverSocketChannel.configureBlocking(false); // 非阻塞
serverSocketChannel.socket().bind(inetSocketAddress);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 向通道注册选择器和对应事件标识
log.info("Server: socket server started.");
while(true) { // 轮询
int nKeys = selector.select();
if(nKeys>0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable()) {
log.info("Server: SelectionKey is acceptable.");
handler.handleAccept(key);
} else if(key.isReadable()) {
log.info("Server: SelectionKey is readable.");
handler.handleRead(key);
} else if(key.isWritable()) {
log.info("Server: SelectionKey is writable.");
handler.handleWrite(key);
}
it.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 简单处理器接口
*
* @author shirdrn
*/
interface Handler {
/**
* 处理{@link SelectionKey#OP_ACCEPT}事件
* @param key
* @throws IOException
*/
void handleAccept(SelectionKey key) throws IOException;
/**
* 处理{@link SelectionKey#OP_READ}事件
* @param key
* @throws IOException
*/
void handleRead(SelectionKey key) throws IOException;
/**
* 处理{@link SelectionKey#OP_WRITE}事件
* @param key
* @throws IOException
*/
void handleWrite(SelectionKey key) throws IOException;
}

/**
* 服务端事件处理实现类
*
* @author shirdrn
*/
class ServerHandler implements Handler {

@Override
public void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
log.info("Server: accept client socket " + socketChannel);
socketChannel.configureBlocking(false);
socketChannel.register(key.selector(), SelectionKey.OP_READ);
}

@Override
public void handleRead(SelectionKey key) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
SocketChannel socketChannel = (SocketChannel)key.channel();
while(true) {
int readBytes = socketChannel.read(byteBuffer);
if(readBytes>0) {
log.info("Server: readBytes = " + readBytes);
log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes));
byteBuffer.flip();
socketChannel.write(byteBuffer);
break;
}
}
socketChannel.close();
}

@Override
public void handleWrite(SelectionKey key) throws IOException {
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
byteBuffer.flip();
SocketChannel socketChannel = (SocketChannel)key.channel();
socketChannel.write(byteBuffer);
if(byteBuffer.hasRemaining()) {
key.interestOps(SelectionKey.OP_READ);
}
byteBuffer.compact();
}
}

public static void main(String[] args) {
NioTcpServer server = new NioTcpServer("localhost", 1000);
server.start();
}
}


客户端实现,代码如下所示:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.logging.Logger;

/**
* NIO客户端
*
* @author shirdrn
*/
public class NioTcpClient {

private static final Logger log = Logger.getLogger(NioTcpClient.class.getName());
private InetSocketAddress inetSocketAddress;

public NioTcpClient(String hostname, int port) {
inetSocketAddress = new InetSocketAddress(hostname, port);
}

/**
* 发送请求数据
* @param requestData
*/
public void send(String requestData) {
try {
SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);
socketChannel.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));
while (true) {
byteBuffer.clear();
int readBytes = socketChannel.read(byteBuffer);
if (readBytes > 0) {
byteBuffer.flip();
log.info("Client: readBytes = " + readBytes);
log.info("Client: data = " + new String(byteBuffer.array(), 0, readBytes));
socketChannel.close();
break;
}
}

} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
String hostname = "localhost";
String requestData = "Actions speak louder than words!";
int port = 1000;
new NioTcpClient(hostname, port).send(requestData);
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值