如何按照协议在服务器端使用WebSocket发送和接收消息?
当我从浏览器向服务器发送数据时,为什么服务器会收到看似随机的字节?数据是否已进行编码?
在服务器→客户端和客户端→服务器方向上,帧是如何工作的?
更新了Haribabu Pasupathy的代码以处理TCP分段。在我的情况下,由浏览器发送的大于1024字节的websocket数据包被分割成TCP段,因此需要重新组装。
private static void processResponse(InputStream inputStream, OutputStream outputStream) throws IOException {
int readPacketLength = 0;
byte[] packet = new byte[1024];
ByteArrayOutputStream packetStream = new ByteArrayOutputStream();
while(true) {
readPacketLength = inputStream.read(packet);
if(readPacketLength != -1) {
if ((packet[0] & (byte) 15) == (byte) 8) { // Disconnect packet
outputStream.write(packet, 0, readPacketLength);
// returning the same packet for client to terminate connection
outputStream.flush();
return;
}
byte messageLengthByte = 0;
int messageLength = 0;
int maskIndex = 2;
int messageStart = 0;
//b[0] is always text in my case so no need to check;
byte data = packet[1];
byte op = (byte) 127; // 0111 111
messageLengthByte = (byte) (data & op);
int totalPacketLength = 0;
if (messageLengthByte == (byte) 126 || messageLengthByte == (byte) 127) {
if (messageLengthByte == (byte) 126) {
maskIndex = 4;
// if (messageLengthInt==(byte)126), then 16-bit length is stored in packet[2] and [3]
ByteBuffer messageLength16Bit = ByteBuffer.allocateDirect(4);
messageLength16Bit.order(ByteOrder.BIG_ENDIAN);
messageLength16Bit.put((byte) 0x00);
messageLength16Bit.put((byte) 0x00);
messageLength16Bit.put(packet, 2, 2);
messageLength16Bit.flip();
messageLength = messageLength16Bit.getInt();
totalPacketLength = messageLength + 8;
} else {
maskIndex = 10;
// if (messageLengthInt==(byte)127), then 64-bit length is stored in bytes [2] to [9]. Using only 32-bit
ByteBuffer messageLength64Bit = ByteBuffer.allocateDirect(4);
messageLength64Bit.order(ByteOrder.BIG_ENDIAN);
messageLength64Bit.put(packet, 6, 4);
messageLength64Bit.flip();
messageLength = messageLength64Bit.getInt();
totalPacketLength = messageLength + 14;
}
if (readPacketLength != totalPacketLength) {
packetStream.write(packet, 0, readPacketLength);
int lastPacketLength = 0;
while (readPacketLength < totalPacketLength) {
packet = new byte[1024];
readPacketLength += lastPacketLength = inputStream.read(packet);
packetStream.write(packet, 0, lastPacketLength);
}
packet = packetStream.toByteArray();
packetStream.reset();
}
}
else { // using message length from packet[1]
messageLength = messageLengthByte;
}
byte[] masks = new byte[4];
int i=0; int j=0;
for(i = maskIndex; i < (maskIndex+4); i++) {
masks[j] = packet[i];
j++;
}
messageStart = maskIndex + 4;
byte[] message = new byte[messageLength];
for(i = messageStart, j = 0; i < readPacketLength; i++, j++){
message[j] = (byte) (packet[i] ^ masks[j % 4]);
}
System.out.println("Received message: " + new String(message));
packet = new byte[1024];
}
}
}
我解决了Nitij的C#实现中“> 65535”消息长度问题。
private static Byte[] EncodeMessageToSend(String message)
{
Byte[] response;
Byte[] bytesRaw = Encoding.UTF8.GetBytes(message);
Byte[] frame = new Byte[10];
Int32 indexStartRawData = -1;
Int32 length = bytesRaw.Length;
frame[0] = (Byte)129;
if (length <= 125)
{
frame[1] = (Byte)length;
indexStartRawData = 2;
}
else if (length >= 126 && length <= 65535)
{
frame[1] = (Byte)126;
frame[2] = (Byte)((length >> 8) & 255);
frame[3] = (Byte)(length & 255);
indexStartRawData = 4;
}
else
{
var lengthAsULong = Convert.ToUInt64(length);
frame[1] = 127;
frame[2] = (byte)((lengthAsULong >> 56) & 255);
frame[3] = (byte)((lengthAsULong >> 48) & 255);
frame[4] = (byte)((lengthAsULong >> 40) & 255);
frame[5] = (byte)((lengthAsULong >> 32) & 255);
frame[6] = (byte)((lengthAsULong >> 24) & 255);
frame[7] = (byte)((lengthAsULong >> 16) & 255);
frame[8] = (byte)((lengthAsULong >> 8) & 255);
frame[9] = (byte)(lengthAsULong & 255);
indexStartRawData = 10;
}
response = new Byte[indexStartRawData + length];
Int32 i, reponseIdx = 0;
//Add the frame bytes to the reponse
for (i = 0; i < indexStartRawData; i++)
{
response[reponseIdx] = frame[i];
reponseIdx++;
}
//Add the data bytes to the response
for (i = 0; i < length; i++)
{
response[reponseIdx] = bytesRaw[i];
reponseIdx++;
}
return response;
}