前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >利用NIO实现单文件的HTTP服务器

利用NIO实现单文件的HTTP服务器

作者头像
用户5325874
发布2022-05-06 16:46:56
4060
发布2022-05-06 16:46:56
举报
代码语言:javascript
复制
利用非阻塞IO实现的单文件HTTP服务器,可以在读取客户端通道数据的时候,分析客户端的请求数据,从而让服务器做出合理的响应,这部分在实现中省略了,为了展示出NIO服务器的一些基本实现形式。
代码语言:javascript
复制
package serverForNIO;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.URLConnection;
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;

// 提供单文件的HTTP服务器,非阻塞

public class HYSingleFileHttpServerNIO {
	private int port = 1000; // 默认端口
	private ByteBuffer contentBuffer; // 存储响应数据的缓冲区
	
	public HYSingleFileHttpServerNIO(ByteBuffer data, 
			String encoding, String MIMEType, int port) {
		this.port = port;
		// 无论是响应行,响应头,还是响应体,都转化成字节byte进行传送
		String header = "HTTP/1.0 200 OK\r\n" +
				"Server: HYSingleFileHttpServerNIO\r\n" +
				"Content-length: " + data.limit() + "\r\n" +
				"Content-type: " + MIMEType + "\r\n\r\n";
		byte[] headerData = header.getBytes(Charset.forName("UTF-8"));
		
		// 将响应行,响应头和响应体添加到缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(data.limit() + headerData.length);
		buffer.put(headerData);
		buffer.put(data);
		buffer.flip();
		this.contentBuffer = buffer;
	}
	
	// 执行监听通道
	public void run() throws IOException {
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		ServerSocket serverSocket = serverSocketChannel.socket();
		serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
		// 将通道设置为非阻塞
		serverSocketChannel.configureBlocking(false);
		
		// 将通道注册到选择器上面
		Selector selector = Selector.open();
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
	
		// 监听器开始监听
		while (true) {
			selector.select();
			// 获取选择器筛选到满足条件的通道的key的集合
			Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
			while (keys.hasNext()) {
				SelectionKey key = (SelectionKey) keys.next();
				// 删除迭代器当前指向的元素
				keys.remove();
				
				try {
					if (key.isAcceptable()) {
						// 服务器端通道时可接受状态,通过接收key获取服务器通道
						// 通过服务器端通道的accept方法,获取请求的客户端通道
						ServerSocketChannel server = (ServerSocketChannel)key.channel();
						SocketChannel client = server.accept();
						client.configureBlocking(false);
						client.register(selector, SelectionKey.OP_READ);
						
						
					} else if(key.isReadable()) { // 客户端通道准备好被读取
						SocketChannel client = (SocketChannel)key.channel();
						ByteBuffer buffer = ByteBuffer.allocate(4096);
						// 这里客户端一定要有数据传过来,不然服务器会一直阻塞
						client.read(buffer);
						// 可以在读取客户端的请求数据后,HTTP服务器对读取到数据缓冲区的数据进行解析.....
						// 这里省略解析客户端数据的过程
						
						// 将通道切换为只写模式(选择器会看key的通道是否满足写的条件)
						key.interestOps(SelectionKey.OP_WRITE);
						key.attach(contentBuffer.duplicate());
					} else if(key.isWritable()) {
						SocketChannel client = (SocketChannel)key.channel();
						ByteBuffer buffer = (ByteBuffer)key.attachment();
						if (buffer.hasRemaining()) {
							client.write(buffer);
						} else {
							client.close();
						}
					}
					
					
				} catch (Exception e) {
					// TODO: handle exception
					key.cancel();
					try {
						key.channel().close();
					} catch (Exception e2) {
						// TODO: handle exception
					}
				}
			}
		}
	}
	
	public static void main(String[] args) throws IOException {
		String contentType = URLConnection.getFileNameMap().getContentTypeFor("sources/index.html");
		File file = new File("sources/index.html");
		FileInputStream fileInputStream = new FileInputStream(file);
		byte[] data = new byte[4096];
		fileInputStream.read(data);
		
		ByteBuffer byteBuffer = ByteBuffer.wrap(data);
		
		HYSingleFileHttpServerNIO singleFileHttpServerNIO = new HYSingleFileHttpServerNIO(byteBuffer, 
				"UTF-8", contentType, 1000);
		singleFileHttpServerNIO.run();
	}
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档