手写HTTP协议
程序员文章站
2022-07-10 15:29:13
...
我们知道HTTP协议是在应用层解析内容的,只需要按照它的报文的格式封装和解析数据就可以了,具体的传输还是使用的Socket,在NioServer的基础上自己做一个简单的实现了HTTP协议的例子。
因为HTTP协议是在接收到数据之后才会用到的,所以我们只需要修改NioServer中的Handler就可以了,在修改后的HttpHandler中首先获取到请求报文并打印出报文的头部(包含首行)、请求的方法类型、Url和Http版本,最后将接收到的请求报文信息封装到响应报文的主体中返回给客户端。这里的HttpHandler使用了单独的线程来执行,而且把SelectionKey中操作类型的选择也放在了HttpHandler中,不过具体处理过程和前面的NioServer没有太大的区别,代码如下:
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.nio.charset.Charset;
import java.util.Iterator;
public class HttpServer {
public static void main(String[] args) throws Exception{
//创建ServerSocketChannel,监听8080端口
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){
// 等待请求,每次等待阻塞3s,超过3s后线程继续向下运行,如果传入0或者不传参数将一直阻塞
if(selector.select(3000)==0){
continue;
}
// 获取待处理的SelectionKey
Iterator<SelectionKey> keyIter=selector.selectedKeys().iterator();
while(keyIter.hasNext()){
SelectionKey key=keyIter.next();
// 启动新线程处理SelectionKey
new Thread(new HttpHandler(key)).run();
// 处理完后,从待处理的SelectionKey迭代器中移除当前所使用的key
keyIter.remove();
}
}
}
private static class HttpHandler implements Runnable{
private int bufferSize = 1024;
private String localCharset = "UTF-8";
private SelectionKey key;
public HttpHandler(SelectionKey key){
this.key = key;
}
public void handleAccept() throws IOException {
SocketChannel clientChannel=((ServerSocketChannel)key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
}
public void handleRead() throws IOException {
// 获取channel
SocketChannel sc=(SocketChannel)key.channel();
// 获取buffer并重置
ByteBuffer buffer=(ByteBuffer)key.attachment();
buffer.clear();
// 没有读到内容则关闭
if(sc.read(buffer)==-1){
sc.close();
} else {
// 接收请求数据
buffer.flip();
String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
// 控制台打印请求报文头
String[] requestMessage = receivedString.split("\r\n");
for(String s: requestMessage){
System.out.println(s);
// 遇到空行说明报文头已经打印完
if(s.isEmpty())
break;
}
// 控制台打印首行信息
String[] firstLine = requestMessage[0].split(" ");
System.out.println();
System.out.println("Method:\t"+firstLine[0]);
System.out.println("url:\t"+firstLine[1]);
System.out.println("HTTP Version:\t"+firstLine[2]);
System.out.println();
// 返回客户端
StringBuilder sendString = new StringBuilder();
sendString.append("HTTP/1.1 200 OK\r\n");//响应报文首行,200表示处理成功
sendString.append("Content-Type:text/html;charset=" + localCharset+"\r\n");
sendString.append("\r\n");// 报文头结束后加一个空行
sendString.append("<html><head><title>显示报文</title></head><body>");
sendString.append("接收到请求报文是:<br/>");
for(String s: requestMessage){
sendString.append(s + "<br/>");
}
sendString.append("</body></html>");
buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
sc.write(buffer);
sc.close();
}
}
@Override
public void run() {
try{
// 接收到连接请求时
if(key.isAcceptable()){
handleAccept();
}
// 读数据
if(key.isReadable()){
handleRead();
}
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
}