用Netty实现的一个简单的HTTP服务器,可以处理静态文件,例子中的注释也比较全。主要是对HTTP的理解,接下来的文章中我也会更新一些HTTP相关的文章以及对例子的进一步完善,由浅到深,记录一些我的学习过程!
public class HttpServer { public static void main(String[] args) { ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(new HttpServerPipelineFactory()); bootstrap.bind(new InetSocketAddress(8080)); System.out.println("服务器已经启动,请访问http://127.0.0.1:8080/index.html进行测试!\n\n"); } }
2.Pipeline
public class HttpServerPipelineFactory implements ChannelPipelineFactory { public ChannelPipeline getPipeline() throws Exception { // Create a default pipeline implementation. ChannelPipeline pipeline = pipeline(); // Uncomment the following line if you want HTTPS // SSLEngine engine = // SecureChatSslContextFactory.getServerContext().createSSLEngine(); // engine.setUseClientMode(false); // pipeline.addLast("ssl", new SslHandler(engine)); pipeline.addLast("decoder", new HttpRequestDecoder()); // Uncomment the following line if you don't want to handle HttpChunks. // pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); pipeline.addLast("encoder", new HttpResponseEncoder()); // Remove the following line if you don't want automatic content // compression. //pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); pipeline.addLast("deflater", new HttpContentCompressor()); pipeline.addLast("handler", new HttpRequestHandler()); return pipeline; } }
3.handler类
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected; import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK; import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; import java.io.File; import java.io.RandomAccessFile; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.handler.codec.http.Cookie; import org.jboss.netty.handler.codec.http.CookieDecoder; import org.jboss.netty.handler.codec.http.CookieEncoder; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpChunk; import org.jboss.netty.handler.codec.http.HttpChunkTrailer; 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.QueryStringDecoder; import org.jboss.netty.handler.stream.ChunkedFile; import org.jboss.netty.util.CharsetUtil; public class HttpRequestHandler extends SimpleChannelUpstreamHandler { private HttpRequest request; private boolean readingChunks; @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (!readingChunks) { HttpRequest request = this.request = (HttpRequest) e.getMessage(); String uri = request.getUri(); System.out.println("-----------------------------------------------------------------"); System.out.println("uri:"+uri); System.out.println("-----------------------------------------------------------------"); /** * 100 Continue * 是这样的一种情况:HTTP客户端程序有一个实体的主体部分要发送给服务器,但希望在发送之前查看下服务器是否会 * 接受这个实体,所以在发送实体之前先发送了一个携带100 * Continue的Expect请求首部的请求。服务器在收到这样的请求后,应该用 100 Continue或一条错误码来进行响应。 */ if (is100ContinueExpected(request)) { send100Continue(e); } // 解析http头部 for (Map.Entry<String, String> h : request.getHeaders()) { System.out.println("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n"); } // 解析请求参数 QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri()); Map<String, List<String>> params = queryStringDecoder.getParameters(); if (!params.isEmpty()) { for (Entry<String, List<String>> p : params.entrySet()) { String key = p.getKey(); List<String> vals = p.getValue(); for (String val : vals) { System.out.println("PARAM: " + key + " = " + val + "\r\n"); } } } if (request.isChunked()) { readingChunks = true; } else { ChannelBuffer content = request.getContent(); if (content.readable()) { System.out.println(content.toString(CharsetUtil.UTF_8)); } writeResponse(e, uri); } } else {// 为分块编码时 HttpChunk chunk = (HttpChunk) e.getMessage(); if (chunk.isLast()) { readingChunks = false; // END OF CONTENT\r\n" HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; if (!trailer.getHeaderNames().isEmpty()) { for (String name : trailer.getHeaderNames()) { for (String value : trailer.getHeaders(name)) { System.out.println("TRAILING HEADER: " + name + " = " + value + "\r\n"); } } } writeResponse(e, "/"); } else { System.out.println("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) + "\r\n"); } } } private void writeResponse(MessageEvent e, String uri) { // 解析Connection首部,判断是否为持久连接 boolean keepAlive = isKeepAlive(request); // Build the response object. HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); response.setStatus(HttpResponseStatus.OK); // 服务端可以通过location首部将客户端导向某个资源的地址。 // response.addHeader("Location", uri); if (keepAlive) { // Add 'Content-Length' header only for a keep-alive connection. response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); } // 得到客户端的cookie信息,并再次写到客户端 String cookieString = request.getHeader(COOKIE); if (cookieString != null) { CookieDecoder cookieDecoder = new CookieDecoder(); Set<Cookie> cookies = cookieDecoder.decode(cookieString); if (!cookies.isEmpty()) { CookieEncoder cookieEncoder = new CookieEncoder(true); for (Cookie cookie : cookies) { cookieEncoder.addCookie(cookie); } response.addHeader(SET_COOKIE, cookieEncoder.encode()); } } final String path = Config.getRealPath(uri); File localFile = new File(path); // 如果文件隐藏或者不存在 if (localFile.isHidden() || !localFile.exists()) { // 逻辑处理 return; } // 如果请求路径为目录 if (localFile.isDirectory()) { // 逻辑处理 return; } RandomAccessFile raf = null; try { raf = new RandomAccessFile(localFile, "r"); long fileLength = raf.length(); response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(fileLength)); Channel ch = e.getChannel(); ch.write(response); // 这里又要重新温习下http的方法,head方法与get方法类似,但是服务器在响应中只返回首部,不会返回实体的主体部分 if (!request.getMethod().equals(HttpMethod.HEAD)) { ch.write(new ChunkedFile(raf, 0, fileLength, 8192));//8kb } } catch (Exception e2) { e2.printStackTrace(); } finally { if (keepAlive) { response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); } if (!keepAlive) { e.getFuture().addListener(ChannelFutureListener.CLOSE); } } } private void send100Continue(MessageEvent e) { HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE); e.getChannel().write(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getCause().printStackTrace(); e.getChannel().close(); } }
4.配置类
public class Config { public static String getRealPath(String uri) { StringBuilder sb=new StringBuilder("/home/guolei/workspace/Test/web"); sb.append(uri); if (!uri.endsWith("/")) { sb.append('/'); } return sb.toString(); } }
5.页面
在项目中新建一个文件夹,名称为web(可以在配置中配置),在文件夹中放入静态页面index.html。
6.启动服务器,测试
相关推荐
同过netty实现HTTP服务器(或者客户端) 。务器提供诸如HTML文件和其他内容之类的资源,或代表客户端执行其他功能,向客户端返回响应消息。 响应包含有关请求的完成状态信息,并且还可以在其消息正文中包含所请求的...
springboot集成netty实现代理服务器,实现http和https请求的代理功能
总结起来,使用Netty实现HTTP文件服务器涉及的关键知识点包括: 1. HTTP协议的基本概念:请求方法、状态码、响应头和响应体。 2. Netty框架:Channel、EventLoopGroup、ChannelPipeline、ChannelHandler和...
本文将深入探讨如何使用Java的Netty框架实现一个基于DTU(Data Transfer Unit)的TCP服务器,该服务器具备多端口通信和多协议解析的能力。 首先,DTU是一种专门用于远程数据传输的设备,它能够通过GPRS、3G/4G等...
在本文中,我们将深入探讨如何使用Spring Boot和Netty实现一个简单的一对一聊天应用程序。Spring Boot是Java领域中广泛使用的微服务框架,它简化了配置并提供了快速启动的应用程序开发体验。Netty则是一个高性能、...
Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。在本文中,我们将深入探讨如何利用 Netty 4.0.23 版本来实现文件的传输,这包括客户端和服务器端的交互过程。...
在本项目中,我们将探讨如何利用 Netty 实现一个简易版的 Tomcat,即一个基础的 HTTP 服务器容器。Tomcat 是一个广泛使用的开源 Java Servlet 容器,它实现了 Java EE 的 Web 应用程序规范。 首先,我们需要了解 ...
基于Netty实现的内网穿透&反向代理的工具 (支持TCP上层协议和HTTP的穿透式反向代理).zip
在文件上传场景下,服务器端通常需要创建一个FileServerHandler,该处理器负责接收客户端发送的文件数据,将其写入本地磁盘,同时还需要处理可能的断点续传、文件大小限制等问题。客户端则通过构建一个...
介绍:Springboot、netty实现的http-flv、websocket-flv流媒体服务(可用于直播点播),支持rtsp、h264、h265等、rtmp等多种源,h5纯js播放(不依赖flash),不需要依赖nginx等第三方,低延迟(支持识别h264、aac...
5. **客户端实现**:对于硬件设备端,可以使用 Netty 的 `Bootstrap` 类创建一个客户端实例,配置相应的 `ChannelInitializer`,并指定服务器的地址和端口。连接成功后,同样通过 `ChannelHandlerContext` 进行数据...
本项目正是基于Netty实现了一个Web框架,旨在展示如何利用Netty的强大功能构建类似SpringBoot的MVC框架。 【描述】:“一个基于Netty实现的Web框架,或者是MVC框架,实现了基于Netty的Web框架,展示了Netty的卓越...
在传统的HTTP或WebSocket等协议中,每次请求都是一个短连接,而长连接则允许客户端和服务器之间保持持续的连接状态,从而减少建立连接的开销,提高通信效率。 描述中的链接指向了一篇关于Netty实现长连接的博客文章...
本教程将详细讲解如何使用Netty实现Websocket服务器以及对应的客户端网页。首先,我们需要理解Netty的核心概念:Channel、EventLoopGroup和Bootstrap。Channel是网络连接的抽象,用于读写数据;EventLoopGroup是一组...
1.基于netty绑定端口监听,对于mqtt消息和http请求消息分别绑定不同的监听端口; 2.在MQTTServerInitializer中,分别添加mqtt编码解码器和http编码解码器,并分别将自定义的mqtt消息处理handle类和http消息handle类...
在 "netty-http-server" 文件中,我们可以找到一个使用 Netty 实现的 HTTP 服务器示例,它展示了如何接收和处理 HTTP 请求。而在 "netty-http-client" 文件中,包含了一个简单的 HTTP 客户端示例,演示了如何向...
- 使用 Netty 创建 WebSocket 服务器,首先需要定义一个 ChannelInitializer,配置处理 WebSocket 数据的 ChannelHandler。例如,WebSocketFrameDecoder 和 WebSocketFrameEncoder 用于解码和编码 WebSocket 帧。 ...
- 使用netty4.X实现的手机游戏服务器,支持tcp,udp,http,websocket链接,采用protobuf自定义协议栈进行网络通信,支持rpc远程调用,使用mybatis3支持db存储分库分表,支持异步mysql存储,db保存时同步更新reids缓存。...
Netty 是一个高性能、异步事件驱动的网络应用程序框架,常用于开发高效的服务器和客户端。在本项目中,“netty5实现的websocket服务器”利用了Netty 5版本的特性来构建WebSocket服务端,旨在为Android和iOS的APP提供...