netty搭建http服务器
public class HttpServerStart { public static volatile boolean flag = false ; public static void start() { int port = 8099; EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new HttpServerInitializer()) .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true); Channel ch = b.bind(port).sync().channel(); System.out.println("httpserver服务成功启动,请打开 http://127.0.0.1:" + port); flag = true ; ch.closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> { protected void initChannel(SocketChannel socketChannel) throws Exception { //HttpObjectAggregator HTTP 消息解码器, 作用时将多个消息转换为1个FullHttpRequest 或者 FullHttpResponse 对象 /** * HttpRequestDecoder 会将每个 HTTP 消息转换为 多个消息对象 * HttpResquest / HttpResponse * HttpContent * LastHttpContent */ //将请求和应答消息编码或解码为HTTP消息 socketChannel.pipeline().addLast(new HttpRequestDecoder()); socketChannel.pipeline().addLast(new HttpObjectAggregator(65536));// 目的是将多个消息转换为单一的request或者response对象 socketChannel.pipeline().addLast(new HttpResponseEncoder()); socketChannel.pipeline().addLast(new ChunkedWriteHandler());//目的是支持异步大文件传输() socketChannel.pipeline().addLast("file-handler", new FileServerHandler()); socketChannel.pipeline().addLast("handler", new HttpServerHandler(false)); } }
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } public HttpServerHandler(boolean isAutoRelease){ super(isAutoRelease); } protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest request) throws Exception { try { if(!request.decoderResult().isSuccess()){ DonkeyHttpUtil.writeResponse(request, BAD_REQUEST, channelHandlerContext); return; } if(request.method() != HttpMethod.GET){ DonkeyHttpUtil.writeResponse(request, METHOD_NOT_ALLOWED, channelHandlerContext); return; } DonkeyHttpUtil.writeResponse(request, OK, channelHandlerContext); } catch (Exception e) { e.printStackTrace(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest request) throws Exception { //request.retain(); HttpResponse response = null; RandomAccessFile randomAccessFile = null; try{ // 状态为1xx的话,继续请求 if (HttpHeaders.is100ContinueExpected(request)) { send100Continue(channelHandlerContext); } String uri = request.uri();// /digitalPlatform/common/common.js if(!uri.endsWith(".js") && !uri.endsWith(".css") && !uri.endsWith(".html") && !uri.endsWith(".png") && !uri.endsWith(".jpg") && !uri.endsWith(".gif")){ channelHandlerContext.fireChannelRead(request); return; } uri = uri.substring(1); InputStream input = MyApplication.getApplication().getAssets().open(uri); File f = new File (Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "cloudsapp"+File.separator+uri); if (f.exists()) { f.delete(); }else{ f.getParentFile().mkdirs(); } f.createNewFile(); byte[] bytes = new byte[input.available()]; input.read(bytes); OutputStream os = new FileOutputStream(f); BufferedOutputStream bos = new BufferedOutputStream(os); bos.write(bytes); bos.flush(); bos.close(); try { randomAccessFile = new RandomAccessFile(f, "r"); } catch (FileNotFoundException e) { DonkeyHttpUtil.writeResponse(request, NOT_FOUND, channelHandlerContext); e.printStackTrace(); return; } if(!f.exists() || f.isHidden()){ DonkeyHttpUtil.writeResponse(request, NOT_FOUND, channelHandlerContext); return; } long fileLength = randomAccessFile.length(); response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK); setContentType(response, f); boolean keepAlive = HttpUtil.isKeepAlive(request); if (keepAlive) { response.headers().set(HttpHeaderNames.CONTENT_LENGTH, fileLength); response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } channelHandlerContext.write(response); ChannelFuture sendFileFuture = channelHandlerContext.write(new ChunkedNioFile(randomAccessFile.getChannel()), channelHandlerContext.newProgressivePromise()); // 写入文件尾部 sendFileFuture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) { if (total < 0) { // total unknown System.out.println("Transfer progress: " + progress); } else { System.out.println("Transfer progress: " + progress + " / " + total); } } @Override public void operationComplete(ChannelProgressiveFuture future) throws Exception { System.out.println("Transfer complete."); } }); ChannelFuture lastContentFuture = channelHandlerContext.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!keepAlive) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } }catch (Exception e){ DonkeyHttpUtil.writeResponse(request, NOT_FOUND, channelHandlerContext); e.printStackTrace(); }finally { if(randomAccessFile != null){ try { randomAccessFile.close(); } catch (IOException e) { e.printStackTrace(); } }else{ DonkeyHttpUtil.writeResponse(request, NOT_FOUND, channelHandlerContext); } } } public static void writeBytesToFile(InputStream is, File file) throws IOException{ FileOutputStream fos = null; try { byte[] data = new byte[2048]; int nbread = 0; fos = new FileOutputStream(file); while((nbread=is.read(data))>-1){ fos.write(data,0,nbread); } } catch (Exception ex) { Log.e("Exception",ex.getMessage()); } finally{ if (fos!=null){ fos.close(); } } } private void setContentType(HttpResponse response, File file){ //MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); if(file.getName().endsWith(".js")){ response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/x-javascript"); }else if(file.getName().endsWith(".css")){ response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/css; charset=UTF-8"); }else if (file.getName().endsWith(".html")){ response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } private static void send100Continue(ChannelHandlerContext ctx) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); ctx.writeAndFlush(response); } }
public class DonkeyHttpUtil { public static boolean writeResponse(FullHttpRequest request, HttpResponseStatus status, ChannelHandlerContext ctx) { // Decide whether to close the connection or not. boolean keepAlive = HttpUtil.isKeepAlive(request); // Build the response object. FullHttpResponse response = new DefaultFullHttpResponse(request.protocolVersion(), status, Unpooled.copiedBuffer(status.toString(), CharsetUtil.UTF_8)); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); if (keepAlive) { // Add 'Content-Length' header only for a keep-alive connection. response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // Add keep alive header as per: response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); } // Encode the cookie. String cookieString = request.headers().get(HttpHeaderNames.COOKIE); if (cookieString != null) { Set<io.netty.handler.codec.http.cookie.Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString); if (!cookies.isEmpty()) { // Reset the cookies if necessary. for (io.netty.handler.codec.http.cookie.Cookie cookie : cookies) { response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie)); } } } // Write the response. ctx.write(response); return keepAlive; } }