摘要

前面两篇文章分别给大家介绍了原理篇,应用篇,这回给大家介绍WebSocket在Netty中的应用。

什么是WebSocket

传统的Http协议只能客户端发起通信,而不能做到服务端主动通知。这里可能有人说可以采用long polling,也就是客户端不断的向服务端请求,获取新数据,虽然能解决问题,但效率低下,浪费资源,只能说是笨办法。所以WebSocket就出现了。

webSocket协议是2008年诞生,2011年成为国际标准,所有浏览器都支持。他最大的特点就是服务端和客户端全双工通信,即客户端能主动给服务端发消息,服务端也能给客户端主动发消息。

该协议默认端口80或者443 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL,例如:

ws://example.com:8080/path

不管是HTTP和Websocket都是建立在TCP协议之上, 如下图:

所不一样的就是与服务端交互流程上有所区别,如下图:

从图中可以看到,传统的Http只有请求和响应,而WebSocket分为这几个阶段。

1. 握手阶段(handshake)

  • 客户端发起握手请求,如下报文:
  • 服务端响应握手请求,如下报文:

可以看出握手阶段采用的是HTTP协议,能通过http的各种代理服务器,与http协议有良好的兼容性。
2. 长连接阶段
通过握手从http协议升级到了websocket协议,
这个时你可以向服务端发送任何消息,服务也可以向你主动推送消息。数据格式可以根据业务需要与服务端商议,灵活度高,通信效率高效。
3. 连接关闭
客户端与服务端其中一方发起关闭即为关闭。

WebSocket应用

我们主要使用Netty框架来实现服务端,采用js来实现客户端。

服务端实现

主要使用了 Netty-socketio 这个开源框架,基于Netty框架里面封装了WebSocket的协议解析,响应,如果你是maven工程,可以在pom.xml中添加dependecy

<dependency>
    <groupId>com.corundumstudio.socketio</groupId>
    <artifactId>netty-socketio</artifactId>
    <version>1.7.12</version>
</dependency>

应用起来比较简单,如下代码

public static void main( String[] args ) throws InterruptedException {
     final SocketIOServer server = new SocketIOServer(loadSocketIOConfig(doc)); // (1)

    // 启动login
    final SocketIONamespace ns = server.addNamespace("/echo"); // (2)
    EchoConnectListener listener = new EchoConnectListener(); // (3)
    EchoEventListener evtlistener = new EchoEventListener(); // (4)
    ns.addConnectListener(listener);
    ns.addDisconnectListener(listener);
    ns.addEventListener(WebEventMessage.EVT_TAG, WebEventMessage.class, evtlistener);  // (5)

    server.start();
    logger.info("============>WSServer started ... ok");
}

private static Configuration loadSocketIOConfig(Document doc) {

    Configuration config = new Configuration();
    config.setHostname(getNodeStr(doc, "host"));
    config.setPort(getNodeInt(doc, "port"));
    config.setBossThreads(getNodeInt(doc, "bossThreads"));
    config.setWorkerThreads(getNodeInt(doc, "workerThreads"));
    //config.setUseLinuxNativeEpoll(PlatformDependent.isWindows() ? false : true);
    return config;
}
public class EchoEventListener implements DataListener<WebEventMessage> {

    private static final Logger logger = LoggerFactory.getLogger(EchoEventListener.class);


    public EchoEventListener() {
    }

    @Override
    public void onData(SocketIOClient client, WebEventMessage data, AckRequest ackSender) throws Exception {
            System.out.println("message:" + data.getMessage());
            WebEventMessage hello = new WebEventMessage();
            JSONObject object = new JSONObject(data.getMessage());
            if (UserTokenMgr.getInstance().verify(object.getInt("userid"), object.getString("token"))) {
                hello.setMessage("login success");
            } else {
                hello.setMessage("login failed");
            }
            client.getNamespace().getBroadcastOperations().sendEvent(WebEventMessage.EVT_TAG, hello);  // (6)
    }
}
  1. 加载配置,实例化SocketIOServer, 配置主要是ip,端口,处理线程和工作线程数量。
  2. 加入一个命名为echo的Namespace,这个具体访问是在url体现的,如下:

ws://example.com:8080/echo

  1. 只要是这个url的访问,都会在这个namespace处理。
  2. 创建连接监听器
  3. 创建业务消息处理器
  4. 将连接监听器,和业务消息处理器添加到 namespace, 在添加业务消息处理器时,需要指定消息协议对象,比如 WebEventMessage。
  5. 服务端接收数据,并返回给客户端。

客户端实现

客户端这里采用的是socket.io, 采用js实现,与前端对接。

var socket =  io.connect('http://127.0.0.1:8080/echo');
socket.on('connect', function() {
    output('<span class="connect-msg">Client has connected to the server!</span>');
}); // (1)

socket.on('message', function(data) {
    output('<span class="username-msg"> echo' + ':</span> ' + data.message);
}); // (2)

socket.on('disconnect', function() {
    output('<span class="disconnect-msg">The client has disconnected!</span>');
}); // (3)

发送数据

var message = JSON.stringify(jsonmsg);
var cmd = 1;
var jsonObject = {'@class': 'WebEventMessage',
    cmd:cmd,
    message: message};
    socket.emit('message', jsonObject, function(arg1, arg2) {}); // (4)
  1. connect连接建立后回调函数。
  2. message用于指定收到服务器数据后的回调函数。
  3. disconnect断开连接后回调函数。
  4. emit向服务器发送消息。

总结

基于Netty的websocket,在提供良好性能的同时大大增强直播,聊天等产品在前端页面的体验。

参考链接

Sokect.io

netty-socketio

WebSocket 教程