netty-netty websocket协议实现

netty-netty websocket协议实现

摘自<netty权威指南>

http协议弊端

  • http 协议采用半双工协议。半双工通信指数据可以在客户端和服务器端上传输,但不能同时传输
  • http 消息冗长繁琐

WebSocket协议

websocket 特点:

  • 单一的TCP连接,采用全双工模式通信
  • 对代理,防火墙和路由器透明
  • 无头部信息、cookie 和身份认证
  • 无安全开销
  • 通过 ping/pong保持链路激活
  • 服务器可以主动传递消息给客户端,不再需要客户端轮询

netty实现websocket协议

服务器端:

public class WebsocketServer {

	private int port;

	public WebsocketServer(int port) {
		this.port = port;
	}


	public static void main(String[] args) throws InterruptedException {

		int port = 8080;
		new WebsocketServer(port).start();
	}

	private void start() throws InterruptedException {

		EventLoopGroup boss = new NioEventLoopGroup();
		EventLoopGroup work = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(boss, work)
					.channel(NioServerSocketChannel.class)
					.option(ChannelOption.SO_BACKLOG, 100)
					.childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {

							ch.pipeline().addLast("http-decoder", new HttpServerCodec());
							ch.pipeline().addLast("http-aggregate", new HttpObjectAggregator(65536));
							ch.pipeline().addLast("http-chunk",new ChunkedWriteHandler());
							ch.pipeline().addLast("", new WebSocketServerHandler());



						}
					});
			ChannelFuture future = b.bind(port);
			future.addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					if (future.isSuccess()){
						System.out.println("server start success...");
					}else {
						System.out.println("server start failed...");
					}
				}
			});
			future.channel().closeFuture().sync();
		} finally {

			boss.shutdownGracefully();
			work.shutdownGracefully();
		}

	}
}

WebSocketServerHandler :

public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {

	private WebSocketServerHandshaker handshaker;

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, 
                                    Object msg) throws Exception {

		if (msg instanceof FullHttpRequest) {
			handleHttpRequest(ctx, (FullHttpRequest) msg);

		} else if (msg instanceof WebSocketFrame) {

			handleWebSocketFrame(ctx, (WebSocketFrame) msg);
		}
	}

	private void handleWebSocketFrame(ChannelHandlerContext ctx, 
                                                WebSocketFrame frame) {

		if (frame instanceof CloseWebSocketFrame) {
			handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
			return;
		}

		if (frame instanceof PingWebSocketFrame) {
			ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
			return;
		}


		if (!(frame instanceof TextWebSocketFrame)) {
			throw new UnsupportedOperationException(
					String.format("%s frame types not supported", 
                    frame.getClass().getName()));
		}

		String text = ((TextWebSocketFrame) frame).text();

		System.out.println(String.format("%s recv %s", ctx.channel(), text));
		ctx.channel().write(new TextWebSocketFrame(text
				+ ",欢迎使用websocket服务,现在时刻:" + new Date().toString()));

	}

	private void handleHttpRequest(ChannelHandlerContext ctx,
                                                     FullHttpRequest request) {
		if (!request.decoderResult().isSuccess() ||
				(!"websocket".equals(request.headers().get("Upgrade")))) {
			sendHttpResponse(ctx, request, new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
		}

		WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(
				"ws://localhost:8080/websocket", null, false
		);

		handshaker = factory.newHandshaker(request);
		if (handshaker == null) {

			WebSocketServerHandshakerFactory
                        .sendUnsupportedVersionResponse(ctx.channel());
		} else {
			handshaker.handshake(ctx.channel(), request);

		}
	}

	private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request,
	                              FullHttpResponse response) {

		if (response.status().code() != 200) {
			ByteBuf byteBuf = Unpooled.copiedBuffer(response.status().toString(),
					CharsetUtil.UTF_8);
			response.content().writeBytes(byteBuf);
			byteBuf.release();
			response.headers().set(HttpHeaderNames.CONTENT_LENGTH,
					response.content().readableBytes());
		}

		ChannelFuture future = ctx.writeAndFlush(response);
		if (!HttpUtil.isKeepAlive(request) || response.status().code() != 200) {
			future.addListener(ChannelFutureListener.CLOSE);
		}
	}


	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) 
                                                    throws Exception {
		ctx.flush();
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, 
                                    Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
}


客户端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时间服务器</title>
</head>
<body>

<script type="text/javascript">
    var socket;
    if (!window.WebSocket){
        window.WebSocket = window.MozWebSocket;
    }

    if (window.WebSocket){
        socket = new WebSocket("ws://localhost:8080/websocket");
        socket.onmessage = function (event) {
            var resp = document.getElementById("responseText");
            resp.value = '';
            resp.value = event.data;
        }

        socket.onopen = function (event) {
            var txt = document.getElementById("responseText");
            txt.value = "打开WebSocket正常,浏览器支持WebSocket!";
        }
        
        socket.onclose = function (event) {
            var resp = document.getElementById("responseText");
            resp.value = '';
            resp.value = event.data;
        }
    }else {
        alert("抱歉,您的浏览器不支持WebSocket协议");
    }


    function sendMsg(message) {
        if (!window.WebSocket){
            return;
        }

        if (socket.readyState == WebSocket.OPEN){
            socket.send(message);
        }else {
            alert("WebSocket连接没有成功");
        }
        
    }
</script>


<form onsubmit="false">
    <input type="text" name="message" value="netty definitive guide">
    <br>
    <br>
    <input type="button" value="send websocket request" 
                        onclick="sendMsg(this.form.message.value)">
    <hr color="red">
    <h3>服务器响应消息</h3>
    <textarea id="responseText" style="width:500px;height: 300px;"></textarea>
</form>
</body>
</html>

运行结果:

server start success...
[id: 0x0a496855, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:53948]
									        recv netty definitive guide
发布了76 篇原创文章 · 获赞 66 · 访问量 51万+

猜你喜欢

转载自blog.csdn.net/u013887008/article/details/104212121