HTTP协议报文结构
HTTP 协议是应用层协议,HTTP协议报文分为请求报文和响应报文两种类型,两者都包含三部分:首行、头部、主体。
-
HTTP 请求报文
-
请求行:GET / HTTP/1.1
表示请求的方法(请求类型)、路径、协议版本 -
请求头:Connection: keep-alive
-
请求体:GET的请求体为空
-
-
HTTP 响应报文
-
响应行:HTTP/1.1 200
-
响应头:content-type: text/html; charset=utf-8
-
响应体:主体内容
-
-
Socket实现HTTP协议:
package it.yus.socket;
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;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
*
* 描述:
* @author yusong
* @since 1.8
*/
public class HttpServer {
public static void main(String[] args) throws Exception {
// 创建 ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
// 配置为非阻塞
serverChannel.configureBlocking(false);
// 注册选择器
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
// 创建线程池
Executor ex = new ThreadPoolExecutor(8, 8, 500, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 创建处理器
while (true) {
// 每次等待0.5秒,0.5秒后返回的就绪通道为0则继续阻塞
if (selector.select(500) == 0) {
continue;
}
// 获取待处理的seletionKey
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
HttpHandler httpHandler = new HttpHandler(key);
ex.execute(httpHandler);
}
}
}
// 处理请求
private static class HttpHandler implements Runnable {
// TCP包长度
private int bufferSize = 1024;
// 编码字符集
private String charset = "UTF-8";
// 注册的事件
private SelectionKey key;
public HttpHandler(SelectionKey key) {
this.key = key;
}
// 处理ServerSocketChannel接受请求
public void handleAccept() throws Exception {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
// 接收的客户端的连接
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
// 注册到选择器
channel.register(key.selector(), SelectionKey.OP_READ,
ByteBuffer.allocate(bufferSize));
}
// socketChannel 读取数据
public void handleRead() throws Exception {
// 获取channel
SocketChannel channel = (SocketChannel) key.channel();
// 获取Buffer并重置
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
// 从通道获取内容
if (channel.read(buffer) == -1) {
channel.close();
} else {
// 接受请求数据
buffer.flip();
String reciveMsg = Charset.forName(charset).newDecoder().decode(buffer).toString();
String[] requestContent = reciveMsg.split("\r\n");
for (String line : requestContent) {
System.out.println(line);
if (line.isEmpty()) {
break;
}
}
// 返回客户端
StringBuilder sendMsg = new StringBuilder();
sendMsg.append("HTTP/1.1 200 OK\r\n");// 响应行
// 响应头
sendMsg.append("cache-control: private;\r\n")
.append("content-type: text/html; charset=utf-8\r\n")
.append("\r\n")
// 响应体
.append("<!DOCTYPE html><html lang=\"zh-cn\">")
.append("<head><meta charset=\"utf-8\"/><title>测试HttpServer</title></head>")
.append("<body><h3>服务端接收到的请求报文</h3>");
for (String line : requestContent) {
sendMsg.append(line+"</br>");
if (line.isEmpty()) {
break;
}
}
sendMsg.append("</body>");
buffer = ByteBuffer.wrap(sendMsg.toString().getBytes(charset));
// 发送
channel.write(buffer);
channel.close();
}
}
@Override
public void run() {
try {
if (key.isAcceptable()) {
handleAccept();
}
if (key.isReadable()) {
handleRead();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 控制台及浏览器打印结果: