HTTP是基于TCP协议的。TCP负责数据传输,而HTTP只是规范了TCP传输的数据的格式,而这个具体的格式,请见后面给出的资料。
HTTP服务的底层实现就是socket编程。
下面基于socket编写一个简单的HTTP server。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
class SocketHandler implements Runnable {
final static String CRLF = "\r\n"; // 1
private Socket clientSocket;
public SocketHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void handleSocket(Socket clientSocket) throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream())
);
PrintWriter out = new PrintWriter(
new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream())),
true
);
String requestHeader = "";
String s;
while ((s = in.readLine()) != null) {
s += CRLF; // 2 很重要,默认情况下in.readLine的结果中`\r\n`被去掉了
requestHeader = requestHeader + s;
if (s.equals(CRLF)){ // 3 此处HTTP请求头我们都得到了;如果从请求头中判断有请求正文,则还需要继续获取数据
break;
}
}
System.out.println("客户端请求头:");
System.out.println(requestHeader);
String responseBody = "客户端的请求头是:\n"+requestHeader;
String responseHeader = "HTTP/1.0 200 OK\r\n" +
"Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Length: "+responseBody.getBytes().length+"\r\n" +
"\r\n";
// 4 问题来了:1、浏览器如何探测编码 2、浏览器受到content-length后会按照什么方式判断?汉字的个数?字节数?
System.out.println("响应头:");
System.out.println(responseHeader);
out.write(responseHeader);
out.write(responseBody);
out.flush();
out.close();
in.close();
clientSocket.close();
}
@Override
public void run() {
try {
handleSocket(clientSocket);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
public class MyHTTPServer {
public static void main(String[] args) throws Exception {
int port = 8000;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("启动服务,绑定端口: " + port);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(30); // 5
while (true) { // 6
Socket clientSocket = serverSocket.accept();
System.out.println("新的连接"
+ clientSocket.getInetAddress() + ":" + clientSocket.getPort());
try {
fixedThreadPool.execute(new SocketHandler(clientSocket));
} catch (Exception e) {
System.out.println(e);
}
}
}
}
这是一个实现HTTP 1.0的服务器,对于所有的HTTP请求,会把HTTP请求头响应回去。 这个程序说明了web服务器处理请求的基本流程,JSP、Servlet、Spring MVC等只是在 这个基础上嫁了许多方法,以让我们更方面的编写web应用。web服务器不仅可以基于多线程, 也可以基于多进程、Reactor模型等。
测试程序:
运行上面的程序。我们使用curl访问http://127.0.0.1
(也可以使用浏览器):
$ curl -i http://127.0.0.1:8000
HTTP/1.0 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 106
客户端的请求头是:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: 127.0.0.1:8000
Accept: */*
Java程序输出: