Java NIO的简单HTTP服务器实现

本文介绍了如何使用Java NIO构建一个简单的HTTP服务器,处理非阻塞的读写操作。通过Selector监听通道事件,实现了对客户端连接的接受、读取请求和发送响应。在读操作后切换到写模式,完成HTTP响应后关闭客户端连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java.nio (NIO stands for non-blocking I/O[1]) is a collection of Java programming language APIs that offer features for intensive I/O operations. It was introduced with the J2SE 1.4 release of Java by Sun Microsystems to complement an existing standard I/O. NIO was developed under the Java Community Process as JSR 51.[2] An extension to NIO that offers a new file system API, called NIO.2, was released with Java SE 7 ("Dolphin").[3]

NIO的模型

Java的传统IO操作是面向流的。而对流的读写是阻塞式的;也就是说,当一个线程调用流的read()或者write()方法时,该线程将被阻塞,直到成功读取或写入这些数据。

JDK1.4引入了新的Java IO库,被称为NIO(New I/O),支持阻塞式和非阻塞式良好两种形式。其非阻塞式模式下,允许一个线程在进行读操作时,总是读取当前可用的数据。当暂时没有数据可用时,就不执行,而不是阻塞等待。

NIO包括Channel(通道)、Buffer(缓冲区)、Selector(多路复用器)三大部分。

用JAVA NIO实现一个最简单的HTTP服务器。

不管浏览器请求什么,都展示Hello, world在浏览器中。

注意点:

(1).在处理ACCEPT事件后,只需要往Selector中注册READ操作。不需要注册WRITE操作,因为WRITE操作是随时都可用的。

(2).处理完读操作之后,要用SelectionKey.interestOps(SelectionKey.WRITE_OP)切换为写模式。

(3).处理完响应操作,才能关掉客户端的SocketChannel。

OP_WRITE is almost always ready. It rarely if ever makes sense to register for OP_READ and OP_WRITE at the same time.

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;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 一个最简单的HTTP服务器。 <br>
 * 不管浏览器请求什么,都返回Hello, world
 */
public class MyHttpServer {
	public static final int DEFAULT_PORT = 8087;

	public static final String CRLF = "\r\n";

	private final static Logger logger = LoggerFactory.getLogger(MyHttpServer.class);

	public void exeute() throws IOException {
		ServerSocketChannel serverChannel = null;
		Selector selector = null;
		try {
			serverChannel = ServerSocketChannel.open();
			serverChannel.bind(new InetSocketAddress(DEFAULT_PORT));
			serverChannel.configureBlocking(false);
			selector = Selector.open();
			// 只需要注册ACCEPT操作
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);
		} catch (IOException e) {
			e.printStackTrace();
		}

		while (true) {
			int n = selector.select();
			if (n < 1) {
				continue;
			}

			Set<SelectionKey> readyKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = readyKeys.iterator();
			while (iterator.hasNext()) {
				SelectionKey key = iterator.next();
				iterator.remove();

				try {
					if (key.isAcceptable()) {
						handleAccetable(key, selector);
					}
					if (key.isReadable()) {
						handleReadable(key);
						// 重要!! 将SelectionKey切换为写模式
						key.interestOps(SelectionKey.OP_WRITE);
					}
					if (key.isWritable()) {
						handleWritable(key);
						// 处理完响应操作,才能关掉客户端的SocketChannel
						key.channel().close();
					}
				} catch (Exception ex) {
					key.cancel();
				}
			}
		}
	}

	private void handleAccetable(SelectionKey key, Selector selector) throws IOException {
		ServerSocketChannel server = (ServerSocketChannel) key.channel();
		SocketChannel client = server.accept();
		InetSocketAddress socketAddress = (InetSocketAddress) client.getRemoteAddress();
		String ip = socketAddress.getAddress().getHostAddress();
		logger.info("Accepted connection from " + ip + ":" + socketAddress.getPort());
		client.configureBlocking(false);
		// 只需要注册READ操作
		SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
		ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		clientKey.attach(byteBuffer);
	}

	private void handleReadable(SelectionKey key) throws IOException {
		logger.info("handleReadable");

		SocketChannel client = (SocketChannel) key.channel();
		ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
		byteBuffer.rewind();
		int n = client.read(byteBuffer);
		if (n < 1) {
			return;
		}
		// 打印HTTP请求报文出来
		byteBuffer.flip();
		byte[] array = byteBuffer.array();
		String str = new String(array, 0, n);
		logger.info("Receive request from client:----------------");
		logger.info(str);
	}

	private void handleWritable(SelectionKey key) throws IOException {
		logger.info("handleWritable");
		SocketChannel client = (SocketChannel) key.channel();
		client.configureBlocking(false);
		ByteBuffer byteBuffer = (ByteBuffer) key.attachment();

		String contenxt = "Hello, world";
		StringBuilder sb = new StringBuilder();
		sb.append("HTTP/1.1 200 OK");
		sb.append(CRLF);
		sb.append("Date:Sun, 21 Apr 2013 15:12:46 GMT");
		sb.append(CRLF);
		sb.append("Server:Apache");
		sb.append(CRLF);
		sb.append("Connection:Close");
		sb.append(CRLF);
		sb.append("Content-Type:text/plain; charset=ISO-8859-1");
		sb.append(CRLF);
		sb.append("Content-Length: " + contenxt.length());
		sb.append(CRLF);
		sb.append(CRLF);
		sb.append(contenxt); // 内容为Hello, world
		sb.append(CRLF);
		String str2 = sb.toString();
		byteBuffer.put(str2.getBytes());
		byteBuffer.flip();
		// 输出HTTP响应报文
		client.write(byteBuffer);
	}

	public static void main(String[] args) throws IOException {
		new MyHttpServer().exeute();
	}
}

效果图

参考:

[1] sockets - Java nio SelectionKey.register and interestops - Stack Overflow

[2] 基于 Java NIO 实现简单的 HTTP 服务器 - 云+社区 - 腾讯云

[3] Java NIO | JAVACORE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值