博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netty-http协议
阅读量:6611 次
发布时间:2019-06-24

本文共 11583 字,大约阅读时间需要 38 分钟。

hot3.png

import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpRequestDecoder;import io.netty.handler.codec.http.HttpResponseEncoder;import io.netty.handler.stream.ChunkedWriteHandler;/** * @FileName OrderServer.java * @Description:  * * @Date 2016年3月5日  * @author Administroter * @version 1.0 *  */public class HttpFileServer {  private static final String DEFAULT_URL = "/src/main/java/com/lxf/netty/";  public void bind(int port,final String url) throws Exception {  // 配置服务端的NIO线程组  EventLoopGroup bossGroup = new NioEventLoopGroup();  EventLoopGroup workerGroup = new NioEventLoopGroup();  try {   ServerBootstrap b = new ServerBootstrap();   b.group(bossGroup, workerGroup)    .channel(NioServerSocketChannel.class)    .childHandler(new ChannelInitializer
() {     @Override     protected void initChannel(SocketChannel ch) throws Exception {      //添加请求消息解码器      ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());      //将消息转为单一的FullHttpRequest或者FullHttpResponse      ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));      //添加响应客户端编码器      ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());      //支持异步发送大的码流,但不会占用过多的内存,防止发生java内存溢出      ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());      ch.pipeline().addLast("fileServerHandler", new HttpFileServerHandler(url));     }    });   ChannelFuture f = b.bind("127.0.0.1",port).sync();   System.out.println("Http文件服务器已启动,网址是 : " + http://127.0.0.1:+port+url);   // 等待服务端监听端口关闭   f.channel().closeFuture().sync();  } finally {   // 优雅退出,释放线程池资源   bossGroup.shutdownGracefully();   workerGroup.shutdownGracefully();  } }   /**  * @param args  * @throws Exception  */ public static void main(String[] args) throws Exception {  String url = DEFAULT_URL;  int port = 8080;  if (args != null && args.length > 0) {   try {    port = Integer.valueOf(args[0]);   } catch (NumberFormatException e) {    // 采用默认值   }  }  new HttpFileServer().bind(port,url); }}
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;import static io.netty.handler.codec.http.HttpHeaders.setContentLength;import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;import static io.netty.handler.codec.http.HttpMethod.GET;import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;import static io.netty.handler.codec.http.HttpResponseStatus.OK;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelProgressiveFuture;import io.netty.channel.ChannelProgressiveFutureListener;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.handler.codec.http.DefaultHttpResponse;import io.netty.handler.codec.http.FullHttpRequest;import io.netty.handler.codec.http.HttpHeaders;import io.netty.handler.codec.http.HttpResponse;import io.netty.handler.codec.http.LastHttpContent;import io.netty.handler.stream.ChunkedFile;import java.io.File;import java.io.FileNotFoundException;import java.io.RandomAccessFile;import com.lxf.netty.common.HttpUrlKit;/** * @FileName HttpFileServerHandler.java * @Description: * * @Date 2016年3月8日 * @author Administroter * @version 1.0 *  */public class HttpFileServerHandler extends SimpleChannelInboundHandler
 { private String url; public HttpFileServerHandler(String url) {  this.url = url; } @Override protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {  //如果出现编码错误,跳转404路径错误页面  if (!request.getDecoderResult().isSuccess()) {   HttpUrlKit.sendError(ctx, BAD_REQUEST);   return;  }  //如果不是浏览器或者表单发送get请求,跳转405错误  if (request.getMethod() != GET) {   HttpUrlKit.sendError(ctx, METHOD_NOT_ALLOWED);  }  final String uri = request.getUri();  //对具体的包装具体的url路径  final String path = HttpUrlKit.sanitizeUri(uri, url);  if (path == null) {   HttpUrlKit.sendError(ctx, FORBIDDEN);   return;  }  //获取文件对象  File file = new File(path);  //文件属于隐藏文件或者不存在  if (file.isHidden() || !file.exists()) {   HttpUrlKit.sendError(ctx, NOT_FOUND);   return;  }  //查看路径名表示的是否是一个目录,如果是,则发送目录的链接给客户端  if (file.isDirectory()) {   if (uri.endsWith("/")) {    HttpUrlKit.sendListing(ctx, file);   } else {    HttpUrlKit.sendRedirect(ctx, uri + "/");   }   return;  }  //查看路径名表示的文件是否是一个标准文件  if (!file.isFile()) {   HttpUrlKit.sendError(ctx, FORBIDDEN);   return;  }  //构建随机访问文件的读取和写入  RandomAccessFile raf = null;  try {   raf = new RandomAccessFile(file, "r");  } catch (FileNotFoundException e) {   HttpUrlKit.sendError(ctx, NOT_FOUND);   return;  }  //获取文件的长度,构造http答应消息  long fileLength = raf.length();  HttpResponse hresp = new DefaultHttpResponse(HTTP_1_1, OK);  setContentLength(request, fileLength);  //设置响应文件的mime类型,即文件的扩展名,而客户端浏览器获取这个类型以后,根绝mime来决定采用什么应用程序来处理数据  HttpUrlKit.setContentTypeHeader(hresp, file);  if (isKeepAlive(request)) {   hresp.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);  }  //发送消息  ctx.write(hresp);  ChannelFuture sendFileFuture;  //将文件写入到发送缓冲区  sendFileFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192), ctx.newProgressivePromise());  //增加ChannelFuture的监听  sendFileFuture.addListener(new ChannelProgressiveFutureListener() {   public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {    if (total < 0) {     System.err.println("Transfer progress: " + progress);    } else {     System.err.println("Transfer progress: " + progress + " / " + total);    }   }   //发送完成信息后触发   public void operationComplete(ChannelProgressiveFuture future) throws Exception {    System.out.println("Transfer complete.");   }  });  //发送编码结束的空消息体,标识消息体发送完成,同时小勇flush方法将发送消息缓冲区的消息刷新到SocketChannel  ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);  //如果非keepalive则标识发送完成,关闭连接  if (!isKeepAlive(request)) {   lastContentFuture.addListener(ChannelFutureListener.CLOSE);  } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  cause.printStackTrace();  if (ctx.channel().isActive()) {   HttpUrlKit.sendError(ctx, INTERNAL_SERVER_ERROR);  } }}
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION;import static io.netty.handler.codec.http.HttpResponseStatus.FOUND;import static io.netty.handler.codec.http.HttpResponseStatus.OK;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.http.DefaultFullHttpResponse;import io.netty.handler.codec.http.FullHttpResponse;import io.netty.handler.codec.http.HttpResponse;import io.netty.handler.codec.http.HttpResponseStatus;import io.netty.util.CharsetUtil;import java.io.File;import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.util.regex.Pattern;import javax.activation.MimetypesFileTypeMap;/** * @FileName HttpFileServer.java * @Description:  * * @Date 2016年3月7日  * @author Administroter * @version 1.0 *  */@SuppressWarnings("restriction")public class HttpUrlKit {  private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*"); private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*"); /**  * @Title: sanitizeUri   * @Description:uri路径合法性校验  * @param uri  * @param url  * @return   * @author Administroter  * @date 2016年3月11日  */ public static String sanitizeUri(String uri, String url) {  try {   //对URL进行解码   uri = URLDecoder.decode(uri, "UTF-8");  } catch (UnsupportedEncodingException e) {   try {    uri = URLDecoder.decode(uri, "ISO-8859-1");   } catch (UnsupportedEncodingException e1) {    throw new Error();   }  }  /**   * 对uri进行合法性的判断   */  if (!uri.startsWith(url)) {   return null;  }  if (!uri.startsWith("/")) {   return null;  }  //将硬编码的文件路径分隔符替换为本地操作系统文件路径分隔符,比如C:\Users\hfgff\Desktop就是将其中的“\”替换成“/”  uri = uri.replace('/', File.separatorChar);  //对uri进行第二次合法性验证验证请求的路径当中是否含有"\."或者".\"或者以"."开头或者结尾,再匹配正则规则。  if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".")    || INSECURE_URI.matcher(uri).matches()) {   return null;  }  //构造项目目录+uri构造绝对路径返回(System.getProperty("user.dir")表用户当前的工作目录)  return System.getProperty("user.dir") + File.separator + uri; } /**  * @Title: sendListing   * @Description:构建请求路径的文件目录  * @param ctx  * @param dir   * @author Administroter  * @date 2016年3月11日  */  public static void sendListing(ChannelHandlerContext ctx, File dir) {  FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);  //这里显示在浏览器,采用html的格式,设置消息头的类型  response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");  //构建消息响应信息体  StringBuilder buf = new StringBuilder();  String dirPath = dir.getPath();  buf.append("\r\n");  buf.append("");  buf.append(dirPath);  buf.append(" 目录:");  buf.append("\r\n");  buf.append("

");  buf.append("Netty学习Dome示例代码目录:");  buf.append("

\r\n");  buf.append("
    ");  buf.append("
  • 返回上一级
  • \r\n");  for (File f : dir.listFiles()) {      if (f.isHidden() || !f.canRead()) {   continue;      }      String name = f.getName();      if (!ALLOWED_FILE_NAME.matcher(name).matches()) {   continue;      }      buf.append("
  • 链接:
    ");      buf.append(name);      buf.append("
  • \r\n");  }  buf.append("
\r\n");  //分配对应消息的缓冲对象  ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);  //将缓冲区的消息存放到http答应消息中  response.content().writeBytes(buffer);  //释放缓冲区  buffer.release();  //将响应消息发送到缓冲区并刷新到SocketChannel中  ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } /**  * @Title: sendRedirect   * @Description:  * @param ctx  * @param newUri   * @author Administroter  * @date 2016年3月11日  */ public static void sendRedirect(ChannelHandlerContext ctx, String newUri) {  FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);  response.headers().set(LOCATION, newUri);  ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } public static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {  FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status.toString()    + "\r\n", CharsetUtil.UTF_8));  response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");  ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); }  public static void setContentTypeHeader(HttpResponse response, File file) {  MimetypesFileTypeMap mft = new MimetypesFileTypeMap();  //获取指定文件的扩展类型,并设置成消息消息头的类型,用于响应客户端后,浏览器根据这个,来决定采用  //浏览器嵌入的哪个应用程序模块处理响应的数据  response.headers().set(CONTENT_TYPE, mft.getContentType(file.getPath())); }}

 

转载于:https://my.oschina.net/673236963/blog/635784

你可能感兴趣的文章
Linux-正则表达式
查看>>
基本shell脚本的编辑及变量
查看>>
加密和解密 tar
查看>>
VMware、Workstation 使用
查看>>
将datatable 保存为 Excel文件(高效率版本)
查看>>
C/C++五大内存分区(转)
查看>>
springmvc_1(hello world)
查看>>
0.随笔——读后感
查看>>
CentOS 6.5下PXE+Kickstart无人值守安装操作系统
查看>>
xtrapivotcontrol 控件用法及相关属性
查看>>
Shell脚本 常用命令总结 二
查看>>
冰球游戏大概的模块
查看>>
JS模拟select下拉菜单
查看>>
线性方程组迭代求解——Jacobi迭代算法(Python实现)
查看>>
vmware workstation14永久激活密钥分享
查看>>
iOS 多线程 之 GCD(大中枢派发)(一)
查看>>
Myeclipse中打开接口实现类的快捷键
查看>>
删除sql dump中的AUTO_INCREMENT
查看>>
使用JdbcTemplate和JdbcDaoSupport
查看>>
C博客作业--指针
查看>>