Java的NIO采用selector来轮循,还是不错,小试牛刀,下附代码
Server:
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;
public class NIOServer {
private Selector selector;
public void initServer(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开服务器套接字通道
serverSocketChannel.configureBlocking(false);// 设置为非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(port));// 绑定端口
this.selector = Selector.open();// 打开选择器
// 注册事件,当事件到达,selector。select()会返回,如果没有事件会一直阻塞
serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);// 注册选择器,接受客户端连接
}
public void listen() throws IOException {
System.out.println("服务器启动!开始轮询监听消息");
while (true) {
int ready = this.selector.select();
if (ready == 0) {
continue;
}
Iterator items = this.selector.selectedKeys().iterator();
while (items.hasNext()) {
SelectionKey key = (SelectionKey) items.next();
// 删除已经选择的key,防止重复处理
items.remove();
// 请求连接
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key
.channel();
if (serverSocketChannel == null) {
continue;
}
SocketChannel socketChannel = serverSocketChannel.accept();//每次接受新的连接请求会产生新的通道,
socketChannel.configureBlocking(false);
socketChannel.write(ByteBuffer.wrap(new String(
"你好,客户端,连接已建立,可以开始通信!").getBytes()));//连接成功,发消息给客户端
System.out.println("连接已经建立");
socketChannel.register(this.selector, SelectionKey.OP_READ);//准备好接收数据
} else if (key.isReadable()) {
this.read(key);
}
}
}
}
public void read(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
socketChannel.read(byteBuffer);
byte[] data = byteBuffer.array();
String msg = new String(data).trim();
if (msg.equals("close~!@#$%")) {//收到关闭指令,发送确认关闭的指令给客户端,结束自身通道
ByteBuffer sendBuffer = ByteBuffer.wrap(new String("bye close").getBytes());
while (sendBuffer.hasRemaining()) {
socketChannel.write(sendBuffer);
}
socketChannel.close();
System.out.println("已经关闭");
} else {
System.out.println("server Receive:" + msg);//服务器收到任何消息都会给客户端发送"收到"
socketChannel.write(ByteBuffer.wrap(new String("收到!").getBytes()));
}
}
public static void main(String[] args) {
NIOServer server = new NIOServer();
try {
server.initServer(1377);
server.listen();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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;
public class NIOClient {
private Selector selector;
//初始化
public void initClient(String ipAddress, int port) throws IOException {
SocketChannel socketChannel = SocketChannel.open();//打开套接字通道
socketChannel.configureBlocking(false);//非阻塞
this.selector = Selector.open();//打开选择器
socketChannel.connect(new InetSocketAddress(ipAddress, port));//指定ip地址和端口号用于连接
socketChannel.register(this.selector, SelectionKey.OP_CONNECT);//通道注册到选择器中,用于连接
}
//监听
public void listen() throws IOException {
while (true) {
//当客户端关闭的时候需要结束轮循的动作,这样程序才会结束
if (!this.selector.isOpen()) {
return;
}
int ready = this.selector.select();//这个才是真正的轮循啦,得到通道
if (ready == 0) {
continue;//如果selector中没有SocketChannel,直接开始下一次咯
}
Iterator items = this.selector.selectedKeys().iterator();
while (items.hasNext()) {
SelectionKey key = (SelectionKey) items.next();
items.remove();//防止重复处理
if (key.isConnectable()) {//连接
System.out.println("正在连接,连接成功后,服务器会返回结果!");
SocketChannel socketChannel = (SocketChannel) key.channel();
// 如果正在连接,则完成连接
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
socketChannel.register(this.selector, SelectionKey.OP_READ);//重新注册该通道,用于读取数据
} else if (key.isReadable()) {//读取
this.read(key);
}
}
}
}
/****************************************************/
/*
* 客户端关闭思路
* 客户端 服务器
*
* 用户输入bye,需要关闭客户端
*
* "close~!@#$%" -> "close~!@#$%" 约定好的内容("close~!@#$%")
*
* "bye close" <- "bye close" ----->服务器通道关闭 约定好的内容("bye close")
*
* 客户端通道关闭 服务器还可以接受别的请求
*
*/
public void read(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer receiveBuffer = ByteBuffer.allocate(100);
socketChannel.read(receiveBuffer);
byte[] data = receiveBuffer.array();
String msg = new String(data).trim();
if (msg.equals("bye close")) {//客户端和服务器约定关闭,收到服务器响应后可以关闭
System.out.println("关闭");
socketChannel.close();
this.selector.close();
return;
}
System.out.println("client receive:" + msg);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String readMsg = reader.readLine();
ByteBuffer sendBuffer = ByteBuffer.allocate(256);
sendBuffer.clear();
if(readMsg.equals("bye")){//告诉服务器,准备要撤退,当服务器返回bye close,关闭通道和选择器
System.out.println("准备关闭");
sendBuffer.put("close~!@#$%".getBytes());
sendBuffer.flip();
}else{
sendBuffer.put(readMsg.getBytes());
sendBuffer.flip();
}
while (sendBuffer.hasRemaining()) {
socketChannel.write(sendBuffer);
}
}
public static void main(String[] args) {
NIOClient client = new NIOClient();
try {
client.initClient("127.0.0.1", 1377);
client.listen();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端可开启多个,用于连接服务器端,亲测可用,有问题联系作者,xusong1313@qq.com
业务逻辑可自己完善,其实建议的搭建不难