TooTallNate-Java-WebSocket源码解析
github上比较流行的java websocket框架,主要包括以下内容:
1.WebSocketServer对象
server的入口,每次都只需要创建一个实例,可以接受所有的websocket请求,内部采用NIO作为底层io框架,主要有两部分组成:
- run()方法中用while创建一个循环,不断轮训NIO的channel,处理的内容包括:接收NIO请求(isAcceptable),进行read操作(key.isReadable()),进行写操作(key.isWritable()),对所有webSocket对象中剩余尚未处理的消息进行处理。
- 其余部分继承了WebSocketListener接口,这个接口主要包括收到消息(onMessage),socket接口打开(onOpen),socket接口关闭(onClose)等方法。主要用途是,这个类包含了很多WebSocket,采用CopyOnWriteArraySet类保存为connections字段名,如果socket打开或关闭,需要更新connections对象,删除或者加入websocket。某个websocket接收到消息之后,交给这个类来进行统一处理,例如,给所有的websocket实例发送该消息(sendToAll),或者对部分webSocket实例发送消息(sendToAllExcept)。
2.WebSocket对象
单个websocket进程session表示的对象。里面包括握手协议,读写消息帧的处理。
- handleRead方法,处理消息的入口,首先判断是否完成握手协议,如果完成的话,直接将消息转化成List<Framedata>对象,然后对每帧进行处理,帧包括Opcode,fin,以及二进制数据内容(payloadData)等对象。根据opCode有不同的处理方式。如果尚未完成握手协议,需要先进行handshake。如果创建websocket对象时候,已经指定了Draft的话,就直接用该draft进行translateHandshake操作,然后获取handshakeState,如果state是matched,则进行下一步操作。如果没有指定Draft,则遍历每个draft然后进行验证,获得正确的draft之后,发送给客户端相应的draft包含的头信息。
- 这个类包含了一个SocketChannel对象,用这个对象进行消息的写操作,send, sendFrame, handleWrite, channelWrite方法都用来往socketChannel对象写入数据。
3.Handshakedata及其实现
主要用来存储握手数据,主要是类似http-head的结构,存储了key-value格式的信息,采用map类型存储。主要内容包括:Connection;Upgrade Sec-WebSocket-Key1;Sec-WebSocket-Key2;Origin等信息。
4.Framedata
存储websocket帧信息,类型有如下这几种:
enum Opcode{
CONTINIOUS,
TEXT,
BINARY,
PING,
PONG,
CLOSING
//more to come
}
5.Draft类及其实现:Draft_10,Draft_17,Draft_75,Draft_76等
主要包括了websocket协议的不同时期规范的实现,重点是在握手阶段。
存在的问题
针对一个socket请求的消息,会转化成多个FrameData,针对每个frameData处理的时候,都会往socketChannel写入数据,这样会导致socketChannel的频繁写入,作者在代码也标注了这个问题(Description Resource Path Location Type TODO high frequently calls to sendFrame are inefficient.WebSocket.java/TooTallNate-Java-WebSocket/src/net/tootallnate/websocket line 336 Java Task)。
另外WebSocket采用FrameData的好处是,同一个socket请求,可以包括很多FrameData,这样的话,客户端可以批量往服务器发送数据,WebSocket内建支持这种批量发送数据的方式,可以减少io量。服务器端也可以考虑累积一定量的FrameData之后再往客户端发送。