NIO-结合Selector设计一个简易WebServer
2017-11-04 17:17
585 查看
参考资料
建议大家找找这种资料来了解下什么是IO,什么是NIO,NIO的理念是什么。
Channel类似旧IO的流,可读可写,读写都是面向Buffer。
Channel通常来自文件或网络:
Selector是非阻塞的核心,不像以前所有的读写都要阻塞,如果要并发就必须开多线程来监听每一路的IO。Selector有一种注册机制,首先读写事件不阻塞,只需向Selector注册感兴趣的事件,这样CPU可以去干点别的事,只需定期轮询Selector即可。
实例化一个Selector,对Server通道的连接就绪事件感兴趣:
轮询Selector状态,如果出现连接事件,实例化一个客户端套接字连接通道,将这个通道上的可读事件注册给Selector;
如果出现可读事件,读取客户端的HTTP连接报文,写回一段HTTP响应报文;
如果出现可写事件,完成内容写,并关闭连接。
建议大家找找这种资料来了解下什么是IO,什么是NIO,NIO的理念是什么。
一点小结论
Channel、Buffer、Selector是NIO的核心API。Channel类似旧IO的流,可读可写,读写都是面向Buffer。
Channel通常来自文件或网络:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); …… RandomAccessFile randomAccessFile = new RandomAccessFile("/Users/zhengwei/lanqiao/ConditionOperator.java", "rw"); FileChannel channel = randomAccessFile.getChannel();
Selector是非阻塞的核心,不像以前所有的读写都要阻塞,如果要并发就必须开多线程来监听每一路的IO。Selector有一种注册机制,首先读写事件不阻塞,只需向Selector注册感兴趣的事件,这样CPU可以去干点别的事,只需定期轮询Selector即可。
简易Server的实现思路
打开Server通道,并做初始化initServerChannel
实例化一个Selector,对Server通道的连接就绪事件感兴趣:
//打开一个选择器 Selector selector = Selector.open();// Selector的创建 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 对channel的连接就绪感兴趣
轮询Selector状态,如果出现连接事件,实例化一个客户端套接字连接通道,将这个通道上的可读事件注册给Selector;
if (key.isAcceptable()) { dealAcceptable(serverSocketChannel, selector); }
如果出现可读事件,读取客户端的HTTP连接报文,写回一段HTTP响应报文;
else if (key.isReadable()) {// 有读的事儿没? dealReadable(selector, key); }
如果出现可写事件,完成内容写,并关闭连接。
else if (key.isWritable()) { dealWritable(key); }
完整代码
package org.lanqiao.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Server implements Runnable {
//Server启停标志
private boolean interrupted = false;
public void run() {
try {
ServerSocketChannel serverSocketChannel = initServerChannel();
//打开一个选择器 Selector selector = Selector.open();// Selector的创建 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 对channel的连接就绪感兴趣
while (!interrupted) {
//查询就绪的通道数量,这是一个阻塞方法
int readyChannels = selector.select(500);
//没有就绪的则继续进行循环
if (readyChannels == 0)
continue;
final Set<SelectionKey> selectionKeys = selector.selectedKeys();// 啥事,阻塞
//迭代当前已发生的事件
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
dealAcceptable(serverSocketChannel, selector);
} else if (key.isConnectable()) {
// a connection was established with a remote server.
}else if (key.isReadable()) {// 有读的事儿没? dealReadable(selector, key); } else if (key.isWritable()) { dealWritable(key); }
//从key集合中删除key,这一步很重要
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void dealWritable(SelectionKey key) throws IOException {
// a channel is ready for writing
SocketChannel clientChannel = (SocketChannel) key.channel();
clientChannel.shutdownInput();
clientChannel.close();
}
private void dealReadable(Selector selector, SelectionKey key) {
// a channel is ready for reading
//该key有Read事件
SocketChannel clientChannel = (SocketChannel) key.channel();
String lines = NIOx.readAllLine(clientChannel);
System.out.println("lines===\n" + lines);
try {
clientChannel.register(selector, SelectionKey.OP_WRITE);
//书写报文
clientChannel.write(ByteBuffer.wrap((
"HTTP/1.1 200 OK\n" +
"Date: Sat, 4 Nov 2017 23:59:59 GMT\n" +
"Content-Type: text/html;charset=utf-8\n" +
"Content-Length: 5\n" +
"\n" +
"hello").getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
private void dealAcceptable(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
//该key有ACCEPT事件
//将监听得到的channel强转为ServerSocketChannel
// a connection was accepted by a ServerSocketChannel.
//得到接收到的SocketChannel
SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);// 打死不阻塞
clientChannel.register(selector, SelectionKey.OP_READ);
}
private ServerSocketChannel initServerChannel() throws IOException {
//打开ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//得到ServerSocket对象
final ServerSocket serverSocket = serverSocketChannel.socket();
//监听端口
serverSocket.bind(new InetSocketAddress(8082));
//配置为非阻塞
serverSocketChannel.configureBlocking(false);
return serverSocketChannel;
}
public static void main(String[] args) {
new Thread(new Server()).start();
}
}
相关文章推荐
- web开发 wsgief 小试牛刀:一个简易的server:envrion 、start_respo
- 一个简易的web服务器:Tinywebserver
- 用Python实现一个简易的WebSocket Server
- Tinywebserver-一个简易的web服务器
- web版的outlook和project的结合,再和sns 结合,形成组织之间的一个共享信息.还有更多应用
- 一个实用的web设计模式
- 计划建造一个 自己的 BBS 系统, 完全采用 Web 标准 设计.
- 使用webpack-dev-middleware 和 webpack-hot-middleware 配置一个dev-server
- 如何设计一个优雅健壮的Android WebView?(上)
- Tornado Web Server框架编写简易Python服务器
- 开始一个React项目(二) 彻底弄懂webpack-dev-server的热更新
- 一个Web系统OA界面设计和开发
- 一个大型web系统架构设计和技术选型的讨论
- 一个可靠的天气预报Web Server
- Java 设计一个贷款计算器 简易
- asp.net 一个简易权限的小例子设计
- ServerSocket和Socket创建一个简易的服务器
- [转]一个Web系统OA界面设计和开发
- 动手开发一个简易的 PHP for Git Server 第一章
- android 设计一个简易的Http网络请求框架