基于Netty的HTTP通信研究分析
来源:互联网 发布:信鸽推送 php代码 编辑:程序博客网 时间:2024/05/11 05:24
1.HTTP服务器的创建
服务器创建时的代码同TCP完全相同。
/**HTTP方式(同TCP)*/
static ChannelFactory HTTPCHANNEL_FACTORY = newNioServerSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
static ServerBootstrap HTTPSERVER_BOOTSTRAP = new ServerBootstrap(HTTPCHANNEL_FACTORY);
/**
* HTTP服务器启动
*
* @author linfenliang
* @date 2012-7-23
* @version V1.0.0
* void
*/
public static void httpServerStartUp() {
// TODO Auto-generated method stub
HTTPSERVER_BOOTSTRAP.setPipelineFactory(new HTTPServerPipelineFactory());
// 这个配置项仅适用于我们接收到的通道实例,而不是ServerSocketChannel实例
HTTPSERVER_BOOTSTRAP.setOption("child.tcpNoDelay", true);
HTTPSERVER_BOOTSTRAP.setOption("child.keepAlive", true);
HTTPSERVER_BOOTSTRAP.setOption("reuseAddress", true);
// 绑定这个服务使用的端口
HTTPSERVER_BOOTSTRAP.bind(new InetSocketAddress(Constants.SERVER_NAME,Constants.HTTPSERVER_PORT));
LOGGER.info("HTTP服务已启动....");
}
/**
* HTTP服务器关闭
*
* @author linfenliang
* @date 2012-7-23
* @version V1.0.0
* void
*/
public static void httpServerShutDown(){
HTTPSERVER_BOOTSTRAP.releaseExternalResources();
LOGGER.info("HTTP服务已关闭");
}
2.HTTP管道工厂的设置
其关键代码如下:
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
// pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));//放在decoder以后
pipeline.addLast("encoder", new HttpResponseEncoder());
// pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
pipeline.addLast("handler", new HTTPServerHandler());
// 设置好aggregator和chunkedWriter后,可上传、下载附件,调用如下
// Channel ch = ...;
// ch.write(new ChunkedFile(new File("video.mkv"));
//
//以下是HTTPS的设置
SSLEngine engine = HttpSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false); //非客户端模式
pipeline.addLast("ssl", new SslHandler(engine));
return pipeline;
当HTTP请求有上传时或者需要给客户端响应文件时,设置aggregator和chunkedWriter,如视频流的下载,音频流的下载等,注意大小不要超出aggregator设置的限制,在ServerBootstrap不要再设置文件大小了。
当需要使用安全的连接即HTTPS连接时,需配置SslHandler到ChannelPipeline,SslHandler无需另外写出,调用NETTY封装的即可,传入的SSLEngine在下面生成
package com.lin.socket.ssl;
import java.security.KeyStore;
import java.security.Security;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.log4j.Logger;
/****************************************************************************
* com.lin.socket.ssl HttpSslContextFactory.java Created on 2012-9-11
*
* @Author: linfenliang
* @Description:SSL服务器端认证
* @Version: 1.0
***************************************************************************/
public class HttpSslContextFactory {
private static final Logger LOGGER = Logger.getLogger(HttpSslContextFactory.class);
private static final String PROTOCOL = "SSLv3";
/**针对于服务器端配置*/
private static SSLContext SSLCONTEXT = null;
static {
// 采用的加密算法
String algorithm = Security
.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
SSLContext serverContext = null;
try {
//访问Java密钥库,JKS是keytool创建的Java密钥库,保存密钥。
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(HttpsKeyStore.getKeyStoreStream(), HttpsKeyStore.getKeyStorePassword());
//创建用于管理JKS密钥库的密钥管理器。
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, HttpsKeyStore.getCertificatePassword());
//构造SSL环境,指定SSL版本为3.0,也可以使用TLSv1,但是SSLv3更加常用。
serverContext = SSLContext.getInstance(PROTOCOL);
//初始化SSL环境。第二个参数是告诉JSSE使用的可信任证书的来源,设置为null是从javax.net.ssl.trustStore中获得证书。第三个参数是JSSE生成的随机数,这个参数将影响系统的安全性,设置为null是个好选择,可以保证JSSE的安全性。
serverContext.init(kmf.getKeyManagers(), null, null);
} catch (Exception e) {
LOGGER.error("初始化客户端SSL失败", e);
throw new Error("Failed to initialize the server SSLContext", e);
}
SSLCONTEXT = serverContext;
}
/**
* 获取SSLContext实例
*
* @author linfenliang
* @date 2012-9-11
* @version V1.0.0
* @return
* SSLContext
*/
public static SSLContext getServerContext() {
return SSLCONTEXT ;
}
}
以上类即为配置的生产SSLContext的工厂,我们已经将我们生成的安全密钥以流的方式加载进KeyStore中了,密码是以CharArray的方式加载的,获取密钥和密码的方法在类HttpsKeyStore中,其主要功能方法如下:
/**
* 读取密钥
*
* @author linfenliang
* @date 2012-9-11
* @version V1.0.0
* @return InputStream
*/
public static InputStream getKeyStoreStream() {
InputStream inStream = null;
try {
inStream = new FileInputStream(Constants.KEYSTORE_PATH);
} catch (FileNotFoundException e) {
LOGGER.error("读取密钥文件失败", e);
}
return inStream;
}
/**
* 获取安全证书密码 (用于创建KeyManagerFactory)
*
* @author linfenliang
* @date 2012-9-11
* @version V1.0.0
* @return char[]
*/
public static char[] getCertificatePassword() {
return Constants.CERTIFICATEPASSWORD.toCharArray();
}
/**
* 获取密钥密码(证书别名密码) (用于创建KeyStore)
*
* @author linfenliang
* @date 2012-9-11
* @version V1.0.0
* @return char[]
*/
public static char[] getKeyStorePassword() {
return Constants.KEYSTOREPASSWORD.toCharArray();
}
3.消息的处理
对于HttpServerHander(自定义类)中的MessageReceived方法,消息处理关键代码如下:
// 判断消息类型,解析 消息
Object msg = e.getMessage();
HttpRequest request = (HttpRequest) e.getMessage();
// //数据提交方式验证
// if (request.getMethod() != HttpMethod.GET) {
// sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED);
// return;
// }
String uri = request.getUri();
uri = URLDecoder.decode(uri, Constants.UTF8ENCODER);
ChannelBuffer buffer=new DynamicChannelBuffer(1024);
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
log.info(msg);
buffer.writeBytes(("测试SSL").getBytes());
response.setContent(buffer);
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.setHeader("Content-Length", response.getContent().writerIndex());
Channel ch = ctx.getChannel();
ch.write(response);
ch.close().awaitUninterruptibly();
注意:(1)对于获取到的Uri(get方式提交的数据),其编码方式为URL编码,需要对其进行解码,解码方式URLDecoder.decode(uri, Constants.UTF8ENCODER);
(2)将设置好的response写入到Channel中即可在浏览器中返回
4一些异常的处理
对于异常的处理:当出现异常时,可参考调用如下方法:
/**
* 异常后返回给客户端的信息
*
* @author linfenliang
* @date 2012-9-11
* @version V1.0.0
* @param ctx
* @param status 返回的页面状态
* void
*/
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
response.setHeader("Content-Type", "text/plain; charset=UTF-8");
response.setHeader("Content-Length", response.getContent().writerIndex());
ChannelBuffer buffer=new DynamicChannelBuffer(1024);
buffer.writeBytes(("请求失败: " + status.toString() + "\r\n").getBytes());
response.setContent(buffer);
ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
}
调用此方法的时机一般为在exceptionCaught方法中,通过判断异常的类型,如
if (e.getCause() instanceof TooLongFrameException) {
//调用。。。
}
5.未解决的问题
1.当有多个HTTP请求时,都会提交到同一个地址,且由一个方法接收,这会对服务器造成较大压力,且可能会产生并发问题
(1)对于不同请求区分的问题的一种解决方式:
可通过在端口后面加参数的方式配置
如:
http://127.0.0.1:8888/REGISTER
http://127.0.0.1:8888/LOGIN
如果后面跟的是汉字,注意采用URLDecoder解码
2.关于客户端
本文档中的请求方式为客户端浏览器直接访问,对于基于代码的客户端尚未设置
3.关于数据在传输过程中的压缩
可在管道工厂中添加如下代码
//数据压缩
pipeline.addLast("deflater", new HttpContentCompressor());
注意此代码需放到handler之前
对于浏览器无需配置,但对于代码写的客户端,解压缩是否也无需配置尚未可知
- 基于Netty的HTTP通信研究分析
- 基于Netty的HTTP通信研究分析
- 基于Netty的http开发
- 基于netty的websocket网络通信
- 基于Netty的“请求-响应”同步通信机制实现
- 基于Netty的“请求-响应”同步通信机制实现
- Flink运行时之基于Netty的网络通信上
- Flink运行时之基于Netty的网络通信中
- Flink运行时之基于Netty的网络通信(下)
- netty开发基于长连接的http客户端
- 实例:Netty 基于Http协议下的数据传输Demo
- 实例:Netty 基于Http协议下的数据传输Demo
- 【Netty基础】基于HTTP的文件下载Server实例
- 【Netty基础】基于HTTP的上传Server实例
- 基于Netty开发水利通信软件
- 基于工作流内核的研究分析
- 【Netty入门】基于Netty的Server / Client
- 【基于GPRS的SOCKET通信的应用研究】
- HDU 1754 I Hate It 线段树入门
- Time Series笔记
- c#调用数据库中的带返回(output参数形式)存储过程的demo
- mac delete使用技巧
- [转]50个c/c++源代码网站
- 基于Netty的HTTP通信研究分析
- Hadoop学习之HDFS架构(三)
- 多线程对构造函数和析构函数的影响
- 位拷贝与值拷贝(文库转载)
- centos的iscsi客户端操作汇总
- 【转】送给和我一样曾经浮躁过的PHP程序猿
- 使用IKAnalyzer分词计算文章关键字并分享几个分词词典
- DDR3详解(以Micron MT41J128M8 1Gb DDR3 SDRAM为例)
- Oracle SQL性能优化