netty 3.x 实现http server和遇到的坑
先转载一篇 【初学与研发之NETTY】netty3之文件上传 http://blog.csdn.net/mcpang/article/details/41139859
- 客户端:
- [java] view plain copy
- package netty3.socket.client;
- import static org.jboss.netty.channel.Channels.pipeline;
- import java.io.File;
- import java.net.InetSocketAddress;
- import java.util.List;
- import java.util.concurrent.Executors;
- import org.jboss.netty.bootstrap.ClientBootstrap;
- import org.jboss.netty.buffer.ChannelBuffer;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelFuture;
- import org.jboss.netty.channel.ChannelHandlerContext;
- import org.jboss.netty.channel.ChannelPipeline;
- import org.jboss.netty.channel.ChannelPipelineFactory;
- import org.jboss.netty.channel.ExceptionEvent;
- import org.jboss.netty.channel.MessageEvent;
- import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
- import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
- import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
- import org.jboss.netty.handler.codec.http.HttpChunk;
- import org.jboss.netty.handler.codec.http.HttpClientCodec;
- import org.jboss.netty.handler.codec.http.HttpHeaders;
- import org.jboss.netty.handler.codec.http.HttpMethod;
- import org.jboss.netty.handler.codec.http.HttpRequest;
- import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
- import org.jboss.netty.handler.codec.http.HttpResponse;
- import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
- import org.jboss.netty.handler.codec.http.HttpVersion;
- import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
- import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
- import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
- import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
- import org.jboss.netty.handler.stream.ChunkedWriteHandler;
- import org.jboss.netty.util.CharsetUtil;
- public class UploadFileClient
- {
- private ClientBootstrap bootstrap = null;
- private ChannelFuture future = null;
- private HttpDataFactory factory = null;
- // 服务端处理完成后返回的消息
- private StringBuffer retMsg = new StringBuffer();
- public UploadFileClient()
- {
- bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
- bootstrap.setPipelineFactory(new UploadChannelFactory());
- // 连接超时时间为3s
- bootstrap.setOption("connectTimeoutMillis", 3000);
- future = bootstrap.connect(new InetSocketAddress("127.0.0.1", 2777));
- // 获得一个阈值,它是来控制上传文件时内存/硬盘的比值,防止出现内存溢出
- factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
- }
- /**
- * 方法描述:关闭文件发送通道(为阻塞式)
- */
- public void shutdownClient()
- {
- // 等待数据的传输通道关闭
- future.getChannel().getCloseFuture().awaitUninterruptibly();
- bootstrap.releaseExternalResources();
- // Really clean all temporary files if they still exist
- factory.cleanAllHttpDatas();
- }
- /**
- * 方法描述:获取发送文件过程中服务端反馈的消息
- * @return 服务端反馈的消息
- */
- public String getRetMsg()
- {
- return retMsg.toString();
- }
- /**
- * 方法描述:将文件上传到服务端
- * @param file 待上传的文件
- */
- public void uploadFile(File file)
- {
- if (!file.canRead())
- {
- return;
- }
- // Simple Post form: factory used for big attributes
- List<InterfaceHttpData> bodylist = formpost(file);
- if (bodylist == null)
- {
- return;
- }
- // Multipart Post form: factory used
- uploadFileToServer(file.getName(), factory, bodylist);
- }
- /**
- * @param file
- * @return
- */
- private List<InterfaceHttpData> formpost(File file)
- {
- // Prepare the HTTP request.
- HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "");
- // Use the PostBody encoder
- HttpPostRequestEncoder bodyRequestEncoder = null;
- try
- {
- bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, false);
- bodyRequestEncoder.addBodyAttribute("getform", "POST");
- bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);
- }
- catch(Exception e)
- {
- // should not be since args are not null
- e.printStackTrace();
- return null;
- }
- // Create the bodylist to be reused on the last version with Multipart support
- List<InterfaceHttpData> bodylist = bodyRequestEncoder.getBodyListAttributes();
- return bodylist;
- }
- /**
- * Multipart example
- */
- private void uploadFileToServer(String fileName, HttpDataFactory factory, List<InterfaceHttpData> bodylist)
- {
- // Wait until the connection attempt succeeds or fails.
- Channel channel = future.awaitUninterruptibly().getChannel();
- if (!future.isSuccess())
- {
- future.getCause().printStackTrace();
- bootstrap.releaseExternalResources();
- return;
- }
- // Prepare the HTTP request.
- HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, fileName);
- // 设置该属性表示服务端文件接收完毕后会关闭发送通道
- request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
- // Use the PostBody encoder
- HttpPostRequestEncoder bodyRequestEncoder = null;
- try
- {
- bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, true);
- bodyRequestEncoder.setBodyHttpDatas(bodylist);
- bodyRequestEncoder.finalizeRequest();
- }
- catch(Exception e)
- {
- // should not be since no null args
- e.printStackTrace();
- }
- System.out.println("开始时间:"+System.currentTimeMillis());
- // send request
- channel.write(request);
- // test if request was chunked and if so, finish the write
- if (bodyRequestEncoder.isChunked())
- {
- channel.write(bodyRequestEncoder).awaitUninterruptibly();
- }
- // Now no more use of file representation (and list of HttpData)
- bodyRequestEncoder.cleanFiles();
- }
- private class UploadChannelFactory implements ChannelPipelineFactory
- {
- public ChannelPipeline getPipeline() throws Exception
- {
- ChannelPipeline pipeline = pipeline();
- pipeline.addLast("decoder", new HttpResponseDecoder());
- pipeline.addLast("encoder", new HttpRequestEncoder());
- pipeline.addLast("codec", new HttpClientCodec());
- pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
- pipeline.addLast("handler", new UploadClientHandler());
- return pipeline;
- }
- }
- private class UploadClientHandler extends SimpleChannelUpstreamHandler
- {
- private boolean readingChunks;
- /**
- * 方法描述:接收服务端返回的消息
- * @param ctx 发送消息的通道对象
- * @param e 消息发送事件对象
- */
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
- {
- if (!readingChunks)
- {
- HttpResponse response = (HttpResponse)e.getMessage();
- // 收到服务端反馈的消息,并且链接正常、且还有后续消息
- if (response.getStatus().getCode() == 200 && response.isChunked())
- {
- readingChunks = true;
- }
- else
- {
- // 服务端有反馈消息,但没有后续的消息了
- ChannelBuffer content = response.getContent();
- if (content.readable())
- {
- retMsg.append(content.toString(CharsetUtil.UTF_8));
- }
- }
- }
- else
- {
- HttpChunk chunk = (HttpChunk)e.getMessage();
- if (chunk.isLast())
- {
- // 服务端的消息接收完毕
- readingChunks = false;
- }
- else
- {
- // 连续接收服务端发过来的消息
- retMsg.append(chunk.getContent().toString(CharsetUtil.UTF_8));
- }
- }
- }
- /**
- * 方法描述:消息接收或发送过程中出现异常
- * @param ctx 发送消息的通道对象
- * @param e 异常事件对象
- */
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
- {
- System.out.println("异常--:" + e.getCause());
- e.getChannel().close();
- // 有异常后释放客户端占用的通道资源
- shutdownClient();
- }
- }
- }
- 服务端:
- [java] view plain copy
- package netty3.socket.server;
- import static org.jboss.netty.channel.Channels.pipeline;
- import java.net.InetSocketAddress;
- import java.util.concurrent.Executors;
- import org.jboss.netty.bootstrap.ServerBootstrap;
- import org.jboss.netty.channel.ChannelPipeline;
- import org.jboss.netty.channel.ChannelPipelineFactory;
- import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
- import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
- import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
- import org.jboss.netty.handler.stream.ChunkedWriteHandler;
- public class InitServer
- {
- private static InitServer sockServer = null;
- private static ServerBootstrap bootstrap = null;
- public static InitServer getInstance()
- {
- if (sockServer == null)
- {
- sockServer = new InitServer();
- }
- return sockServer;
- }
- public InitServer()
- {
- bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
- bootstrap.setPipelineFactory(new ChannelPipelineFactory()
- {
- public ChannelPipeline getPipeline() throws Exception
- {
- ChannelPipeline pipeline = pipeline();
- pipeline.addLast("decoder", new HttpRequestDecoder());
- pipeline.addLast("encoder", new HttpResponseEncoder());
- pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
- pipeline.addLast("handler", new ServerHandler());
- return pipeline;
- }
- });
- bootstrap.bind(new InetSocketAddress("127.0.0.1", 2777));
- }
- public void shutdownServer()
- {
- bootstrap.releaseExternalResources();
- }
- }
- [java] view plain copy
- package netty3.socket.server;
- import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CACHE_CONTROL;
- import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
- import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.DATE;
- import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.EXPIRES;
- import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LAST_MODIFIED;
- import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.RandomAccessFile;
- import java.io.UnsupportedEncodingException;
- import java.net.URLDecoder;
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
- import java.util.Date;
- import java.util.GregorianCalendar;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Random;
- import java.util.TimeZone;
- import javax.activation.MimetypesFileTypeMap;
- import netty3.socket.client.SendMsgClient;
- import org.jboss.netty.buffer.ChannelBuffer;
- import org.jboss.netty.buffer.ChannelBuffers;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelFuture;
- import org.jboss.netty.channel.ChannelFutureListener;
- import org.jboss.netty.channel.ChannelFutureProgressListener;
- import org.jboss.netty.channel.ChannelHandlerContext;
- import org.jboss.netty.channel.ChannelStateEvent;
- import org.jboss.netty.channel.Channels;
- import org.jboss.netty.channel.DefaultFileRegion;
- import org.jboss.netty.channel.ExceptionEvent;
- import org.jboss.netty.channel.FileRegion;
- import org.jboss.netty.channel.MessageEvent;
- import org.jboss.netty.channel.SimpleChannelHandler;
- import org.jboss.netty.handler.codec.frame.TooLongFrameException;
- import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
- import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
- import org.jboss.netty.handler.codec.http.HttpChunk;
- import org.jboss.netty.handler.codec.http.HttpHeaders;
- import org.jboss.netty.handler.codec.http.HttpMethod;
- import org.jboss.netty.handler.codec.http.HttpRequest;
- import org.jboss.netty.handler.codec.http.HttpResponse;
- import org.jboss.netty.handler.codec.http.HttpResponseStatus;
- import org.jboss.netty.handler.codec.http.HttpVersion;
- import org.jboss.netty.handler.codec.http.multipart.Attribute;
- import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
- import org.jboss.netty.handler.codec.http.multipart.DiskFileUpload;
- import org.jboss.netty.handler.codec.http.multipart.FileUpload;
- import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
- import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
- import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
- import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
- import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
- import org.jboss.netty.handler.ssl.SslHandler;
- import org.jboss.netty.handler.stream.ChunkedFile;
- import org.jboss.netty.util.CharsetUtil;
- public class ServerHandler extends SimpleChannelHandler
- {
- public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
- public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
- public static final int HTTP_CACHE_SECONDS = 60;
- private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed MINSIZE
- private HttpPostRequestDecoder decoder;
- private HttpRequest request;
- private String receiveFileName = "";
- private Map<String, String> msgMap = new HashMap<String, String>();
- private boolean readingChunks = false;
- static
- {
- DiskFileUpload.baseDirectory = "/home/build1/file_test/";
- }
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
- {
- if (e.getMessage() instanceof HttpRequest)
- {
- HttpRequest request = (DefaultHttpRequest)e.getMessage();
- String uri = sanitizeUri(request.getUri());
- System.out.println(request.isChunked());
- if (request.getMethod() == HttpMethod.POST)
- {
- // 接收客户端上传的文件
- receiveFileName = uri;
- this.request = request;
- // clean previous FileUpload if Any
- if (decoder != null)
- {
- decoder.cleanFiles();
- decoder = null;
- }
- // if GET Method: should not try to create a HttpPostRequestDecoder
- try
- {
- decoder = new HttpPostRequestDecoder(factory, request);
- }
- catch(Exception e1)
- {
- e1.printStackTrace();
- writeResponse(e.getChannel(), "接收文件信息时出现异常:" + e1.toString());
- Channels.close(e.getChannel());
- return;
- }
- if (!request.isChunked())
- {
- readHttpDataAllReceive(e.getChannel());
- writeResponse(e.getChannel(), "服务端文件接收完毕!");
- }
- }
- }
- else
- {
- // New chunk is received
- HttpChunk chunk = (HttpChunk)e.getMessage();
- // example of reading only if at the end
- if (!chunk.isLast())
- {
- try
- {
- decoder.offer(chunk);
- }
- catch(Exception e1)
- {
- e1.printStackTrace();
- writeResponse(e.getChannel(), "接收文件数据时出现异常:" + e1.toString());
- Channels.close(e.getChannel());
- return;
- }
- // example of reading chunk by chunk (minimize memory usage due to Factory)
- readHttpDataChunkByChunk();
- } else {
- readHttpDataAllReceive(e.getChannel()); //最后数据
- //writeResponse(e.getChannel(), "服务端数据接收完毕!");
- String sendMsg = msgMap.get("sendMsg");
- System.out.println("服务端收到消息:" + sendMsg);
- sendReturnMsg(ctx, HttpResponseStatus.OK, "服务端返回的消息!");
- }
- }
- }
- /**
- * Example of reading all InterfaceHttpData from finished transfer
- */
- private void readHttpDataAllReceive(Channel channel)
- {
- List<InterfaceHttpData> datas;
- try
- {
- datas = decoder.getBodyHttpDatas();
- }
- catch(Exception e1)
- {
- e1.printStackTrace();
- writeResponse(channel, "接收文件数据时出现异常:" + e1.toString());
- Channels.close(channel);
- return;
- }
- for (InterfaceHttpData data : datas)
- {
- writeHttpData(data);
- }
- }
- /**
- * Example of reading request by chunk and getting values from chunk to chunk
- */
- private void readHttpDataChunkByChunk()
- {
- try
- {
- while(decoder.hasNext())
- {
- InterfaceHttpData data = decoder.next();
- if (data != null)
- {
- // new value
- writeHttpData(data);
- }
- }
- }
- catch(EndOfDataDecoderException e1)
- {
- e1.printStackTrace();
- }
- }
- private void writeHttpData(InterfaceHttpData data)
- {
- if (data.getHttpDataType() == HttpDataType.FileUpload)
- {
- FileUpload fileUpload = (FileUpload)data;
- if (fileUpload.isCompleted())
- {
- try
- {
- Random r = new Random();
- StringBuffer fileNameBuf = new StringBuffer();
- fileNameBuf.append(DiskFileUpload.baseDirectory).append("U").append(System.currentTimeMillis());
- fileNameBuf.append(String.valueOf(r.nextInt(10))).append(String.valueOf(r.nextInt(10)));
- fileNameBuf.append(receiveFileName.substring(receiveFileName.lastIndexOf(".")));
- fileUpload.renameTo(new File(fileNameBuf.toString()));
- }
- catch(IOException e)
- {
- e.printStackTrace();
- }
- System.out.println("结束时间:"+System.currentTimeMillis());
- }
- else
- {
- System.out.println("\tFile to be continued but should not!\r\n");
- }
- }
- else if (data.getHttpDataType() == HttpDataType.Attribute)
- {
- Attribute attribute = (Attribute)data;
- try
- {
- msgMap.put(attribute.getName(), attribute.getString());
- }
- catch(IOException e)
- {
- e.printStackTrace();
- }
- }
- }
- private void writeResponse(Channel channel, String retMsg)
- {
- // Convert the response content to a ChannelBuffer.
- ChannelBuffer buf = ChannelBuffers.copiedBuffer(retMsg, CharsetUtil.UTF_8);
- // Decide whether to close the connection or not.
- boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION))
- || request.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
- && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION));
- // Build the response object.
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
- response.setContent(buf);
- response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
- if (!close)
- {
- // There's no need to add 'Content-Length' header
- // if this is the last response.
- response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
- }
- // Write the response.
- ChannelFuture future = channel.write(response);
- // Close the connection after the write operation is done if necessary.
- if (close)
- {
- future.addListener(ChannelFutureListener.CLOSE);
- }
- }
- private String sanitizeUri(String uri)
- {
- try
- {
- uri = URLDecoder.decode(uri, "UTF-8");
- }
- catch(UnsupportedEncodingException e)
- {
- try
- {
- uri = URLDecoder.decode(uri, "ISO-8859-1");
- }
- catch(UnsupportedEncodingException e1)
- {
- throw new Error();
- }
- }
- return uri;
- }
- /**
- * 方法描述:设置请求响应的header信息
- * @param response 请求响应对象
- * @param fileToCache 下载文件
- */
- private static void setContentTypeHeader(HttpResponse response, File fileToCache)
- {
- MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
- response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(fileToCache.getPath()));
- SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
- dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
- // Date header
- Calendar time = new GregorianCalendar();
- response.setHeader(DATE, dateFormatter.format(time.getTime()));
- // Add cache headers
- time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
- response.setHeader(EXPIRES, dateFormatter.format(time.getTime()));
- response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
- response.setHeader(LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
- }
- /**
- * 方法描述:给客户端发送反馈消息
- * @param ctx 发送消息的通道
- * @param status 状态
- * @param retMsg 反馈消息
- */
- private static void sendReturnMsg(ChannelHandlerContext ctx, HttpResponseStatus status, String retMsg)
- {
- HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
- response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
- response.setContent(ChannelBuffers.copiedBuffer(retMsg, CharsetUtil.UTF_8));
- // 信息发送成功后,关闭连接通道
- ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
- }
- public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
- {
- if (decoder != null)
- {
- decoder.cleanFiles();
- }
- System.out.println("连接断开:" + e.getChannel().getRemoteAddress().toString());
- }
- public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
- {
- String remoteIp = e.getChannel().getRemoteAddress().toString();
- System.out.println(remoteIp.substring(1, remoteIp.indexOf(":")));
- System.out.println("收到连接:" + e.getChannel().getRemoteAddress().toString());
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
- {
- Channel ch = e.getChannel();
- Throwable cause = e.getCause();
- if (cause instanceof TooLongFrameException)
- {
- return;
- }
- System.err.println("连接的通道出现异常:" + cause.toString());
- if (ch.isConnected())
- {
- System.out.println("连接还没有关闭!");
- ch.close();
- }
- }
- }
我在此基础上仿写了一个http server, 但是当执行到 readHttpDataAllReceive(e.getChannel()); //最后数据 这里面的方法体datas = decoder.getBodyHttpDatas();的时候就会报错,我首先忽略这部分异常不返回,还是继续走下面的读取代码,虽然程序没有报错,但是最后获取到的数据就丢失了部分
NotEnoughDataDecoderException
- public List<InterfaceHttpData> getBodyHttpDatas()
- throws NotEnoughDataDecoderException {
- if (!isLastChunk) {
- throw new NotEnoughDataDecoderException();
- }
- return bodyListHttpData;
- }
isLastChunk=false得时候就会触发这个异常,但是我们之前的代码流程不是已经到了最后一个chunk了吗,怎么回事?我们再看看isLastChunk的代码
- public void offer(HttpChunk chunk) throws ErrorDataDecoderException {
- ChannelBuffer chunked = chunk.getContent();
- if (undecodedChunk == null) {
- undecodedChunk = chunked;
- } else {
- //undecodedChunk = ChannelBuffers.wrappedBuffer(undecodedChunk, chunk.getContent());
- // less memory usage
- undecodedChunk = ChannelBuffers.wrappedBuffer(
- undecodedChunk, chunked);
- }
- if (chunk.isLast()) {
- isLastChunk = true;
- }
- parseBody();
- }
offer方法里会触发isLastChunk=true, 那问题就清晰了,我们再回到readHttpDataAllReceive(e.getChannel()); //最后数据 这段代码里,在这之前也加上
- try
- {
- decoder.offer(chunk);
- }
- catch(Exception e1)
- {
- e1.printStackTrace();
- return;
- }
问题解决,接收数据也完整
stackoverflow也提及过这个问题
https://stackoverflow.com/questions/23989217/posting-data-to-netty-with-apache-httpclient
To solve the problem you either need to offer()
all chunks (HttpContent
) of a message to HttpPostRequestDecoder
before calling getBodyHttpDatas()
, or alternatively you can just add the HttpObjectAggregator
handler right before your handler to the channel's pipeline. If you do so, HttpObjectAggregator
will collect all chunks for you and produce a single FullHttpRequest
in place of multiple chunks. Passing FullHttpRequest
instead of an ordinary HttpRequest
to HttpPostRequestDecoder
's constructor eliminates need to offer()
chunks.
意思是通过offer方法把一个http请求的若干个chunk合在一起,在调用getBodyHttpDatas()前必须使用offer()方法把message完整化
刚开始写这个http server的时候,客户端发送数据很少,包体不会拆分成若干个chunk,一切都很正常,后来当客户端发送数据很大的时候,使用之前的httpRequest根本没有办法获取到数据
PS: 一个http请求里的拆分成若干个chunk后,channelid是一样的, 我们通过这个channelid来重新组装数据
netty 3.x 实现http server和遇到的坑的更多相关文章
- dubbo源码分析4-基于netty的dubbo协议的server
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Netty源码—一、server启动(1)
Netty作为一个Java生态中的网络组件有着举足轻重的位置,各种开源中间件都使用Netty进行网络通信,比如Dubbo.RocketMQ.可以说Netty是对Java NIO的封装,比如ByteBu ...
- SQL SERVER 2012/2014 链接到 SQL SERVER 2000的各种坑
本文总结一下SQL SERVER 2012/2014链接到SQL SERVER 2000的各种坑,都是在实际应用中遇到的疑难杂症.可能会有人说怎么还在用SQL SERVER 2000,为什么不升级呢? ...
- ubuntu下搭建node server的几个坑
[ubuntu下搭建node server的几个坑] 1.环境变量 process.env.PORT需要使用 export PORT=80设置 windows下是set PORT=80 2.命令连结 ...
- Windows Server 2012搭建SQL Server Always On踩坑全记录
Windows Server 2012搭建SQL Server Always On踩坑全记录 环境信息: Windows Server 2012 R2 Sql Server 2012 整个搭建集群的过 ...
- Netty源码—二、server启动(2)
我们在使用Netty的时候的初始化代码一般如下 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGro ...
- 基于netty的一款http server
cicada 基本功能 1.基于注解.注解扫描 2.ioc 对象管理 3.intercepter 拦截器 ref https://github.com/TogetherOS/cicada
- JMeter一个错误the target server failed to respond--JMeter坑
问题:1.在测试一个http景象,特别是集波动TPS时刻,出现了一个错误.它现在是一个必须错误(压力顺利时却零星的错误,甚至很少见): 每次必现错误(開始一直怀疑是网络或程序的问题) 2.失败事务 ...
- Microsoft SQL Server on Linux 踩坑指南
微软用 SQL Server 在 2016 年的时候搞了一个大新闻,宣传 Microsoft ❤️ Linux 打得一众软粉措手不及.但是这还是好事情,Linux 上也有好用的 SQL Server ...
随机推荐
- python------模块定义、导入、优化 ------->hashlib模块
一.hashlib模块 用于加密相关的操作,3.x版本里代替了md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512,MD5算法. (MD5消息摘要算法(英语 ...
- CF使用TGP下载后,分卷文件损坏的解决方法
首先从游戏的列表删除游戏(安装失败出现分卷文件损坏的游戏) 然后进入游戏重新,继续找到该游戏(安装失败的游戏) 点击下载游戏!不会重新下载的,之后下载一些失败的文件,不会花费多少时间,慢慢等待即可 之 ...
- django+uwsgi+nginx数据表过大引起"out of memory for query result"
昨天负责的一个项目突然爆“out of memory for query result”. 背景 项目的数据表是保存超过10m的文本数据,通过json方式保存进postgres中,上传一个13m的大文 ...
- phpdocumentor安装和使用总结
为了解决一校友在安装和使用phpDocumentor过程中遇到的问题,自己闲时也折腾了一下这个东西,总结见下: 一.定义: 自己刚听到这个词时还不知道这个是什么东西,干啥用的,就去百度了一下,说道: ...
- 数据格式转换(一)PDF转换技术
PDF(Portable Document Format)文件格式是Adobe公司开发的电子文件格式. 这样的文件格式与操作系统平台无关.这一特点使它成为在Internet上进行电子文档发行 ...
- NPOI之Excel——设置单元格背景色
NPOI Excel 单元格颜色对照表,在引用了 NPOI.dll 后可通过 ICellStyle 接口的 FillForegroundColor 属性实现 Excel 单元格的背景色设置,FillP ...
- git 查看提交历史
查看提交历史 git log 查看每次提交的具体改动内容 git log -p 查看某个文件历次提交的具体改动内容 git log -p <file name> # git log -p ...
- [转]TA-Lib 安装
转自:https://mrjbq7.github.io/ta-lib/install.html Installation You can install from PyPI: $ pip instal ...
- Kafka使用log.retention.hours改变消息端的消息保存时间
数据存储的最大时间超过这个时间会根据log.cleanup.policy设置的策略处理数据,也就是消费端能够多久去消费数据log.retention.bytes和log.retention.hours ...
- Oracle和SQL语句的优化策略(基础篇)
转载自: http://blog.csdn.net/houpengfei111/article/details/9245337 http://blog.csdn.net/uniqed/article/ ...