HTTP協議應該是目前使用最多的應用層協議了,用瀏覽器打開一個網站就是使用HTTP協議進行數據傳輸。html
HTTP協議也是基於TCP協議,因此也有服務器和客戶端。HTTP客戶端通常是瀏覽器,固然還有多是其餘東西。HTTP服務器,也就是Web服務器,目前已經有不少成熟的產品,例如Apache HTTP Server、Tomcat、Nginx、IIS等。react
本文的內容不是講解如何使用以上的HTTP服務器,而是要分別用MINA、Netty、Twisted實現一個簡單的HTTP服務器。git
首先,要簡單瞭解一下HTTP協議。github
HTTP協議是請求/響應式的協議,客戶端須要發送一個請求,服務器纔會返回響應內容。例如在瀏覽器上輸入一個網址按下Enter,或者提交一個Form表單,瀏覽器就會發送一個請求到服務器,而打開的網頁的內容,就是服務器返回的響應。web
下面瞭解一下HTTP請求和響應包含的內容。瀏覽器
HTTP請求有不少種method,最經常使用的就是GET和POST,每種method的請求之間會有細微的區別。下面分別分析一下GET和POST請求。服務器
GET請求:session
下面是瀏覽器對http://localhost:8081/test?name=XXG&age=23的GET請求時發送給服務器的數據:app
能夠看出請求包含request line和header兩部分。其中request line中包含method(例如GET、POST)、request uri和protocol version三部分,三個部分之間以空格分開。request line和每一個header各佔一行,以換行符CRLF(即\r\n)分割。異步
POST請求:
下面是瀏覽器對http://localhost:8081/test的POST請求時發送給服務器的數據,一樣帶上參數name=XXG&age=23:
能夠看出,上面的請求包含三個部分:request line、header、message,比以前的GET請求多了一個message body,其中header和message body之間用一個空行分割。POST請求的參數不在URL中,而是在message body中,header中多了一項Content-Length用於表示message body的字節數,這樣服務器才能知道請求是否發送結束。這也就是GET請求和POST請求的主要區別。
HTTP響應和HTTP請求很是類似,HTTP響應包含三個部分:status line、header、massage body。其中status line包含protocol version、狀態碼(status code)、reason phrase三部分。狀態碼用於描述HTTP響應的狀態,例如200表示成功,404表示資源未找到,500表示服務器出錯。
HTTP響應:
在上面的HTTP響應中,Header中的Content-Length一樣用於表示message body的字節數。Content-Type表示message body的類型,一般瀏覽網頁其類型是HTML,固然還會有其餘類型,好比圖片、視頻等。
學習了HTTP協議後,那麼就能夠分別經過MINA、Netty、Twisted實現針對請求的解碼器和針對響應的編碼器來實現一個HTTP服務器。實際上HTTP協議的細節還有不少,本身實現起來沒那麼容易。不過,MINA、Netty、Twisted都已經提供了針對HTTP協議的編碼解碼器和一些實用的API。
下面分別用MINA、Netty、Twisted來實現一個HTTP服務器,用瀏覽器訪問:
http://localhost:8080/?name=叉叉哥
就能夠打開一個頁面,將參數顯示在頁面上:
MINA:
MINA中有一個mina-http-2.0.7.jar包,專門用於處理HTTP協議。在下面的代碼中,須要將這個jar包引入到項目中。
HTTP協議的請求解碼器和響應編碼器即HttpServerCodec,它會將HTTP客戶端請求轉成HttpRequest對象,將HttpResponse對象編碼成HTTP響應發送給客戶端。須要注意的是,HttpRequest和HttpResponse的實現類對象都沒有包含message body部分,因此下面代碼中body還經過原始的IoBuffer類型來構造。
public class HttpServer { public static void main(String[] args) throws IOException { IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("codec", new HttpServerCodec()); acceptor.setHandler(new HttpServerHandle()); acceptor.bind(new InetSocketAddress(8080)); } } class HttpServerHandle extends IoHandlerAdapter { @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { cause.printStackTrace(); } @Override public void messageReceived(IoSession session, Object message) throws Exception { if (message instanceof HttpRequest) { // 請求,解碼器將請求轉換成HttpRequest對象 HttpRequest request = (HttpRequest) message; // 獲取請求參數 String name = request.getParameter("name"); name = URLDecoder.decode(name, "UTF-8"); // 響應HTML String responseHtml = "<html><body>Hello, " + name + "</body></html>"; byte[] responseBytes = responseHtml.getBytes("UTF-8"); int contentLength = responseBytes.length; // 構造HttpResponse對象,HttpResponse只包含響應的status line和header部分 Map<String, String> headers = new HashMap<String, String>(); headers.put("Content-Type", "text/html; charset=utf-8"); headers.put("Content-Length", Integer.toString(contentLength)); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SUCCESS_OK, headers); // 響應BODY IoBuffer responseIoBuffer = IoBuffer.allocate(contentLength); responseIoBuffer.put(responseBytes); responseIoBuffer.flip(); session.write(response); // 響應的status line和header部分 session.write(responseIoBuffer); // 響應body部分 } } }
Netty:
Netty和MINA很是相似。惟一有區別的地方就是FullHttpResponse包含響應的message body。
public class HttpServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpServerHandler()); } }); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } class HttpServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException { if (msg instanceof HttpRequest) { // 請求,解碼器將請求轉換成HttpRequest對象 HttpRequest request = (HttpRequest) msg; // 獲取請求參數 QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri()); String name = queryStringDecoder.parameters().get("name").get(0); // 響應HTML String responseHtml = "<html><body>Hello, " + name + "</body></html>"; byte[] responseBytes = responseHtml.getBytes("UTF-8"); int contentLength = responseBytes.length; // 構造FullHttpResponse對象,FullHttpResponse包含message body FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseBytes)); response.headers().set("Content-Type", "text/html; charset=utf-8"); response.headers().set("Content-Length", Integer.toString(contentLength)); ctx.writeAndFlush(response); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
Twisted:
Twisted的HTTP相比MINA、Netty來講功能最完善。Twisted不但包含HTTP協議的編碼器和解碼器以及相關API,還提供了一整套Web應用解決方案。想完整學習的話能夠參考官方文檔。
# -*- coding:utf-8 –*- from twisted.web import server, resource from twisted.internet import reactor class MainResource(resource.Resource): isLeaf = True # 用於處理GET類型請求 def render_GET(self, request): # name參數 name = request.args['name'][0] # 設置響應編碼 request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8") # 響應的內容直接返回 return "<html><body>Hello, " + name + "</body></html>" site = server.Site(MainResource()) reactor.listenTCP(8080, site) reactor.run()
MINA、Netty、Twisted一塊兒學(一):實現簡單的TCP服務器
MINA、Netty、Twisted一塊兒學(二):TCP消息邊界問題及按行分割消息
MINA、Netty、Twisted一塊兒學(三):TCP消息固定大小的前綴(Header)
MINA、Netty、Twisted一塊兒學(四):定製本身的協議
MINA、Netty、Twisted一塊兒學(五):整合protobuf
MINA、Netty、Twisted一塊兒學(六):session
MINA、Netty、Twisted一塊兒學(七):發佈/訂閱(Publish/Subscribe)
MINA、Netty、Twisted一塊兒學(八):HTTP服務器
MINA、Netty、Twisted一塊兒學(九):異步IO和回調函數
MINA、Netty、Twisted一塊兒學(十):線程模型
MINA、Netty、Twisted一塊兒學(十一):SSL/TLS
MINA、Netty、Twisted一塊兒學(十二):HTTPS