前言
tomcat是常用的Web 應用服務器,目前國內有很多文章講解了tomcat架構,請求流程等,但是沒有如何解析http請求及如何解決TCP粘包拆包,所以這篇文章的目的就是介紹這塊內容,一下內容完全是個人查看tomcat nio 相關源碼來總結的,源碼版本9.0.30,歡迎提問,歡迎指出錯誤。
請求解析
參數在請求行時的請求形式
GET /myServlet?name=zhangsan HTTP/1.1
Connection: keep-alive
參數在請求體時的請求形式
POST /myServlet HTTP/1.1
Connection: keep-alive
name=zhangsan
中間有一個空行表示請求頭和請求體的分界。
解析請求行
以 GET /myServlet?name=zhangsan HTTP/1.1為例
將請求行按空格進行分割,將method(POST),requestURI(/myServlet ),protocol(HTTP/1.1 )存起來
將?之后的數據存入queryString(name=zhangsan)存起來。
一直遇見換行符解析結束。
解析請求頭
以這個為例
Content-Length: 13
Connection: keep-alive
name=zhangsan
解析過程很簡單,以":"進行分割,一直到讀取到一個只有換行符的空行,請求頭解析結束。
解析請求體
請求體解析是通過請求頭中的Content-Length來進行解析的,讀取Content-Length值中對應的字節數。
如何解決拆包粘包
知道了請求結果和解析流程,下面就介紹一下怎樣處理拆包粘包。
粘包
粘包的解決是非常簡單的,比如粘包后是這樣的數據。
POST /myServlet?name=liuhao HTTP/1.1
Content-Length: 13
name=zhangsan
POST /myServlet?name=liuhao HTTP/1.1
tomcat處理請求時根據Content-Length進行讀取,是不會讀到第二個請求的,如果沒有Content-Length的話也就沒有請求體,請求頭和下一個請求有空行,也不會讀取。
拆包
拆包的處理方式大致相同就是數據沒有讀取完成就等
請求行拆包
POST /myServlet?name=liuh
請求行拆包,請求行結束的標志是換行符,如果沒有收到換行符,表示請求行沒有解析完,這個時候會重新監聽讀事件(使用java nio selector),之前的數據會放到buffer中緩存。
請求頭拆包
POST /myServlet?name=liuhao HTTP/1.1
Content-Type:
請求頭拆包,請求頭結束的標志是空行,如果沒有只有換行符的空行,表示請求行沒有解析完,這個時候會重新監聽讀事件(使用java nio selector),之前的數據會放到buffer中緩存。
請求體拆包
POST /myServlet HTTP/1.1
Content-Length: 13
Connection: keep-alive
name=
請求體拆包,請求體結束的標志是數據讀取足夠的字節數,如果讀取不夠,會阻塞的讀取數據直到讀取成功或超時報錯。
源碼閱讀指南
將tomcat的源碼導入到IDE,然后從架構和原理來了解tomcat,之后可以通過閱讀其他博客來了解源碼,最后自己在IDE中查看相關源碼,如果看的吃力,可以debug來看,如果想看拆包解包的源碼,可以用postman發送一個完整的http請求,然后使用telnet來進行拆包粘包測試,也可以寫一個client來測試,不過有一點要注意,telnet每次回車時會將\n傳過去,需要在debug的時候去除,如果寫一個client的話也要注意這一點。