Netty websoket初试

Netty简介

Netty是基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。

Netty是一个事件驱动框架,什么是事件驱动呢,我的理解就是类似于js中,onclick、onblur之类的,由某些事件触发执行动作的过程,就有点类似于SAX解析,当然SAX解析也是一个事件驱动型框架,事件驱动框架的好处就在于,不会进行多余的消耗,可以精确定位到你想触发的动作,比如服务端向客户端推送消息事件,传统的做法可能是写个长轮询,事实上这个长轮询的大部分请求都是无意义的请求,白白浪费了资源,而且一般的http请求又需要封装请求头,封装握手次数,也是资源和性能上的浪费。如果使用websoket就可以像IM一样只在需要的时候进行及时会话,而且netty在没有事件触发时,线程是处于休眠状态。

我想到的使用websoket的场景,比如扫码登录,一般扫码登录的做法是,将token附带到url上然后生成二维码,扫码请求二维码后,后台记录动作,前端页面轮询后台查看扫码状态。还有一种稍微好些的做法,在页面刷新时就发送一个请求至后台,然后后台将这个请求缓存起来,直至扫码确认后,再将请求结果返回。第一种毫无疑问是效率最低的,而且也会产生延迟,第二种稍微好些,但也是可能产生很多无效的请求。比如期货股票系统,CTP高频行情是1s会产生4次,如果每次都靠浏览器向后台拉取数据,也会产生一定的延迟,经常也会发生这一perk的数据延迟至下一perk,而且用户资金状态(如浮动盈亏)需要前端实时计算,后端又需要计算一次,前端计算也是仅作为展示,实际产生的盈亏还是由后台最终计算生成,前端大量的业务计算,也会造成性能和体验上的不足,如果这些功能都采用websoket进行的话,在数据展示还有用户体验上都可以做的更好。


接下来就是一个使用netty来进行websoket会话的简单demo


1. pom.xml

<netty.version>5.0.0.Alpha2</netty.version>

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>${netty.version}</version>
</dependency>


2.Handler

对事件的响应处理

public class Wshandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
	
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		System.out.println("@ChannelId : " + ctx.channel().id().asLongText());
	}

	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		System.out.println("@用户下线: " + ctx.channel().id().asLongText());
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("@exception ");
		ctx.channel().close();
	}

	@Override
	protected void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
		Channel channel = ctx.channel();
		System.out.println("@" + channel.remoteAddress() + ": " + msg.text());
		ctx.channel().writeAndFlush(new TextWebSocketFrame("@来自服务端: " + LocalDateTime.now()));
	}
	
}


3.Init

初始化websoket的参数

public class WsInit extends ChannelInitializer<SocketChannel>{

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		ChannelPipeline pipeline = ch.pipeline();
        //HttpServerCodec: 针对http协议进行编解码
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //ChunkedWriteHandler分块写处理,文件过大会将内存撑爆
        pipeline.addLast("chunkedWriteHandler", new ChunkedWriteHandler());
        /**
         * 作用是将一个Http的消息组装成一个完成的HttpRequest或者HttpResponse,那么具体的是什么
         * 取决于是请求还是响应, 该Handler必须放在HttpServerCodec后的后面
         */
        pipeline.addLast("httpObjectAggregator", new HttpObjectAggregator(8192));
        
        //用于处理websocket, /ws为访问websocket时的uri
        pipeline.addLast("webSocketServerProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
        
        pipeline.addLast("wshandler", new Wshandler());
		
	}

}


4.server

启动netty服务

public class WsServer {
	public static void main(String[] args) throws InterruptedException {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new WsInit());

			ChannelFuture channelFuture = serverBootstrap.bind(6666).sync();
			channelFuture.channel().closeFuture().sync();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}

5.html

简单的websoket页面


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Socket</title>
        <script type="text/javascript">
            var websocket;
            
            //如果浏览器支持WebSocket
            if(window.WebSocket){  
                websocket = new WebSocket("ws://127.0.0.1:6666/ws");  //获得WebSocket对象
                
                //当有消息过来的时候触发
                websocket.onmessage = function(event){ 
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = respMessage.value + "\n" + event.data;
                }
                
                //连接关闭的时候触发
                websocket.onclose = function(event){
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = respMessage.value + "\n断开连接";
                }
                
                //连接打开的时候触发
                websocket.onopen = function(event){
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = "建立连接";
                }
            }else{
                alert("浏览器不支持WebSocket");
            }
            
            function sendMsg(msg) { //发送消息 
                if(window.WebSocket){
					setInterval(function(){
						websocket.send(msg);
					}, 250)
                    //if(websocket.readyState == WebSocket.OPEN) { //如果WebSocket是打开状态
                    //   websocket.send(msg); //send()发送消息
                    //}
                }else{
                    return;
                }
            }
        </script>
    </head>
<body>
    <form οnsubmit="return false">
        <textarea style="width: 300px; height: 200px;" name="message"></textarea>
        <input type="button" οnclick="sendMsg(this.form.message.value)" value="发送"><br>
        <h3>内容</h3>
        <textarea style="width: 300px; height: 200px;" id="respMessage"></textarea>
        <input type="button" value="清空" οnclick="javascript:document.getElementById('respMessage').value = ''">
    </form>
</body>
</html>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值