- 浏览: 786727 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
萨琳娜啊:
Java读源码之Netty深入剖析网盘地址:https://p ...
Netty源码学习-FileRegion -
飞天奔月:
写得有趣 ^_^
那一年你定义了一个接口 -
GoldRoger:
第二个方法很好
java-判断一个自然数是否是某个数的平方。当然不能使用开方运算 -
bylijinnan:
<script>alert("close ...
自己动手实现Java Validation -
paul920531:
39行有个bug:"int j=new Random ...
java-蓄水池抽样-要求从N个元素中随机的抽取k个元素,其中N无法确定
今天看Netty如何实现一个Http Server
org.jboss.netty.example.http.file.HttpStaticFileServerPipelineFactory:
分析一下这三个ChannalHandler
HttpResponseEncoder比较简单,没什么可说的
按照Http协议规定的Response的格式
把org.jboss.netty.handler.codec.http.HttpResponse转为ChannelBuffer就可以了
注意添加必要的CR(Carriage Return)和LF(Line Feed)
HttpRequestDecoder
如何解析HttpRequest?很容易想到要用ReplayingDecoder:
所以 HttpRequestDecoder extends ReplayingDecoder
根据HttpRequest的消息结构,定义State:
后面的几个定义与“http chunk”有关,稍后再分析
简单地看一看decode方法:
很好理解。ReplayingDecoder使用时,“可以认为”数据已经接收完整,因此,按行读入并解析就可以了
那如何“断行”呢?别忘了,HttpResponse时会写入CRLF,因此readLine时,遇到CRLF就表示读到行尾了
真正的难题在HttpChunkAggregator
这个ChannelHandler有什么用?
首先看看“http chunk”是什么
当Server返回的HttpResponse是动态生成,无法“一开始”就确定Content-Length时,
可以采用“http chunk”
举例:
一般形式的HttpResponse:
对应的“http chunk”形式HttpResponse:
简单地说,“http chunk”就是
1.在header加入“Transfer-Encoding: chunked”,
2.把body分成多段(每段的格式是:字节流的长度+CRLF+字节流+CRLF)
长度为0的段表示结束
注意表示长度的数字是十六进制,计算长度时不包括末尾的CRLF
详见《HTTP权威指南》
HttpChunkAggregator的作用就是把“http chunk”形式转为一般形式
如何实现?
与“http chunk”对着干就可以了:
1.把“Transfer-Encoding: chunked”去掉
2.把分段的数据组合成一段
从上面r的if-else判断也可看到,HttpChunkAggregator是把“http chunk”分成两大部分来decode的:
HttpMessage和HttpChunk
这两部分的分割是谁来做?当然是HttpRequestDecoder
这就是为什么API里面会说要把HttpChunkAggregator放在HttpRequestDecoder的后面
回头细看一下HttpRequestDecoder的decode方法:
可以看到,HttpRequestDecoder把接收到的每一个分段数据组装成一个HttpChunk返回给
下一个Handler(这里是HttpChunkAggregator)处理
所以HttpChunkAggregator要把多个HttpChunk读取完毕后再组装在一起
另外HttpChunkAggregator组装时如果发现content-length超过maxContentLength,
就会抛TooLongFrameException
另外,上面还提到了http的100-continue:
有时Client发送数据前会先发送Expect: 100-continue,如果Server愿意接受数据,则返回类似如下的响应:
这是两个HttpResponse
第二个才是真实的、响应的数据,Client可以直接忽略第一个
org.jboss.netty.example.http.file.HttpStaticFileServerPipelineFactory:
pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("aggregator", new HttpChunkAggregator(65536)); pipeline.addLast("encoder", new HttpResponseEncoder());
分析一下这三个ChannalHandler
HttpResponseEncoder比较简单,没什么可说的
按照Http协议规定的Response的格式
把org.jboss.netty.handler.codec.http.HttpResponse转为ChannelBuffer就可以了
注意添加必要的CR(Carriage Return)和LF(Line Feed)
HttpRequestDecoder
如何解析HttpRequest?很容易想到要用ReplayingDecoder:
所以 HttpRequestDecoder extends ReplayingDecoder
根据HttpRequest的消息结构,定义State:
protected static enum State { SKIP_CONTROL_CHARS, READ_INITIAL, //请求行 READ_HEADER, //请求头 READ_VARIABLE_LENGTH_CONTENT, READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS, READ_FIXED_LENGTH_CONTENT, //请求主体 READ_FIXED_LENGTH_CONTENT_AS_CHUNKS, READ_CHUNK_SIZE, READ_CHUNKED_CONTENT, READ_CHUNKED_CONTENT_AS_CHUNKS, READ_CHUNK_DELIMITER, READ_CHUNK_FOOTER; }
后面的几个定义与“http chunk”有关,稍后再分析
简单地看一看decode方法:
protected Object decode(...) { switch (state) { //... case READ_INITIAL: { String[] initialLine = splitInitialLine(readLine(buffer, maxInitialLineLength)); message = createMessage(initialLine); checkpoint(State.READ_HEADER); } case READ_HEADER: { State nextState = readHeaders(buffer); checkpoint(nextState); } } //... } private State readHeaders(ChannelBuffer buffer) throws TooLongFrameException { final HttpMessage message = this.message; String line = readHeader(buffer); String name = null; String value = null; //... message.addHeader(name, value); }
很好理解。ReplayingDecoder使用时,“可以认为”数据已经接收完整,因此,按行读入并解析就可以了
那如何“断行”呢?别忘了,HttpResponse时会写入CRLF,因此readLine时,遇到CRLF就表示读到行尾了
真正的难题在HttpChunkAggregator
这个ChannelHandler有什么用?
首先看看“http chunk”是什么
当Server返回的HttpResponse是动态生成,无法“一开始”就确定Content-Length时,
可以采用“http chunk”
举例:
一般形式的HttpResponse:
HTTP/1.1 200 OK Date: Mon, 22 Mar 2004 11:15:03 GMT Content-Type: text/html Content-Length: 129 Expires: Sat, 27 Mar 2004 21:12:00 GMT <html><body><p>The file you requested is 3,400 bytes long and was last modified: Sat, 20 Mar 2004 21:12:00 GMT.</p></body></html>
对应的“http chunk”形式HttpResponse:
HTTP/1.1 200 OK Date: Mon, 22 Mar 2004 11:15:03 GMT Content-Type: text/html Transfer-Encoding: chunked Trailer: Expires 29 <html><body><p>The file you requested is 5 3,400 23 bytes long and was last modified: 1d Sat, 20 Mar 2004 21:12:00 GMT 13 .</p></body></html> 0 Expires: Sat, 27 Mar 2004 21:12:00 GMT
简单地说,“http chunk”就是
1.在header加入“Transfer-Encoding: chunked”,
2.把body分成多段(每段的格式是:字节流的长度+CRLF+字节流+CRLF)
长度为0的段表示结束
注意表示长度的数字是十六进制,计算长度时不包括末尾的CRLF
详见《HTTP权威指南》
HttpChunkAggregator的作用就是把“http chunk”形式转为一般形式
如何实现?
与“http chunk”对着干就可以了:
1.把“Transfer-Encoding: chunked”去掉
2.把分段的数据组合成一段
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object msg = e.getMessage(); HttpMessage currentMessage = this.currentMessage; if (msg instanceof HttpMessage) { HttpMessage m = (HttpMessage) msg; //处理Expect: 100-continue请求 if (is100ContinueExpected(m)) { write(ctx, succeededFuture(ctx.getChannel()), CONTINUE.duplicate()); } if (m.isChunked()) { //移除 'Transfer-Encoding' List<String> encodings = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); encodings.remove(HttpHeaders.Values.CHUNKED); if (encodings.isEmpty()) { m.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); } m.setChunked(false); this.currentMessage = m; } else { // Not a chunked message - pass through. this.currentMessage = null; ctx.sendUpstream(e); } } else if (msg instanceof HttpChunk) { HttpChunk chunk = (HttpChunk) msg; ChannelBuffer content = currentMessage.getContent(); appendToCumulation(chunk.getContent()); if (chunk.isLast()) { this.currentMessage = null; //“http chunk”有时候会把一个“拖挂”(例如上面例子里面的Expires)放到最后 //要读取这个值并设置header if (chunk instanceof HttpChunkTrailer) { HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; for (Entry<String, String> header: trailer.getHeaders()) { currentMessage.setHeader(header.getKey(), header.getValue()); } } //设置Content-Length,这个值就是各段大小的总和 currentMessage.setHeader( HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(content.readableBytes())); Channels.fireMessageReceived(ctx, currentMessage, e.getRemoteAddress()); } } else { ctx.sendUpstream(e); } }
从上面r的if-else判断也可看到,HttpChunkAggregator是把“http chunk”分成两大部分来decode的:
HttpMessage和HttpChunk
这两部分的分割是谁来做?当然是HttpRequestDecoder
这就是为什么API里面会说要把HttpChunkAggregator放在HttpRequestDecoder的后面
回头细看一下HttpRequestDecoder的decode方法:
case READ_HEADER: { State nextState = readHeaders(buffer); checkpoint(nextState); if (nextState == State.READ_CHUNK_SIZE) { // Chunked encoding message.setChunked(true); // Generate HttpMessage first. HttpChunks will follow. return message; } case READ_CHUNK_SIZE: { String line = readLine(buffer, maxInitialLineLength); int chunkSize = getChunkSize(line); this.chunkSize = chunkSize; //... checkpoint(State.READ_CHUNKED_CONTENT); } case READ_CHUNKED_CONTENT: { HttpChunk chunk = new DefaultHttpChunk(buffer.readBytes((int) chunkSize)); checkpoint(State.READ_CHUNK_DELIMITER); return chunk; }
可以看到,HttpRequestDecoder把接收到的每一个分段数据组装成一个HttpChunk返回给
下一个Handler(这里是HttpChunkAggregator)处理
所以HttpChunkAggregator要把多个HttpChunk读取完毕后再组装在一起
另外HttpChunkAggregator组装时如果发现content-length超过maxContentLength,
就会抛TooLongFrameException
另外,上面还提到了http的100-continue:
有时Client发送数据前会先发送Expect: 100-continue,如果Server愿意接受数据,则返回类似如下的响应:
HTTP/1.1 100 Continue HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Content-Length: 42 some-footer: some-value another-footer: another-value abcdefghijklmnoprstuvwxyz1234567890abcdef
这是两个HttpResponse
第二个才是真实的、响应的数据,Client可以直接忽略第一个
发表评论
-
TCP的TIME-WAIT
2014-04-23 16:35 1197原文连接:http://vincent.bernat.im/e ... -
《TCPIP详解卷1》学习-拥塞避免
2014-01-15 15:16 159拥塞避免算法、 ... -
Netty源码学习-HTTP-tunnel
2014-01-14 18:19 4300Netty关于HTTP tunnel的说明: http://d ... -
Netty源码学习-FileRegion
2013-12-31 17:17 5654今天看org.jboss.netty.example.http ... -
Netty源码学习-ReadTimeoutHandler
2013-12-26 17:53 3836ReadTimeoutHandler的实现思 ... -
Netty学习笔记
2013-12-25 18:39 1488本文是阅读以下两篇文章时: http://seeallhear ... -
Netty源码学习-ChannelHandler
2013-12-25 18:12 1634一般来说,“有状态”的ChannelHandler不应 ... -
Netty源码学习-ServerBootstrap启动及事件处理过程
2013-12-19 20:11 10763Netty是采用了Reactor模式的多线程版本,建议先看下面 ... -
Netty源码学习-Java-NIO-Reactor
2013-12-19 18:21 4891Netty里面采用了NIO-based Reactor Pat ... -
Netty源码学习-ReplayingDecoder
2013-12-13 20:21 4265ReplayingDecoder是FrameDecoder的子 ... -
Netty源码学习-DefaultChannelPipeline2
2013-12-11 15:47 1284Netty3的API http://docs.jboss.or ... -
Netty源码学习-CompositeChannelBuffer
2013-12-06 15:54 2761CompositeChannelBuffer体现了Netty的 ... -
Netty源码学习-DelimiterBasedFrameDecoder
2013-12-05 18:36 9559看DelimiterBasedFrameDecoder的AP ... -
Netty源码学习-ObjectEncoder和ObjectDecoder
2013-12-05 16:06 5004Netty中传递对象的思路很直观: Netty中数据的传递是基 ... -
Netty源码学习-LengthFieldBasedFrameDecoder
2013-12-05 15:20 7306先看看LengthFieldBasedFrameDecoder ... -
Netty源码学习-FrameDecoder
2013-11-28 18:38 3929Netty 3.x的user guide里FrameDecod ... -
Netty源码学习-DefaultChannelPipeline
2013-11-27 17:00 2238package com.ljn.channel; /** ...
相关推荐
赠送jar包:netty-transport-native-unix-common-4.1.73.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.73....
赠送jar包:netty-codec-mqtt-4.1.73.Final.jar; 赠送原API文档:netty-codec-mqtt-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-mqtt-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:netty-resolver-dns-4.1.65.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
netty源码netty-netty-4.1.36.Final.rarnetty源码netty-netty-4.1.36.Final.rarnetty源码netty-netty-4.1.36.Final.rarnetty源码netty-netty-4.1.36.Final.rarnetty源码netty-netty-4.1.36.Final.rarnetty源码netty-...
赠送jar包:netty-codec-http2-4.1.74.Final.jar; 赠送原API文档:netty-codec-http2-4.1.74.Final-javadoc.jar; 赠送源代码:netty-codec-http2-4.1.74.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-transport-classes-epoll-4.1.74.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.74.Final-sources.jar;...
赠送jar包:netty-transport-native-unix-common-4.1.74.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.74....
赠送jar包:netty-codec-dns-4.1.65.Final.jar; 赠送原API文档:netty-codec-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-codec-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-dns-...
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:netty-codec-haproxy-4.1.73.Final.jar; 赠送原API文档:netty-codec-haproxy-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-haproxy-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-resolver-dns-4.1.65.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-transport-native-unix-common-4.1.68.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.68.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.68....
赠送jar包:netty-transport-rxtx-4.1.74.Final.jar; 赠送原API文档:netty-transport-rxtx-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-rxtx-4.1.74.Final-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:netty-codec-http2-4.1.73.Final.jar; 赠送原API文档:netty-codec-http2-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-http2-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-codec-redis-4.1.73.Final.jar; 赠送原API文档:netty-codec-redis-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-redis-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-codec-http-4.1.27.Final.jar; 赠送原API文档:netty-codec-http-4.1.27.Final-javadoc.jar; 赠送源代码:netty-codec-http-4.1.27.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...