看透SpringMVC系列(二)用NIO自己手动实现HTTP协议

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27605885/article/details/79561219

我们知道HTTP协议是在应用层解析内容的,只需要按照它的报文格式封装和解析数据就可以了,具体的传输还是使用的Socket。

因为HTTP协议是在接受到数据之后才会用到的:代码

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
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 HttpServer {

	public static void main(String[] args) throws Exception{
		ServerSocketChannel ssc =ServerSocketChannel.open();
		ssc.socket().bind(new InetSocketAddress(8080));
		ssc.configureBlocking(false);//非阻塞
		//为ssc注册选择器
		Selector selector = Selector.open();
		ssc.register(selector, SelectionKey.OP_ACCEPT);
		while(true){
			if(selector.select(3000) == 0){
				continue;
			}
			
			Iterator<SelectionKey> it = selector.selectedKeys().iterator();
			while (it.hasNext()) {
				SelectionKey key = it.next();
				//启动新线程处理SelectionKey
				new Thread(new HttpHandler(key)).run();
				it.remove();
			}
		}

	}
	private static class HttpHandler implements Runnable{
		private int bufferSize = 1024;
		private String localChatset ="UTF-8";
		SelectionKey key;
		public HttpHandler(SelectionKey key) {
			this.key = key;
		}
		public void handleAccept() throws IOException{
			SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
			channel.configureBlocking(false);
			channel.register(key.selector(), SelectionKey.OP_READ, 
					ByteBuffer.allocate(bufferSize));
		}
		
		public void handleRead() throws IOException{
			SocketChannel channel = (SocketChannel) key.channel();
			ByteBuffer buffer = (ByteBuffer) key.attachment();
			buffer.clear();
			if(channel.read(buffer) == -1){
				channel.close();
			}else{
				//将buffer转换为读状态
				buffer.flip();
				String receiveMsg = Charset.forName(localChatset).newDecoder().decode(buffer).toString();
				System.out.println("收到数据:" + receiveMsg);
				
				//返回数据给客户端
				StringBuilder sendMsg = new StringBuilder();
				sendMsg.append("HTTP/1.1 200 OK\r\n");
				sendMsg.append("content-type:text/html;charset=" + localChatset
						+ "\r\n");
				sendMsg.append("\r\n");
				sendMsg.append("现在内容将呈现在浏览器中");//响应体
				
				
				ByteBuffer buffer2 = ByteBuffer.wrap(sendMsg.toString().getBytes());
				channel.write(buffer2);
				channel.close();
			}
		}
		@Override
		public void run() {
			try {
				if(key.isAcceptable()){
					handleAccept();
				}
				if(key.isReadable()){
					handleRead();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			
		}
	}

}

浏览器



控制台输出

收到数据:GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.1704055086.1517401277

这里也只是一个简单的事例,目的是了解HTTP协议的实现的方法,这里的功能还不够完善,它不能真正处理请求,实际处理中应该根据不同的url和不同的请求方法进行不同的处理并返回不同的响应报文,另外这里的请求报文也必须在1024范围内,如果太长就会接收不全,而且也不能返回图片等流类型的数据(流类型只需要在响应报文中写清楚Content-Type的类型,并将相应的数据写入报文的主题就可以了),不过对于了解HTTP协议实现的方法以及够用了


猜你喜欢

转载自blog.csdn.net/qq_27605885/article/details/79561219