Mina、Netty、Twisted一起学(八):HTTP服务器

释放双眼,带上耳机,听听看~!

HTTP协议应该是目前使用最多的应用层协议了,用浏览器打开一个网站就是使用HTTP协议进行数据传输。

HTTP协议也是基于TCP协议,所以也有服务器和客户端。HTTP客户端一般是浏览器,当然还有可能是其他东西。HTTP服务器,也就是Web服务器,目前已经有很多成熟的产品,例如Apache HTTP Server、Tomcat、Nginx、IIS等。

本文的内容不是讲解如何使用以上的HTTP服务器,而是要分别用MINA、Netty、Twisted实现一个简单的HTTP服务器。

首先,要简单了解一下HTTP协议。

HTTP协议是请求/响应式的协议,客户端需要发送一个请求,服务器才会返回响应内容。例如在浏览器上输入一个网址按下Enter,或者提交一个Form表单,浏览器就会发送一个请求到服务器,而打开的网页的内容,就是服务器返回的响应。

下面了解一下HTTP请求和响应包含的内容。

HTTP请求有很多种method,最常用的就是GET和POST,每种method的请求之间会有细微的区别。下面分别分析一下GET和POST请求。

GET请求:

下面是浏览器对http://localhost:8081/test?name=XXG&age=23的GET请求时发送给服务器的数据:

Mina、Netty、Twisted一起学(八):HTTP服务器

可以看出请求包含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:

Mina、Netty、Twisted一起学(八):HTTP服务器

可以看出,上面的请求包含三个部分: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响应:

Mina、Netty、Twisted一起学(八):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、Netty、Twisted一起学(八):HTTP服务器

**MINA:
**

MINA中有一个mina-http-2.0.7.jar包,专门用于处理HTTP协议。在下面的代码中,需要将这个jar包引入到项目中。

HTTP协议的请求解码器和响应编码器即HttpServerCodec,它会将HTTP客户端请求转成HttpRequest对象,将HttpResponse对象编码成HTTP响应发送给客户端。需要注意的是,HttpRequest和HttpResponse的实现类对象都没有包含message body部分,所以下面代码中body还通过原始的IoBuffer类型来构造。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
1public class HttpServer {
2
3    public static void main(String[] args) throws IOException {
4        IoAcceptor acceptor = new NioSocketAcceptor();
5        acceptor.getFilterChain().addLast("codec", new HttpServerCodec());
6        acceptor.setHandler(new HttpServerHandle());
7        acceptor.bind(new InetSocketAddress(8080));
8    }
9}
10
11class HttpServerHandle extends IoHandlerAdapter {
12    
13    @Override
14    public void exceptionCaught(IoSession session, Throwable cause)
15            throws Exception {
16        cause.printStackTrace();
17    }
18
19    @Override
20    public void messageReceived(IoSession session, Object message)
21            throws Exception {
22        
23        if (message instanceof HttpRequest) {
24            
25            // 请求,解码器将请求转换成HttpRequest对象
26            HttpRequest request = (HttpRequest) message;
27            
28            // 获取请求参数
29            String name = request.getParameter("name");
30            name = URLDecoder.decode(name, "UTF-8");
31
32            // 响应HTML
33            String responseHtml = "<html><body>Hello, " + name + "</body></html>";
34            byte[] responseBytes = responseHtml.getBytes("UTF-8");
35            int contentLength = responseBytes.length;
36            
37            // 构造HttpResponse对象,HttpResponse只包含响应的status line和header部分
38            Map<String, String> headers = new HashMap<String, String>();
39            headers.put("Content-Type", "text/html; charset=utf-8");
40            headers.put("Content-Length", Integer.toString(contentLength));
41            HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SUCCESS_OK, headers);
42            
43            // 响应BODY
44            IoBuffer responseIoBuffer = IoBuffer.allocate(contentLength);
45            responseIoBuffer.put(responseBytes);
46            responseIoBuffer.flip();
47            
48            session.write(response); // 响应的status line和header部分
49            session.write(responseIoBuffer); // 响应body部分
50        }
51    }
52}
53

Netty:

Netty和MINA非常类似。唯一有区别的地方就是FullHttpResponse包含响应的message body。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
1public class HttpServer {
2
3    public static void main(String[] args) throws InterruptedException {
4        EventLoopGroup bossGroup = new NioEventLoopGroup();
5        EventLoopGroup workerGroup = new NioEventLoopGroup();
6        try {
7            ServerBootstrap b = new ServerBootstrap();
8            b.group(bossGroup, workerGroup)
9                    .channel(NioServerSocketChannel.class)
10                    .childHandler(new ChannelInitializer<SocketChannel>() {
11                        @Override
12                        public void initChannel(SocketChannel ch) throws Exception {
13                            ChannelPipeline pipeline = ch.pipeline();
14                            pipeline.addLast(new HttpServerCodec());
15                            pipeline.addLast(new HttpServerHandler());
16                        }
17                    });
18            ChannelFuture f = b.bind(8080).sync();
19            f.channel().closeFuture().sync();
20        } finally {
21            workerGroup.shutdownGracefully();
22            bossGroup.shutdownGracefully();
23        }
24    }
25}
26
27class HttpServerHandler extends ChannelInboundHandlerAdapter {
28
29    @Override
30    public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
31        
32        if (msg instanceof HttpRequest) {
33            
34            // 请求,解码器将请求转换成HttpRequest对象
35            HttpRequest request = (HttpRequest) msg;
36            
37            // 获取请求参数
38            QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
39            String name = queryStringDecoder.parameters().get("name").get(0);
40                        
41            // 响应HTML
42            String responseHtml = "<html><body>Hello, " + name + "</body></html>";
43            byte[] responseBytes = responseHtml.getBytes("UTF-8");
44            int contentLength = responseBytes.length;
45            
46            // 构造FullHttpResponse对象,FullHttpResponse包含message body
47            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(responseBytes));
48            response.headers().set("Content-Type", "text/html; charset=utf-8");
49            response.headers().set("Content-Length", Integer.toString(contentLength));
50
51            ctx.writeAndFlush(response);
52        }
53    }
54
55    @Override
56    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
57        cause.printStackTrace();
58        ctx.close();
59    }
60}
61

Twisted:

Twisted的HTTP相比MINA、Netty来说功能最完善。Twisted不但包含HTTP协议的编码器和解码器以及相关API,还提供了一整套Web应用解决方案。想完整学习的话可以参考官方文档。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1# -*- coding:utf-8 –*-
2
3from twisted.web import server, resource
4from twisted.internet import reactor
5
6class MainResource(resource.Resource):
7    
8    isLeaf = True
9    
10    # 用于处理GET类型请求
11    def render_GET(self, request):
12        
13        # name参数
14        name = request.args['name'][0]
15        
16        # 设置响应编码
17        request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8")
18                
19        # 响应的内容直接返回
20        return "<html><body>Hello, " + name + "</body></html>"
21
22
23site = server.Site(MainResource())
24reactor.listenTCP(8080, site)
25reactor.run()
26

 

MINA、Netty、Twisted一起学系列

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

源码

https://github.com/wucao/mina-netty-twisted

给TA打赏
共{{data.count}}人
人已打赏
安全技术

用node.js从零开始去写一个简单的爬虫

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索