用java.net.Socket来模拟实现Tomcat,碰到了一些坑,大部分是没有想到的,记录下来自查。
直接上代码,
public class TomcatDemo { private static ExecutorService executorService = Executors.newCachedThreadPool(); public static void main(String[] args) throws IOException { //监听9000端口 @SuppressWarnings("resource") ServerSocket serverSocket = new ServerSocket(9000); System.out.println("Tomcat服务启动成功!"); while (!serverSocket.isClosed()) { //阻塞式 Socket request = serverSocket.accept(); System.out.println(request.getInetAddress()); executorService.execute(() -> { try { InputStream inputStream = request.getInputStream(); System.out.println("收到请求..."); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"utf-8")); StringBuffer sb = new StringBuffer(); String line; while((line = br.readLine()) != null) { sb.append(line).append(" "); } System.out.println(sb.toString()); //由servlet处理业务逻辑 System.out.println("-----------------end"); //请求结束... OutputStream outputStream = request.getOutputStream(); outputStream.write("HTTP/1.1 200 OK ".getBytes()); outputStream.write(("Content-Length: " + "Hello World!".getBytes().length + " ").getBytes()); outputStream.write("Hello World!".getBytes()); outputStream.flush(); System.out.println("请求结束..."); } catch (IOException e) { e.printStackTrace(); } finally { try { request.close(); } catch (Exception e2) { e2.printStackTrace(); } } }); } } }
运行程序,使用Chrome访问localhost:9000
问题有两个,
(1) 竟然发起两次GET请求
(2) 出现socket write error
起初开始解决socket write error,大多数说是因为socket提前close或者超时导致的,但是发起的请求还没有结束。后来一步步调试,发现代码在 line = br.readLine()) != null 处阻塞等待了!
原来是br.readLine()的机制同自己想的并不一样,它是阻塞式的,叉掉http://localhost:9000/的请求后,后续代码才继续被执行。
增加如下判断代码解决,
while((line = br.readLine()) != null) { if (line.length() == 0){ break; } sb.append(line).append(" "); }
但是socket write error的问题还在,这个应该就和重复请求有关系了。
改用Firefox访问http://localhost:9000/,发现请求正常,
看来是Chrome的问题,遂查了下,应该是插件造成的请求重复发送,这也导致了如上socket write error的问题。
用Chrome访问还会出现请求/favicon.ico,这个是Chrome后台默默做的,用来显示和网站相关的信息。这也正是平时大家说的Chrome一次GET请求,出现两次的根源。