Netty是什么?
基于Java NIO的非阻塞网络框架,Netty是一个NIO client-server(客户端服务器)框架,异步非阻塞是其主要的特性,使用Netty可以快速开发网络应用,例如服务器和客户端协议。
Java NIO教程:http://ifeve.com/overview/
Netty官方用户指南:http://ifeve.com/netty5-user-guide/
可以用来干什么?
高实时性,高并发性的网络应用,比如: 即时聊天系统,网游,股票分析等等;支持websocket,可以用来实现相关的网页聊天系统。
websocket是什么?
WebSocket协议被设计来取代现有的使用HTTP作为传输层的双向通信技术,服务器根据http header识别是否一个websocket请求,如果是,则将请求升级为一个websocket连接,握手成功后就进入双向长连接的数据传输阶段,即,
实例(以使用websocket协议的例子):
NettyServer:
public class NettyServer {
public void run(final int port) throws InterruptedException {
/*
* Boss线程:由这个线程池提供的线程是boss种类的,用于创建、连接、绑定socket, (有点像门卫,,
* 然后把这些socket传给worker线程池。在服务器端每个监听的socket都 有一个boss线 程来处理。
* 在客户端,只有一个boss线程来处理所有的socket。
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
/*
* Worker线程:Worker线 程执行所有的异步I/O,即处理操作
*/
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器,并且开始监听端口的socket请求
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
// 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类
.channel(NioServerSocketChannel.class)
//WebsocketHandlerImp 对出入的数据进行的业务操作,其继承ChannelInitializer
.childHandler(new WebsocketHandlerImp());
System.out.println("ok!!~~~~");
ChannelFuture future = b.bind(port).sync();//绑定本地端口并同步等待完成,future是一个通道状态类
future.channel().closeFuture().sync();
} finally {//往下就是异常时的关闭了
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
System.out.println("down!!~~~~");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
NettyServer server = new NettyServer();
try {
server.run(8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}
WebsocketHandlerImp:
public class WebsocketHandlerImp extends ChannelInitializer<SocketChannel>{
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpServerCodec());//http解码,websocket是以http发起请求的
ch.pipeline().addLast(new HttpObjectAggregator(65536));//HttpObjectAggregator会把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
ch.pipeline().addLast(new ChunkedWriteHandler());//这个?。。啥意思
ch.pipeline().addLast(new WebsocketHandler());//WebsocketHandler 自己定义的类,用以处理传过来的数据
}
}
WebsocketHandler:
public class WebsocketHandler extends SimpleChannelInboundHandler<Object>{
private WebSocketServerHandshaker handshaker;
protected void messageReceived(ChannelHandlerContext ctx, Object msg)
throws Exception {
<span style="white-space:pre"> </span>system.out.println(msg);//msg输出看看是啥
if(msg instanceof FullHttpRequest){//处理http握手请求
handleHttpRequest(ctx,(FullHttpRequest)msg);
}else if(msg instanceof WebSocketFrame){//websocket请求
handleWebSocketFrame(ctx,(WebSocketFrame)msg);
}else{
//其他请求
}
}
//处理http的握手请求
private void handleHttpRequest(ChannelHandlerContext ctx,
FullHttpRequest req) throws Exception {
// 如果HTTP解码失败,返回HTTP异常,如果不是websocket,异常返回
if (!req.getDecoderResult().isSuccess()
|| (!"websocket".equals(req.headers().get("Upgrade")))) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
BAD_REQUEST));
return;
}
// 构造握手响应返回,本机测试
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
"ws://"+req.headers().get(HttpHeaders.Names.HOST), null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
WebSocketServerHandshakerFactory
.sendUnsupportedWebSocketVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);//返回response
}
}
//websocket数据交互
private void handleWebSocketFrame(ChannelHandlerContext ctx,
WebSocketFrame frame) {
// 判断是否是关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(),
(CloseWebSocketFrame) frame.retain());
return;
}
// 判断是否是Ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(
new PongWebSocketFrame(frame.content().retain()));
return;
}
//文本消息,不支持二进制消息
if (frame instanceof TextWebSocketFrame) {
// 返回应答消息
String request = ((TextWebSocketFrame) frame).text();
ctx.channel().writeAndFlush(
new TextWebSocketFrame(request
+ " , 欢迎使用Netty WebSocket服务,现在时刻:"
+ new java.util.Date().toString()));
}
}
//握手请求不成功时返回的应答
private static void sendHttpResponse(ChannelHandlerContext ctx,
FullHttpRequest req, FullHttpResponse res) {
// 返回应答给客户端
if (res.getStatus().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),
CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
setContentLength(res, res.content().readableBytes());
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.getStatus().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
//异常出错
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
其中:来着客户端的握手请求形式:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
来自服务器的握手看起来像如下形式:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
以下msg第一次输出为以上的来着客户端的握手请求形式http报头
之后输出形如:TextWebSocketFrame(data: UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 6, cap: 6))
关闭页面后:CloseWebSocketFrame(data: UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 2, cap: 2))