解決用netty去做web服務時,post長度過大的問題

原文地址

http://my.oschina.net/momohuang/blog/114552


先說一下,本來是想自己寫socket ,啓動一個簡單點的web服務用於接收數據的。寫完之後,發現會有各種情況沒有考慮到的,很有可能出現問題,而且,太折騰了。於是,就用了netty去其web服務,另外,我也覺得netty基本上是最簡單的web服務了吧,如果童鞋們有其他推薦的話,就留個言唄。


1、server

public class AdminServer {
 protected static final Log log = LogFactory.getLog(AdminServer.class);
 public static void main(String[] args) {
 log.info("start app");
 start(8088);
//		System.out.println("admin start on "+1);
 }


 public static void start(int port) {
 // 配置服務器-使用java線程池作爲解釋線程
 ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
 // 設置 pipeline factory.
 bootstrap.setPipelineFactory(new ServerPipelineFactory());
 // 綁定端口
 bootstrap.bind(new InetSocketAddress(port));
 System.out.println("admin start on "+port);
 ServiceLocator.initServiceLocator();
 }


 private static class ServerPipelineFactory implements
 ChannelPipelineFactory {
 public ChannelPipeline getPipeline() throws Exception {
 // Create a default pipeline implementation.
 ChannelPipeline pipeline = Channels.pipeline();
 pipeline.addLast("decoder", new HttpRequestDecoder());
 pipeline.addLast("encoder", new HttpResponseEncoder());


 //http處理handler
 pipeline.addLast("handler", new AdminServerHandler());
 return pipeline;
 }
 }
}

啓動了服務,綁定了8088端口。


2、當客戶端給服務端post數據的時候,如果數據超過50K,這個時候服務端接受到的post內容是空的了。這是因爲超過了 服務端默認的post的長度的最大值。

http協議裏邊,本來是沒有對post的長度進行限制,但是,無論是系統層面或者是服務端層面的,都會對post的長度進行限制,這個也有利於網絡安全。


3、在netty中的解決方法

private static class ServerPipelineFactory implements
            ChannelPipelineFactory {
        public ChannelPipeline getPipeline() throws Exception {
            // Create a default pipeline implementation.
            ChannelPipeline pipeline = Channels.pipeline();
//			pipeline.addFirst("frameDecoder", new LengthFieldBasedFrameDecoder(100000000,0,4,0,4));
            pipeline.addLast("decoder", new HttpRequestDecoder());
            pipeline.addLast("encoder", new HttpResponseEncoder());
//		         pipeline.addLast("streamer", new ChunkedWriteHandler()); 
                 pipeline.addLast("aggregator", new HttpChunkAggregator(65536));//設置塊的最大字節數
            //http處理handler
            pipeline.addLast("handler", new AdminServerHandler());
            return pipeline;
        }
    }

加上

pipeline.addLast("aggregator", new HttpChunkAggregator(65536))

之後,設置默認的chunk最大爲 65536,這樣,就可以接受最大post的內容大小爲 65536。

這樣有一個不好的地方,就是這個大小不好控制,開大了,會浪費空間。並且在接受到的字符串的最後,會出現空白的字符串,這是由於post的內容長度小於chunk裏邊的ChannelBuffer的數組的大小,程序給予補全。

4、自己設置,自己讀取chunk

加上

pipeline.addLast("streamer", new ChunkedWriteHandler());

設置爲給位 分開一個個chunk去接受信息。

public boolean excuteChunk(ChannelHandlerContext ctx, MessageEvent e)
            throws TooLongFrameException {
        // HttpMessage currentMessage = e.getMessage();

        if (e.getMessage() instanceof HttpMessage) {
            HttpMessage m = (HttpMessage) e.getMessage();
            if (m.isChunked()) {
                // A chunked message - remove 'Transfer-Encoding' header,
                // initialize the cumulative buffer, and wait for incoming
                // chunks.
                List<String> encodings = m
                        .getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
                encodings.remove(HttpHeaders.Values.CHUNKED);
                if (encodings.isEmpty()) {
                    m.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
                }
                m.setContent(ChannelBuffers.dynamicBuffer(e.getChannel()
                        .getConfig().getBufferFactory()));
                this.currentMessage = m;
            } else {
                // Not a chunked message - pass through.
                this.currentMessage = null;
            }
            return false;
        } else if (e.getMessage() instanceof HttpChunk) {
            // Sanity check
            if (currentMessage == null) {
                throw new IllegalStateException("received "
                        + HttpChunk.class.getSimpleName() + " without "
                        + HttpMessage.class.getSimpleName());
            }

            // Merge the received chunk into the content of the current message.
            HttpChunk chunk = (HttpChunk) e.getMessage();
            ChannelBuffer content = currentMessage.getContent();

            if (content.readableBytes() > maxContentLength
                    - chunk.getContent().readableBytes()) {
                throw new TooLongFrameException("HTTP content length exceeded "
                        + maxContentLength + " bytes.");
            }

            content.writeBytes(chunk.getContent());
            if (chunk.isLast()) {
                this.currentMessage = null;
                currentMessage.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
                        String.valueOf(content.readableBytes()));
                return true;
                // Channels.fireMessageReceived(ctx, currentMessage,
                // e.getRemoteAddress());
            }
        }
        return true;
    }

在handle中,自己做處理,接受 整個post過來的數據,然後在整合起來,即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章