java實現一個最簡單的tomcat服務


 

在了解tomcat的基本原理之前,首先要了解tomcatt最基本的運行原理。

 

1.如何啟動?

main方法是程序的入口,tomcat也不例外,查看tomcat源碼,發現main是在Bootstrap 類中的;

 

2.如何建立連接?

 

要通訊,必須要建議socket連接,我們需要使用哪種socket,是根據它使用的哪種協議進行判斷的。tcp協議or udp協議?http協議本身屬於tcp協議,因此我們選擇的socket是基本tcp協議的socket。在tomcat中,StandardServer 中 await() 方法具體實現了 socket連接;

 

3.使用哪種io模式?

  • BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高;
  • NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器;
  • AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器
  • tomcat 使用了aio,bio、nio三種io模式,它們不同的應用各自發揮其優點。這里通過bio簡單實現一個tomcat服務。

代碼示例:

package com.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class ChannelSocketTest {
    public void start() throws IOException {
        
        // 1.新建NIO通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); // 設置為非阻塞狀態
        ServerSocket socket = ssc.socket();
        System.out.println("啟動web服務");
        socket.bind(new InetSocketAddress(8888));

        while (true) {
            SocketChannel channel = ssc.accept();
       if (channel!=null) Thread thread
= new Thread(new HttpServerThread(channel)); thread.start(); } } } // 內部類 private class HttpServerThread implements Runnable { SocketChannel channel; HttpServerThread(SocketChannel channel) { this.channel = channel; } @Override public void run() { if (channel != null) { try { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress(); // System.out.println(remoteAddress.getAddress()); // System.out.println(remoteAddress.getPort()); channel.read(byteBuffer); byteBuffer.flip(); while (byteBuffer.hasRemaining()) { char c = (char) byteBuffer.get(); System.out.print(c); } // 此處打印執行的線程名稱,永遠為 main 線程 System.out.println(Thread.currentThread().getName() + "開始向web返回消息。。。"); ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024); // 給客戶端一個響應,即向輸出流寫入信息 String reply = "HTTP/1.1\n"; // 必須添加的響應頭 reply += "Content-type:text/html\n\n"; // 必須添加的響應頭 reply += "服務器返回的消息"; byteBuffer2.put(new String(reply).getBytes()); byteBuffer2.flip(); channel.write(byteBuffer2); channel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws IOException { new ChannelSocketTest().start(); } }

 

 

 

在瀏覽器上輸入:http://localhost:8888/

控制台輸出:

第0行信息:Host: localhost:8888
第1行信息:Connection: keep-alive
第2行信息:Cache-Control: max-age=0
第3行信息:User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
第4行信息:Upgrade-Insecure-Requests: 1
第6行信息:Accept-Encoding: gzip, deflate, br
第5行信息:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
第8行信息:Cookie: JSESSIONID=F373E4FD1D4E6E57AB618563B796B909;
第7行信息:Accept-Language: zh-CN,zh;q=0.9
第9行信息:

注意:控制台上的輸出包含http請求頭信息,socket接收的流格式為字符類型,每一行都代表一種類型的信息,因此解析時需要逐行解析。之前使用BufferedReader的readLine( )方法,但是此方法是阻塞線程的,如果讀取不到,會一直處理等待狀態,因此配合ready( )方法一起使用。

上面代碼在瀏覽器請求時,后台會執行兩次,我們可以打開瀏覽器F12調試模式查看

發現有一個http請求,還有一個favicon.ico 圖片(瀏覽器窗口圖標)的請求。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM