Nio Socket
Posted code2038
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nio Socket相关的知识,希望对你有一定的参考价值。
Niosocket
Buffer 要传输的数据,like货物
Channel 传输通道,like快递配送中心
Selector 选择器,like挑拣员
configureBlocking(false)设置非阻塞模式,之后才能调用register方法注册Selector到ServerSocketChannel或SocketChannel
Selector可以通过工厂方法open创建,可以通过它的select方法来等待请求(其long类型参数代表最长等待时间,如参数为0或调用无参的select()重载方法会采用阻塞模式)
Selector调用selectedKeys方法返回SelectionKey集合(保存当前请求的Channel和Selector,并提供四种操作类型,可通过register的第二个参数选择特定的类型)
OP_ACCEPT 接受请求操作
OP_CONNECT 连接操作
OP_READ 读操作
OP_WRITE 写操作
NioSocket中服务端的处理过程可以分5步:
1)创建ServerSocketChannel并设置相应参数
2)创建Selector并注册到ServerSocketChannel
3)调用Selector的select方法等待请求
4)Selector接收到请求后使用selectedKeys返回SelectionKey集合
5)使用SelectionKey获取Channel、Selector和操作类型并进行具体操作
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.nio.charset.Charset;
import java.util.Iterator;
public class NIOServer {
public static void main(String[] args) throws Exception{
//创建ServerSocketChannel,监听8080端口
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(8080));
//设置为非阻塞模式
ssc.configureBlocking(false);
//为ssc注册选择器
Selector selector=Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
//创建处理器
Handler handler = new Handler(1024);
while(true){
// 等待请求,每次等待阻塞3s,超过3s后线程继续向下运行,如果传入0或者不传参数将一
// 直阻塞
if(selector.select(3000)==0){
System.out.println("等待请求超时……");
continue;
}
System.out.println("处理请求……");
// 获取待处理的SelectionKey
Iterator<SelectionKey> keyIter=selector.selectedKeys().iterator();
while(keyIter.hasNext()){
SelectionKey key=keyIter.next();
try{
// 接收到连接请求时
if(key.isAcceptable()){
handler.handleAccept(key);
}
// 读数据
if(key.isReadable()){
handler.handleRead(key);
}
} catch(IOException ex) {
keyIter.remove();
continue;
}
// 处理完后,从待处理的SelectionKey迭代器中移除当前所使用的key
keyIter.remove();
}
}
}
private static class Handler {
private int bufferSize = 1024;
private String localCharset = "UTF-8";
public Handler(){}
public Handler(int bufferSize){
this(bufferSize, null);
}
public Handler(String LocalCharset){
this(-1, LocalCharset);
}
public Handler(int bufferSize, String localCharset){
if(bufferSize>0)
this.bufferSize=bufferSize;
if(localCharset!=null)
this.localCharset=localCharset;
}
public void handleAccept(SelectionKey key) throws IOException {
SocketChannel sc=((ServerSocketChannel)key.channel()).accept();
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate (bufferSize));
}
public void handleRead(SelectionKey key) throws IOException {
// 获取channel
SocketChannel sc=(SocketChannel)key.channel();
// 获取buffer并重置
ByteBuffer buffer=(ByteBuffer)key.attachment();
buffer.clear();
// 没有读到内容则关闭
if(sc.read(buffer)==-1){
sc.close();
} else {
// 将buffer转换为读状态
buffer.flip();
// 将buffer中接收到的值按localCharset格式编码后保存到receivedString
String receivedString = Charset.forName(localCharset).newDecoder(). decode(buffer).toString();
System.out.println("received from client: " + receivedString);
// 返回数据给客户端
String sendString = "received data: " + receivedString;
buffer = ByteBuffer.wrap(sendString.getBytes(localCharset));
sc.write(buffer);
// 关闭Socket
sc.close();
}
}
}
}
java.nio.Buffer 专门用于存储数据,其中的四个属性:capacity(容量,Buffer最多可以保存的元素数量,在创建时设置,使用过程中不可改变)limit(可以使用的上限,不可以超过capacity)position(当前操作元素所在的索引位置,从0开始,随着get和put方法自动更新)mark(用来暂时保存position的值,position修改后可以通过reset方法将mark值恢复到position;默认值-1,其值必须小于position值)
mark<=position<=limit<=capacity
clear()重新初始化limit=capacity、position=0、mark=-1三个属性
1 public final Buffer clear() {
2
3 position = 0;
4
5 limit = capacity;
6
7 mark = -1;
8
9 return this;
10
11 }
12
13 public final Buffer flip() {
14
15 limit = position;
16
17 position = 0;
18
19 mark = -1;
20
21 return this;
22
23 }
实际开发多使用多线程处理
以上是关于Nio Socket的主要内容,如果未能解决你的问题,请参考以下文章