`

用Netty实现的一个简单的HTTP服务器

 
阅读更多

用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.启动服务器,测试

 

分享到:
评论
1 楼 kongdong88 2017-08-16  
Netty简单应用与线上服务器部署
课程学习地址:http://www.xuetuwuyou.com/course/198
课程出自学途无忧网:http://www.xuetuwuyou.com

一、开发环境
4.1.11.Final 
jdk1.8
maven 3.2
Spring 4.3.9


二、适合人群
①想深入学习java ClassLoader
②想在线上linux服务器上运行netty或Springboot服务

三、课程目标
①掌控ClassLoader
②学会编写shell脚本
③了解jvm内存分配
④熟悉线上服务器部署


四、课程目录
1、系统架构技术介绍
2、netty服务器编写
3、netty的编码和解码
4、Netty客户端编写
5、与spring整合
6、netty服务器待解决的问题
7、Classloader类加载器
8、自定义类加载器
9、第二种类加载器
10、热部署
11、类加载器加载外部配置文件
12、项目运行时加载依赖jar
13、项目运行时加载依赖jar第二种方案
14、linux服务器编写通用shell脚本启动JVM
15、优化Shell脚本
16、Shell脚本中加上JVM堆栈内存参数以及垃圾回收机制
17、Netty服务器加上配置信息




Netty课程整合推荐:

Netty简单应用与线上服务器部署
课程观看地址:http://www.xuetuwuyou.com/course/198

深入浅出Netty源码剖析
课程观看地址:http://www.xuetuwuyou.com/course/157

Netty实战高性能分布式RPC
课程观看地址:http://www.xuetuwuyou.com/course/171

NIO+Netty5各种RPC架构实战演练
课程观看地址:http://www.xuetuwuyou.com/course/52

物联网核心技术之Netty入门到精通课程
课程观看地址:http://www.xuetuwuyou.com/course/14

Netty三部曲,夜行侠老师带你玩转netty
课程观看地址:http://www.xuetuwuyou.com/course/175

Netty物联网高并发系统第一季
课程观看地址:http://www.xuetuwuyou.com/course/178

相关推荐

    Netty实现高性能的HTTP

    同过netty实现HTTP服务器(或者客户端) 。务器提供诸如HTML文件和其他内容之类的资源,或代表客户端执行其他功能,向客户端返回响应消息。 响应包含有关请求的完成状态信息,并且还可以在其消息正文中包含所请求的...

    springboot集成netty实现代理服务器

    springboot集成netty实现代理服务器,实现http和https请求的代理功能

    使用netty使用http协议开发文件服务器

    总结起来,使用Netty实现HTTP文件服务器涉及的关键知识点包括: 1. HTTP协议的基本概念:请求方法、状态码、响应头和响应体。 2. Netty框架:Channel、EventLoopGroup、ChannelPipeline、ChannelHandler和...

    Java采用Netty实现基于DTU的TCP服务器 + 多端口 + 多协议

    本文将深入探讨如何使用Java的Netty框架实现一个基于DTU(Data Transfer Unit)的TCP服务器,该服务器具备多端口通信和多协议解析的能力。 首先,DTU是一种专门用于远程数据传输的设备,它能够通过GPRS、3G/4G等...

    springboot+netty 实现简单的一对一聊天

    在本文中,我们将深入探讨如何使用Spring Boot和Netty实现一个简单的一对一聊天应用程序。Spring Boot是Java领域中广泛使用的微服务框架,它简化了配置并提供了快速启动的应用程序开发体验。Netty则是一个高性能、...

    用netty实现文件传输

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。在本文中,我们将深入探讨如何利用 Netty 4.0.23 版本来实现文件的传输,这包括客户端和服务器端的交互过程。...

    netty实现简易tomcat

    在本项目中,我们将探讨如何利用 Netty 实现一个简易版的 Tomcat,即一个基础的 HTTP 服务器容器。Tomcat 是一个广泛使用的开源 Java Servlet 容器,它实现了 Java EE 的 Web 应用程序规范。 首先,我们需要了解 ...

    基于Netty实现的内网穿透&反向代理的工具 (支持TCP上层协议和HTTP的穿透式反向代理).zip

    基于Netty实现的内网穿透&反向代理的工具 (支持TCP上层协议和HTTP的穿透式反向代理).zip

    基于Netty实现的文件上传

    在文件上传场景下,服务器端通常需要创建一个FileServerHandler,该处理器负责接收客户端发送的文件数据,将其写入本地磁盘,同时还需要处理可能的断点续传、文件大小限制等问题。客户端则通过构建一个...

    Springboot+netty实现的流媒体服务(可用于直播点播)

    介绍:Springboot、netty实现的http-flv、websocket-flv流媒体服务(可用于直播点播),支持rtsp、h264、h265等、rtmp等多种源,h5纯js播放(不依赖flash),不需要依赖nginx等第三方,低延迟(支持识别h264、aac...

    netty实现sdtp协议

    5. **客户端实现**:对于硬件设备端,可以使用 Netty 的 `Bootstrap` 类创建一个客户端实例,配置相应的 `ChannelInitializer`,并指定服务器的地址和端口。连接成功后,同样通过 `ChannelHandlerContext` 进行数据...

    基于netty实现的web框架

    本项目正是基于Netty实现了一个Web框架,旨在展示如何利用Netty的强大功能构建类似SpringBoot的MVC框架。 【描述】:“一个基于Netty实现的Web框架,或者是MVC框架,实现了基于Netty的Web框架,展示了Netty的卓越...

    netty 实现长连接

    在传统的HTTP或WebSocket等协议中,每次请求都是一个短连接,而长连接则允许客户端和服务器之间保持持续的连接状态,从而减少建立连接的开销,提高通信效率。 描述中的链接指向了一篇关于Netty实现长连接的博客文章...

    用Netty实现Websocket(包含服务器代码和客户端网页)

    本教程将详细讲解如何使用Netty实现Websocket服务器以及对应的客户端网页。首先,我们需要理解Netty的核心概念:Channel、EventLoopGroup和Bootstrap。Channel是网络连接的抽象,用于读写数据;EventLoopGroup是一组...

    基于netty实现mqtt协议 服务器端开发,可解码http、mqtt协议请求

    1.基于netty绑定端口监听,对于mqtt消息和http请求消息分别绑定不同的监听端口; 2.在MQTTServerInitializer中,分别添加mqtt编码解码器和http编码解码器,并分别将自定义的mqtt消息处理handle类和http消息handle类...

    netty http client & server

    在 "netty-http-server" 文件中,我们可以找到一个使用 Netty 实现的 HTTP 服务器示例,它展示了如何接收和处理 HTTP 请求。而在 "netty-http-client" 文件中,包含了一个简单的 HTTP 客户端示例,演示了如何向...

    整合netty实时通讯

    - 使用 Netty 创建 WebSocket 服务器,首先需要定义一个 ChannelInitializer,配置处理 WebSocket 数据的 ChannelHandler。例如,WebSocketFrameDecoder 和 WebSocketFrameEncoder 用于解码和编码 WebSocket 帧。 ...

    使用netty实现的游戏服务器,支持tcp,udp,http,websocket连接.rar

    - 使用netty4.X实现的手机游戏服务器,支持tcp,udp,http,websocket链接,采用protobuf自定义协议栈进行网络通信,支持rpc远程调用,使用mybatis3支持db存储分库分表,支持异步mysql存储,db保存时同步更新reids缓存。...

    netty5实现的websocket服务器

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,常用于开发高效的服务器和客户端。在本项目中,“netty5实现的websocket服务器”利用了Netty 5版本的特性来构建WebSocket服务端,旨在为Android和iOS的APP提供...

Global site tag (gtag.js) - Google Analytics