版权声明:本博客为记录本人学习过程而开,内容大多从网上学习与整理所得,若侵权请告知! https://blog.csdn.net/Fly_as_tadpole/article/details/84205161
不同的SelectableChannel所支持的操作是不同的。例如ServerSocketChannel代表一个ServerSocket,它就只支持OP_ACCEPT操作;
当Selector上注册的所有Channel都没有需要处理的IO操作的时候,select方法将会被阻塞,调用该方法的线程被阻塞。
int select();//默认阻塞
int select(long timeout);//设置超时
int selectNow();//立即返回
服务器上的所有的Channel(ServerSocketChannel 和 SocketChannel)都需要向selector注册。
服务器端需要使用ServerSocketChannel来监听客户端的连接请求。
ServerSocketChannel server = ServerSocketChannel.open();
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",30000);
server.bind(isa);
server.configureBlocking(false);
server.register(selector,SelectionKey.OP_ACCEPT);
监听到客户端连接请求时,返回一个SocketChannel实例。
服务器端:
package com.nanhao.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
public class Server {
private Selector selector = null;
static final int PORT = 30000;
static final int BUFFSIZE = 1024;
//定义实现编码解码的字符集对象
private Charset charSet = Charset.forName("UTF-8");
public void init()throws IOException{
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",PORT);
server.bind(isa);
//设置ServerSocket以非阻塞的方式进行
server.configureBlocking(false);
//将server注册到selector里面(每个套接字具有的注册功能)
server.register(selector, SelectionKey.OP_ACCEPT);
while(selector.select()>0){
for(SelectionKey sk:selector.selectedKeys()){
//一旦正在处理这个套接字,那么就要先从集合中删除这个套接字
selector.selectedKeys().remove(sk);
if(sk.isAcceptable()){
SocketChannel sc = server.accept();
//设置非阻塞模式
sc.configureBlocking(false);
//将该套接字注册到selector里面
sc.register(selector,SelectionKey.OP_READ);
//将之前的sk修改为准备接受其他请求
sk.interestOps(SelectionKey.OP_ACCEPT);
}
if(sk.isReadable()){
SocketChannel sc = (SocketChannel)sk.channel();
//定义准备接受数据的BUFFER
ByteBuffer buff = ByteBuffer.allocate(BUFFSIZE);
String context = "";
//开始读取数据
try{
while(sc.read(buff)>0){
buff.flip();
//实现解码
context += charSet.decode(buff);
}
System.out.println("读取的数据:"+context);
//将此套接字对应的channel设置成准备下一次读取
sk.interestOps(SelectionKey.OP_READ);
//如果捕获到该SK对应的channel出现异常的话,即表明该channel对应的client出现了问题
//所以从Selector里面取消sk的注册。
}catch(IOException io){
sk.cancel();
if(sk.channel() !=null){
sk.channel().close();
}
}
if(context.length()>0){
for(SelectionKey key :selector.keys()){
//获取key对应的channel
Channel targetChannel = key.channel();
if(targetChannel instanceof SocketChannel){
SocketChannel dest = (SocketChannel) targetChannel;
//实现编码
dest.write(charSet.encode(context));
}
}
}
}
}
}
}
public static void main(String[]args) throws IOException{
new Server().init();
}
}
客户端:
package com.nanhao.client;
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.nio.charset.Charset;
import java.util.Scanner;
public class Client{
private Selector selector =null;
static final int PORT=30000;
static final int BUFFSIZE = 1024;
private Charset charset = Charset.forName("UTF-8");
//创建客户端套接字
private SocketChannel sc = null;
public void init()throws IOException {
selector = Selector.open();
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",PORT);
//调用静态open方法创建连接到指定主机的SocketChannel
sc = SocketChannel.open();
//设置非阻塞的模式
sc.configureBlocking(false);
//注册到Selector
sc.register(selector, SelectionKey.OP_READ);
//启动读取服务器端数据库数据的线程
new ClientThread().start();
//创建键盘输入流
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()){
String line = scanner.nextLine();
//将键盘的内容写到SocketChannel
sc.write(charset.encode(line));
}
}
private class ClientThread extends Thread {
public void run(){
try{
while(selector.select()>0){
//遍历每个IO可用的channel对应的SelectorKey
for(SelectionKey sk :selector.selectedKeys()){
selector.selectedKeys().remove(sk);
if(sk.isReadable()){
SocketChannel sc = (SocketChannel)sk.channel();
//创建buff
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFSIZE);
String context = "";
while(sc.read(byteBuffer)>0){
//清空内存
byteBuffer.flip();
context += charset.decode(byteBuffer);
}
System.out.println("聊天信息:"+context);
sk.interestOps(SelectionKey.OP_READ);
}
}
}
}catch(IOException io){
io.printStackTrace();
}
}
}
public static void main(String[]args) throws IOException{
new Client().init();
}
}