在网上找了很多关于websocket协议的资料。我发现大部分的资料或是实现记录的都不完整,或者只给出了最基本的实现。于是,我花了一周的业余实现写了一个相对完整的实现。
首先是解码器部分:
public class WSDecoder extends CumulativeProtocolDecoder {
private final static String REQUEST_CONTEXT_KEY = "__REQUEST_DATA_CONTEXT";
private final static String END_TAG = "\r\n";
private enum FrameType {
Text,Binary,Control;
}
private class RequestDataContext {
private IoBuffer _tmp;
private CharsetDecoder _charsetDecoder;
private FrameType _frameType;
RequestDataContext(String charset) {
this._tmp = IoBuffer.allocate(512).setAutoExpand(true);
this._charsetDecoder = Charset.forName("utf-8").newDecoder();
}
public FrameType getFrameType() {
return this._frameType;
}
public String getDataAsString() {
try {
_tmp.flip();
return _tmp.getString(_charsetDecoder);
} catch (CharacterCodingException e) {
return null;
}
}
public byte[] getDataAsArray() {
_tmp.flip();
byte[] data = new byte[_tmp.remaining()];
_tmp.get(data);
return data;
}
void append(byte[] data) {
this._tmp.put(data);
}
void setFrameType(FrameType _frameType) {
this._frameType = _frameType;
}
}
private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
public void setByteOrder(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
}
private CharsetDecoder charsetDecoder = Charset.forName("utf-8")
.newDecoder();
public void setCharsetDecoder(CharsetDecoder charsetDecoder) {
this.charsetDecoder = charsetDecoder;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput decoderOutput)
throws CharacterCodingException, NoSuchAlgorithmException {
if (!in.hasRemaining())
return false;
WSSessionState state = getSessionState(session);
switch (state) {
case Handshake:
doHandshake(session, in);
break;
case Connected:
if (in.remaining() < 2)
return false;
in.mark().order(this.byteOrder);
byte fstByte = in.get();
int opCode = fstByte & 0xf;
switch (opCode) {
case 0x0:
case 0x1:
case 0x2:
boolean isFinalFrame = fstByte < 0;
boolean isRsvColZero = (fstByte & 0x70) == 0;
if (!isRsvColZero) {
closeConnection(session, in);
break;
}
byte secByte = in.get();
boolean isMasking = secByte < 0;
int dataLength = 0;
byte payload = (byte) (secByte & 0x7f);
if (payload == 126)
dataLength = in.getUnsignedShort();
else if (payload ==