
上一篇文章讨论了Websocket握手协议的处理,现在我们开始讨论数据传输. Websocket数据帧的封装和传输与处理握手请求的过程没有太大不同,并且两者都需要写入套接字的输出流或通过字节缓冲区从输入流中读取. 在这里,我们从解析数据帧开始,知道如何解析数据帧websocket ping,封装不是问题.
让我们回顾一下Websocket的数据传输协议
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
具体每一bit的意思
FIN 1bit 表示信息的最后一帧
RSV 1-3 1bit each 以后备用的 默认都为 0
Opcode 4bit 帧类型,稍后细说
Mask 1bit 掩码,是否加密数据,默认必须置为1
Payload 7bit 数据的长度
Masking-key 1 or 4 bit 掩码
Payload data (x + y) bytes 数据
Extension data x bytes 扩展数据
Application data y bytes 程序数据

更重要的一个是“操作码”字段. 该字段指示帧的类型,例如传输的帧是文本类型还是二进制类型. 以二进制类型传输的数据可以是图片或语音.
OPCODE:4位
解释PayloadData,如果接收到未知的opcode,接收端必须关闭连接。
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
解析数据帧之后,我们需要根据操作码字段的类型对消息执行不同的回调处理. 可以如下定义帧数据,并通过枚举定义操作码.

public abstract interface Framedata
{
public abstract boolean isFin();
public abstract boolean getTransfereMasked();
public abstract Opcode getOpcode();
public abstract ByteBuffer getPayloadData();
public abstract void append(Framedata paramFramedata)
throws InvalidFrameException;
public static enum Opcode
{
CONTINUOUS, TEXT, BINARY, PING, PONG, CLOSING;
}
}
解析首先必须确定Fin字段和Opcode字段. 这两个字段与消息片段的概念有关. 通常,对于已知长度的消息,Fin值为1,表示结束,而Opcode值不能为0. 您可以查看上面代码的枚举类型. 0表示连续,也就是说有连续的数据帧将被发送出去.
某些长度未知的消息需要分片发送. 根据我的理解,实时语音聊天就是这种情况. 此时,前一帧的Fin值为0,操作码值为0,最后一帧的Fin值为1,操作码不为0.

第一步是读取二进制流以解析数据,从字节缓冲区中获取数据,然后按字节读取. 一个字节有8位. 数据帧由位定义. 我们必须从字节中解析细节. Websocket协议中的每一帧.
以下是用于读取二进制流的代码. 值得注意的是,每个位的数据都是通过按位运算获得的. 更麻烦的是播放负载的处理,并且数据需要根据Mask掩码解密. 其中有一个掩码,用于指示是否加密数据. 默认设置为1. 我在这里不再赘述
public Framedata translateSingleFrame(ByteBuffer buffer) throws Draft_10.IncompleteException, InvalidDataException {
int maxpacketsize = buffer.remaining();
int realpacketsize = 2;
if (maxpacketsize < realpacketsize)
throw new IncompleteException(realpacketsize);
byte b1 = buffer.get();
boolean FIN = b1 >> 8 != 0;
byte rsv = (byte) ((b1 & 0x7F) >> 4);
if (rsv != 0)
throw new InvalidFrameException("bad rsv " + rsv);
byte b2 = buffer.get();
boolean MASK = (b2 & 0xFFFFFF80) != 0;
int payloadlength = (byte) (b2 & 0x7F);
Framedata.Opcode optcode = toOpcode((byte) (b1 & 0xF));
if ((!FIN) && (
(optcode == Framedata.Opcode.PING) || (optcode == Framedata.Opcode.PONG) || (optcode == Framedata.Opcode.CLOSING))) {
throw new InvalidFrameException("control frames may no be fragmented");
}
if ((payloadlength < 0) || (payloadlength > 125)) {
if ((optcode == Framedata.Opcode.PING) || (optcode == Framedata.Opcode.PONG) || (optcode == Framedata.Opcode.CLOSING)) {
throw new InvalidFrameException("more than 125 octets");
}
if (payloadlength == 126) {
realpacketsize += 2;
if (maxpacketsize < realpacketsize)
throw new IncompleteException(realpacketsize);
byte[] sizebytes = new byte[3];
sizebytes[1] = buffer.get();
sizebytes[2] = buffer.get();
payloadlength = new BigInteger(sizebytes).intValue();
} else {
realpacketsize += 8;
if (maxpacketsize < realpacketsize)
throw new IncompleteException(realpacketsize);
byte[] bytes = new byte[8];
for (int i = 0; i < 8; i++) {
bytes[i] = buffer.get();
}
long length = new BigInteger(bytes).longValue();
if (length > 2147483647L) {
throw new LimitExedeedException("Payloadsize is to big...");
}
payloadlength = (int) length;
}
}
realpacketsize += (MASK ? 4 : 0);
realpacketsize += payloadlength;
if (maxpacketsize < realpacketsize) {
throw new IncompleteException(realpacketsize);
}
ByteBuffer payload = ByteBuffer.allocate(checkAlloc(payloadlength));
if (MASK) {
byte[] maskskey = new byte[4];
buffer.get(maskskey);
for (int i = 0; i < payloadlength; i++)
payload.put((byte) (buffer.get() ^ maskskey[(i % 4)]));
} else {
payload.put(buffer.array(), buffer.position(), payload.limit());
buffer.position(buffer.position() + payload.limit());
}
FrameBuilder frame;
FrameBuilder frame;
if (optcode == Framedata.Opcode.CLOSING) {
frame = new CloseFrameBuilder();
} else {
frame = new FramedataImpl1();
frame.setFin(FIN);
frame.setOptcode(optcode);
}
payload.flip();
frame.setPayload(payload);
return frame;
}
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/ruanjian/article-155608-1.html
这与买到有虫子的青菜一样