您的位置:首页 > 理论基础 > 计算机网络

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

2018-03-14 22:58 267 查看
我们知道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);

//返回数据给客户端
Strin
4000
gBuilder 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协议实现的方法以及够用了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: