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请求时发送给服务器的数据:

可以看出请求包含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一起学系列

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

Mina、Netty、Twisted一起学(八):HTTP服务器的更多相关文章

  1. Mina、Netty、Twisted一起学(一):实现简单的TCP服务器

    MINA.Netty.Twisted为什么放在一起学习?首先,不妨先分别看一下它们官方网站对其的介绍: MINA: Apache MINA is a network application frame ...

  2. Mina、Netty、Twisted一起学(十):线程模型

    要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...

  3. Mina、Netty、Twisted一起学(九):异步IO和回调函数

    用过JavaScript或者jQuery的同学都知道,JavaScript特别是jQuery中存在大量的回调函数,例如Ajax.jQuery的动画等. $.get(url, function() { ...

  4. Mina、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)

    消息传递有很多种方式,请求/响应(Request/Reply)是最常用的.在前面的博文的例子中,很多都是采用请求/响应的方式,当服务器接收到消息后,会立即write回写一条消息到客户端.HTTP协议也 ...

  5. Mina、Netty、Twisted一起学(六):session

    开发过Web应用的同学应该都会使用session.由于HTTP协议本身是无状态的,所以一个客户端多次访问这个web应用的多个页面,服务器无法判断多次访问的客户端是否是同一个客户端.有了session就 ...

  6. Mina、Netty、Twisted一起学(五):整合protobuf

    protobuf是谷歌的Protocol Buffers的简称,用于结构化数据和字节码之间互相转换(序列化.反序列化),一般应用于网络传输,可支持多种编程语言. protobuf如何使用这里不再介绍, ...

  7. Mina、Netty、Twisted一起学(三):TCP消息固定大小的前缀(Header)

    在上一篇博文中,有介绍到用换行符分割消息的方法.但是这种方法有个小问题,如果消息中本身就包含换行符,那将会将这条消息分割成两条,结果就不对了. 本文介绍另外一种消息分割方式,即上一篇博文中讲的第2条: ...

  8. Mina、Netty、Twisted一起学(四):定制自己的协议

    在前面的博文中,介绍一些消息分割的方案,以及MINA.Netty.Twisted针对这些方案提供的相关API.例如MINA的TextLineCodecFactory.PrefixedStringCod ...

  9. Mina、Netty、Twisted一起学(二):TCP消息边界问题及按行分割消息

    在TCP连接开始到结束连接,之间可能会多次传输数据,也就是服务器和客户端之间可能会在连接过程中互相传输多条消息.理想状况是一方每发送一条消息,另一方就立即接收到一条,也就是一次write对应一次rea ...

随机推荐

  1. 简单入门canvas - 通过刮奖效果来学习

    一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...

  2. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  3. Code Review 程序员的寄望与哀伤

    一个程序员,他写完了代码,在测试环境通过了测试,然后他把它发布到了线上生产环境,但很快就发现在生产环境上出了问题,有潜在的 bug. 事后分析,是生产环境的一些微妙差异,使得这种 bug 场景在线下测 ...

  4. JAVA回调机制(CallBack)详解

    序言 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBack做了一个定义.当然了,我在理解了回 ...

  5. UML课程复习重点

    第一章 一.UML图示建模工具 二.UML--统一建模语言,以图形符号为基础,描述软件模型既简洁又清晰.它不是开发方法,是独立于任何开发方法之外的语言.它用于描述软件系统分析.设计和实施中的各种模型. ...

  6. app引导页(背景图片切换加各个页面动画效果)

    前言:不知不觉中又加班到了10点半,整个启动页面做了一天多的时间,一共有三个页面,每个页面都有动画效果,动画效果调试起来麻烦,既要跟ios统一,又要匹配各种不同的手机,然后产品经理还有可能在中途改需求 ...

  7. UVA, 10336 Rank the Languages

    难点在于:递归函数和输出: #include <iostream> #include <vector> #include <algorithm> #include ...

  8. TCP/IP基础

    TCP/IP 是用于因特网 (Internet) 的通信协议. 计算机通信协议是对那些计算机必须遵守以便彼此通信的规则的描述. 什么是 TCP/IP? TCP/IP 是供已连接因特网的计算机进行通信的 ...

  9. iOS之开发中一些相关的路径以及获取路径的方法

    模拟器的位置: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs ...

  10. Android Weekly Notes Issue #236

    Android Weekly Issue #236 December 18th, 2016 Android Weekly Issue #236 本期内容包括: Google的物联网平台Android ...