用Netty实现的简单HTTP服务器
来源:互联网 发布:网络推广员专业知识 编辑:程序博客网 时间:2024/06/06 02:46
用Netty实现的一个简单的HTTP服务器,可以处理静态文件,例子中的注释也比较全。主要是对HTTP的理解,接下来的文章中我也会更新一些HTTP相关的文章以及对例子的进一步完善,由浅到深,记录一些我的学习过程!
1.Server
01
public class HttpServer {
02
public static void main(String[] args) {
03
ServerBootstrap bootstrap =
new
ServerBootstrap(
new
NioServerSocketChannelFactory(
04
Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
05
06
bootstrap.setPipelineFactory(
new
HttpServerPipelineFactory());
07
08
bootstrap.bind(
new
InetSocketAddress(8080));
09
System.out.println(
"服务器已经启动,请访问http://127.0.0.1:8080/index.html进行测试!\n\n"
);
10
}
11
}
2.Pipeline
01
public class HttpServerPipelineFactory implements ChannelPipelineFactory {
02
public ChannelPipeline getPipeline() throws Exception {
03
// Create a default pipeline implementation.
04
ChannelPipeline pipeline = pipeline();
05
06
// Uncomment the following line if you want HTTPS
07
// SSLEngine engine =
08
// SecureChatSslContextFactory.getServerContext().createSSLEngine();
09
// engine.setUseClientMode(false);
10
// pipeline.addLast("ssl", new SslHandler(engine));
11
12
pipeline.addLast(
"decoder"
,
new
HttpRequestDecoder());
13
// Uncomment the following line if you don't want to handle HttpChunks.
14
// pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
15
pipeline.addLast(
"encoder"
,
new
HttpResponseEncoder());
16
// Remove the following line if you don't want automatic content
17
// compression.
18
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
19
pipeline.addLast(
"chunkedWriter"
,
new
ChunkedWriteHandler());
20
pipeline.addLast(
"deflater"
,
new
HttpContentCompressor());
21
pipeline.addLast(
"handler"
,
new
HttpRequestHandler());
22
return
pipeline;
23
}
24
}
001
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
002
003
import static org.jboss.netty.handler.codec.http.HttpHeaders.isKeepAlive;
004
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
005
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE;
006
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE;
007
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
008
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
009
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
010
011
import java.io.File;
012
import java.io.RandomAccessFile;
013
import java.util.List;
014
import java.util.Map;
015
import java.util.Map.Entry;
016
import java.util.Set;
017
018
import org.jboss.netty.buffer.ChannelBuffer;
019
import org.jboss.netty.channel.Channel;
020
import org.jboss.netty.channel.ChannelFutureListener;
021
import org.jboss.netty.channel.ChannelHandlerContext;
022
import org.jboss.netty.channel.ExceptionEvent;
023
import org.jboss.netty.channel.MessageEvent;
024
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
025
import org.jboss.netty.handler.codec.http.Cookie;
026
import org.jboss.netty.handler.codec.http.CookieDecoder;
027
import org.jboss.netty.handler.codec.http.CookieEncoder;
028
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
029
import org.jboss.netty.handler.codec.http.HttpChunk;
030
import org.jboss.netty.handler.codec.http.HttpChunkTrailer;
031
import org.jboss.netty.handler.codec.http.HttpHeaders;
032
import org.jboss.netty.handler.codec.http.HttpMethod;
033
import org.jboss.netty.handler.codec.http.HttpRequest;
034
import org.jboss.netty.handler.codec.http.HttpResponse;
035
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
036
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
037
import org.jboss.netty.handler.stream.ChunkedFile;
038
import org.jboss.netty.util.CharsetUtil;
039
040
public class HttpRequestHandler extends SimpleChannelUpstreamHandler {
041
042
private HttpRequest request;
043
private boolean readingChunks;
044
045
@Override
046
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
047
if
(!readingChunks) {
048
HttpRequest request =
this
.request = (HttpRequest) e.getMessage();
049
String uri = request.getUri();
050
System.out.println(
"-----------------------------------------------------------------"
);
051
System.out.println(
"uri:"
+uri);
052
System.out.println(
"-----------------------------------------------------------------"
);
053
/**
054
* 100 Continue
055
* 是这样的一种情况:HTTP客户端程序有一个实体的主体部分要发送给服务器,但希望在发送之前查看下服务器是否会
056
* 接受这个实体,所以在发送实体之前先发送了一个携带100
057
* Continue的Expect请求首部的请求。服务器在收到这样的请求后,应该用 100 Continue或一条错误码来进行响应。
058
*/
059
if
(is100ContinueExpected(request)) {
060
send100Continue(e);
061
}
062
// 解析http头部
063
for
(Map.Entry<String, String> h : request.getHeaders()) {
064
System.out.println(
"HEADER: "
+ h.getKey() +
" = "
+ h.getValue() +
"\r\n"
);
065
}
066
// 解析请求参数
067
QueryStringDecoder queryStringDecoder =
new
QueryStringDecoder(request.getUri());
068
Map<String, List<String>> params = queryStringDecoder.getParameters();
069
if
(!params.isEmpty()) {
070
for
(Entry<String, List<String>> p : params.entrySet()) {
071
String key = p.getKey();
072
List<String> vals = p.getValue();
073
for
(String val : vals) {
074
System.out.println(
"PARAM: "
+ key +
" = "
+ val +
"\r\n"
);
075
}
076
}
077
}
078
if
(request.isChunked()) {
079
readingChunks =
true
;
080
}
else
{
081
ChannelBuffer content = request.getContent();
082
if
(content.readable()) {
083
System.out.println(content.toString(CharsetUtil.UTF_8));
084
}
085
writeResponse(e, uri);
086
}
087
}
else
{
// 为分块编码时
088
HttpChunk chunk = (HttpChunk) e.getMessage();
089
if
(chunk.isLast()) {
090
readingChunks =
false
;
091
// END OF CONTENT\r\n"
092
HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
093
if
(!trailer.getHeaderNames().isEmpty()) {
094
for
(String name : trailer.getHeaderNames()) {
095
for
(String value : trailer.getHeaders(name)) {
096
System.out.println(
"TRAILING HEADER: "
+ name +
" = "
+ value +
"\r\n"
);
097
}
098
}
099
}
100
writeResponse(e,
"/"
);
101
}
else
{
102
System.out.println(
"CHUNK: "
+ chunk.getContent().toString(CharsetUtil.UTF_8)
103
+
"\r\n"
);
104
}
105
}
106
}
107
108
private void writeResponse(MessageEvent e, String uri) {
109
// 解析Connection首部,判断是否为持久连接
110
boolean keepAlive = isKeepAlive(request);
111
112
// Build the response object.
113
HttpResponse response =
new
DefaultHttpResponse(HTTP_1_1, OK);
114
response.setStatus(HttpResponseStatus.OK);
115
// 服务端可以通过location首部将客户端导向某个资源的地址。
116
// response.addHeader("Location", uri);
117
if
(keepAlive) {
118
// Add 'Content-Length' header only for a keep-alive connection.
119
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
120
}
121
// 得到客户端的cookie信息,并再次写到客户端
122
String cookieString = request.getHeader(COOKIE);
123
if
(cookieString !=
null
) {
124
CookieDecoder cookieDecoder =
new
CookieDecoder();
125
Set<Cookie> cookies = cookieDecoder.decode(cookieString);
126
if
(!cookies.isEmpty()) {
127
CookieEncoder cookieEncoder =
new
CookieEncoder(
true
);
128
for
(Cookie cookie : cookies) {
129
cookieEncoder.addCookie(cookie);
130
}
131
response.addHeader(SET_COOKIE, cookieEncoder.encode());
132
}
133
}
134
final String path = Config.getRealPath(uri);
135
File localFile =
new
File(path);
136
// 如果文件隐藏或者不存在
137
if
(localFile.isHidden() || !localFile.exists()) {
138
// 逻辑处理
139
return
;
140
}
141
// 如果请求路径为目录
142
if
(localFile.isDirectory()) {
143
// 逻辑处理
144
return
;
145
}
146
RandomAccessFile raf =
null
;
147
try
{
148
raf =
new
RandomAccessFile(localFile,
"r"
);
149
long fileLength = raf.length();
150
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(fileLength));
151
Channel ch = e.getChannel();
152
ch.write(response);
153
// 这里又要重新温习下http的方法,head方法与get方法类似,但是服务器在响应中只返回首部,不会返回实体的主体部分
154
if
(!request.getMethod().equals(HttpMethod.HEAD)) {
155
ch.write(
new
ChunkedFile(raf, 0, fileLength, 8192));
//8kb
156
}
157
}
catch
(Exception e2) {
158
e2.printStackTrace();
159
} finally {
160
if
(keepAlive) {
161
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
162
}
163
if
(!keepAlive) {
164
e.getFuture().addListener(ChannelFutureListener.CLOSE);
165
}
166
}
167
}
168
169
private void send100Continue(MessageEvent e) {
170
HttpResponse response =
new
DefaultHttpResponse(HTTP_1_1, CONTINUE);
171
e.getChannel().write(response);
172
}
173
174
@Override
175
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
176
e.getCause().printStackTrace();
177
e.getChannel().close();
178
}
179
}
01
public class Config {
02
03
public static String getRealPath(String uri) {
04
StringBuilder sb=
new
StringBuilder(
"/home/guolei/workspace/Test/web"
);
05
sb.append(uri);
06
if
(!uri.endsWith(
"/"
)) {
07
sb.append(
'/'
);
08
}
09
return
sb.toString();
10
}
11
}
5.页面
在项目中新建一个文件夹,名称为web(可以在配置中配置),在文件夹中放入静态页面index.html。
6.启动服务器,测试
0 1
- 用Netty实现的简单HTTP服务器
- 用Netty实现的简单HTTP服务器
- 用Netty实现的简单HTTP服务器
- Netty实现简单HTTP服务器
- 网络编程知识(5)--用Netty实现的一个简单的HTTP服务器
- netty实现http服务器
- Play框架的Netty Http服务器实现
- 实现简单的HTTP服务器
- HTTP服务器的简单实现
- HTTP服务器的简单实现
- netty的简单实现
- Netty-一个简单的服务器
- netty 对 http 的实现
- netty同时做HTTP和websocket服务器并且实现HTTP路由的思路
- 简单HTTP服务器实现
- 一个简单的http服务器的实现
- 用java socket实现了一个简单的http服务器
- 自己如何实现简单的http服务器
- tomcat内存溢出,设置内存方式
- AUPE学习第十章------信号
- class与struct,this指针(笔记)
- 火狐浏览器—FlashPlayerPlugin_12_0_0_44.exe 应用程序错误
- wpf只运行一个实例
- 用Netty实现的简单HTTP服务器
- 自定义控件(二)
- rmi开发基本步骤
- 手把手教你ranorex_android自动化测试第一个示例
- 春,是朝气勃发的季节
- the setup controller has encountered
- 构造函数中调用虚函数
- WPF入门教程系列(一) 创建你的第一个WPF项目
- WPF入门教程系列(二) 深入剖析WPF Binding的使用方法