private static int WriteHeader(MessageOpcode opcode, byte[] sendBuffer, ArraySegment<byte> payload, bool endOfMessage, bool useMask)
{
// Client header format:
// 1 bit - FIN - 1 if this is the final fragment in the message (it could be the only fragment), otherwise 0
// 1 bit - RSV1 - Reserved - 0
// 1 bit - RSV2 - Reserved - 0
// 1 bit - RSV3 - Reserved - 0
// 4 bits - Opcode - How to interpret the payload
// - 0x0 - continuation
// - 0x1 - text
// - 0x2 - binary
// - 0x8 - connection close
// - 0x9 - ping
// - 0xA - pong
// - (0x3 to 0x7, 0xB-0xF - reserved)
// 1 bit - Masked - 1 if the payload is masked, 0 if it's not. Must be 1 for the client
// 7 bits, 7+16 bits, or 7+64 bits - Payload length
// - For length 0 through 125, 7 bits storing the length
// - For lengths 126 through 2^16, 7 bits storing the value 126, followed by 16 bits storing the length
// - For lengths 2^16+1 through 2^64, 7 bits storing the value 127, followed by 64 bytes storing the length
// 0 or 4 bytes - Mask, if Masked is 1 - random value XOR'd with each 4 bytes of the payload, round-robin
// Length bytes - Payload data
Debug.Assert(sendBuffer.Length >= MaxMessageHeaderLength, $"Expected sendBuffer to be at least {MaxMessageHeaderLength}, got {sendBuffer.Length}");
sendBuffer[0] = (byte)opcode; // 4 bits for the opcode
if (endOfMessage)
{
sendBuffer[0] |= 0x80; // 1 bit for FIN
}
// Store the payload length.
int maskOffset;
if (payload.Count <= 125)
{
sendBuffer[1] = (byte)payload.Count;
maskOffset = 2; // no additional payload length
}
else if (payload.Count <= ushort.MaxValue)
{
sendBuffer[1] = 126;
sendBuffer[2] = (byte)(payload.Count / 256);
sendBuffer[3] = (byte)payload.Count;
maskOffset = 2 + sizeof(ushort); // additional 2 bytes for 16-bit length
}
else
{
sendBuffer[1] = 127;
int length = payload.Count;
for (int i = 9; i >= 2; i--)
{
sendBuffer[i] = (byte)length;
length = length / 256;
}
maskOffset = 2 + sizeof(ulong); // additional 8 bytes for 64-bit length
}
if (useMask)
{
// Generate the mask.
sendBuffer[1] |= 0x80;
WriteRandomMask(sendBuffer, maskOffset);
}
// Return the position of the mask.
return maskOffset;
}