To send a message through the WebSocket connection you call the send() method on your WebSocket instance; passing in the data you want to transfer. socket. send(data); You can send both text and binary data through a WebSocket.
The Message event takes place usually when the server sends some data. Messages sent by the server to the client can include plain text messages, binary data, or images. Whenever data is sent, the onmessage function is fired.
With WebSockets: the server can send a message to the client without the client explicitly requesting something. the client and the server can talk to each other simultaneously. very little data overhead needs to be exchanged to send messages.
The key word in that definition is two-way: with WebSocket, both the client and the server can trigger communication with one another, and both can send messages, at the same time.
Note: This is some explanation and pseudocode as to how to implement a very trivial server that can handle incoming and outcoming WebSocket messages as per the definitive framing format. It does not include the handshaking process. Furthermore, this answer has been made for educational purposes; it is not a full-featured implementation.
Specification (RFC 6455)
(In other words, server → browser)
The frames you're sending need to be formatted according to the WebSocket framing format. For sending messages, this format is as follows:
The first byte will be 1000 0001
(or 129
) for a text frame.
The second byte has its first bit set to 0
because we're not encoding the data (encoding from server to client is not mandatory).
It is necessary to determine the length of the raw data so as to send the length bytes correctly:
0 <= length <= 125
, you don't need additional bytes126 <= length <= 65535
, you need two additional bytes and the second byte is 126
length >= 65536
, you need eight additional bytes, and the second byte is 127
The length has to be sliced into separate bytes, which means you'll need to bit-shift to the right (with an amount of eight bits), and then only retain the last eight bits by doing AND 1111 1111
(which is 255
).
After the length byte(s) comes the raw data.
This leads to the following pseudocode:
bytesFormatted[0] = 129
indexStartRawData = -1 // it doesn't matter what value is
// set here - it will be set now:
if bytesRaw.length <= 125
bytesFormatted[1] = bytesRaw.length
indexStartRawData = 2
else if bytesRaw.length >= 126 and bytesRaw.length <= 65535
bytesFormatted[1] = 126
bytesFormatted[2] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[3] = ( bytesRaw.length ) AND 255
indexStartRawData = 4
else
bytesFormatted[1] = 127
bytesFormatted[2] = ( bytesRaw.length >> 56 ) AND 255
bytesFormatted[3] = ( bytesRaw.length >> 48 ) AND 255
bytesFormatted[4] = ( bytesRaw.length >> 40 ) AND 255
bytesFormatted[5] = ( bytesRaw.length >> 32 ) AND 255
bytesFormatted[6] = ( bytesRaw.length >> 24 ) AND 255
bytesFormatted[7] = ( bytesRaw.length >> 16 ) AND 255
bytesFormatted[8] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[9] = ( bytesRaw.length ) AND 255
indexStartRawData = 10
// put raw data at the correct index
bytesFormatted.put(bytesRaw, indexStartRawData)
// now send bytesFormatted (e.g. write it to the socket stream)
(In other words, browser → server)
The frames you obtain are in the following format:
The first byte usually does not matter - if you're just sending text you are only using the text type. It will be 1000 0001
(or 129
) in that case.
The second byte and the additional two or eight bytes need some parsing, because you need to know how many bytes are used for the length (you need to know where the real data starts). The length itself is usually not necessary since you have the data already.
The first bit of the second byte is always 1
which means the data is masked (= encoded). Messages from the client to the server are always masked. You need to remove that first bit by doing secondByte AND 0111 1111
. There are two cases in which the resulting byte does not represent the length because it did not fit in the second byte:
0111 1110
, or 126
, means the following two bytes are used for the length0111 1111
, or 127
, means the following eight bytes are used for the lengthThe four mask bytes are used for decoding the actual data that has been sent. The algorithm for decoding is as follows:
decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]
where encodedByte
is the original byte in the data, encodedByteIndex
is the index (offset) of the byte counting from the first byte of the real data, which has index 0
. masks
is an array containing of the four mask bytes.
This leads to the following pseudocode for decoding:
secondByte = bytes[1]
length = secondByte AND 127 // may not be the actual length in the two special cases
indexFirstMask = 2 // if not a special case
if length == 126 // if a special case, change indexFirstMask
indexFirstMask = 4
else if length == 127 // ditto
indexFirstMask = 10
masks = bytes.slice(indexFirstMask, 4) // four bytes starting from indexFirstMask
indexFirstDataByte = indexFirstMask + 4 // four bytes further
decoded = new array
decoded.length = bytes.length - indexFirstDataByte // length of real data
for i = indexFirstDataByte, j = 0; i < bytes.length; i++, j++
decoded[j] = bytes[i] XOR masks[j MOD 4]
// now use "decoded" to interpret the received data
Java implementation (if any one requires)
Reading : Client to Server
int len = 0;
byte[] b = new byte[buffLenth];
//rawIn is a Socket.getInputStream();
while(true){
len = rawIn.read(b);
if(len!=-1){
byte rLength = 0;
int rMaskIndex = 2;
int rDataStart = 0;
//b[0] is always text in my case so no need to check;
byte data = b[1];
byte op = (byte) 127;
rLength = (byte) (data & op);
if(rLength==(byte)126) rMaskIndex=4;
if(rLength==(byte)127) rMaskIndex=10;
byte[] masks = new byte[4];
int j=0;
int i=0;
for(i=rMaskIndex;i<(rMaskIndex+4);i++){
masks[j] = b[i];
j++;
}
rDataStart = rMaskIndex + 4;
int messLen = len - rDataStart;
byte[] message = new byte[messLen];
for(i=rDataStart, j=0; i<len; i++, j++){
message[j] = (byte) (b[i] ^ masks[j % 4]);
}
parseMessage(new String(message));
//parseMessage(new String(b));
b = new byte[buffLenth];
}
}
Writing : Server to Client
public void brodcast(String mess) throws IOException{
byte[] rawData = mess.getBytes();
int frameCount = 0;
byte[] frame = new byte[10];
frame[0] = (byte) 129;
if(rawData.length <= 125){
frame[1] = (byte) rawData.length;
frameCount = 2;
}else if(rawData.length >= 126 && rawData.length <= 65535){
frame[1] = (byte) 126;
int len = rawData.length;
frame[2] = (byte)((len >> 8 ) & (byte)255);
frame[3] = (byte)(len & (byte)255);
frameCount = 4;
}else{
frame[1] = (byte) 127;
int len = rawData.length;
frame[2] = (byte)((len >> 56 ) & (byte)255);
frame[3] = (byte)((len >> 48 ) & (byte)255);
frame[4] = (byte)((len >> 40 ) & (byte)255);
frame[5] = (byte)((len >> 32 ) & (byte)255);
frame[6] = (byte)((len >> 24 ) & (byte)255);
frame[7] = (byte)((len >> 16 ) & (byte)255);
frame[8] = (byte)((len >> 8 ) & (byte)255);
frame[9] = (byte)(len & (byte)255);
frameCount = 10;
}
int bLength = frameCount + rawData.length;
byte[] reply = new byte[bLength];
int bLim = 0;
for(int i=0; i<frameCount;i++){
reply[bLim] = frame[i];
bLim++;
}
for(int i=0; i<rawData.length;i++){
reply[bLim] = rawData[i];
bLim++;
}
out.write(reply);
out.flush();
}
JavaScript implementation:
function encodeWebSocket(bytesRaw){
var bytesFormatted = new Array();
bytesFormatted[0] = 129;
if (bytesRaw.length <= 125) {
bytesFormatted[1] = bytesRaw.length;
} else if (bytesRaw.length >= 126 && bytesRaw.length <= 65535) {
bytesFormatted[1] = 126;
bytesFormatted[2] = ( bytesRaw.length >> 8 ) & 255;
bytesFormatted[3] = ( bytesRaw.length ) & 255;
} else {
bytesFormatted[1] = 127;
bytesFormatted[2] = ( bytesRaw.length >> 56 ) & 255;
bytesFormatted[3] = ( bytesRaw.length >> 48 ) & 255;
bytesFormatted[4] = ( bytesRaw.length >> 40 ) & 255;
bytesFormatted[5] = ( bytesRaw.length >> 32 ) & 255;
bytesFormatted[6] = ( bytesRaw.length >> 24 ) & 255;
bytesFormatted[7] = ( bytesRaw.length >> 16 ) & 255;
bytesFormatted[8] = ( bytesRaw.length >> 8 ) & 255;
bytesFormatted[9] = ( bytesRaw.length ) & 255;
}
for (var i = 0; i < bytesRaw.length; i++){
bytesFormatted.push(bytesRaw.charCodeAt(i));
}
return bytesFormatted;
}
function decodeWebSocket (data){
var datalength = data[1] & 127;
var indexFirstMask = 2;
if (datalength == 126) {
indexFirstMask = 4;
} else if (datalength == 127) {
indexFirstMask = 10;
}
var masks = data.slice(indexFirstMask,indexFirstMask + 4);
var i = indexFirstMask + 4;
var index = 0;
var output = "";
while (i < data.length) {
output += String.fromCharCode(data[i++] ^ masks[index++ % 4]);
}
return output;
}
C# Implementation
Browser -> Server
private String DecodeMessage(Byte[] bytes)
{
String incomingData = String.Empty;
Byte secondByte = bytes[1];
Int32 dataLength = secondByte & 127;
Int32 indexFirstMask = 2;
if (dataLength == 126)
indexFirstMask = 4;
else if (dataLength == 127)
indexFirstMask = 10;
IEnumerable<Byte> keys = bytes.Skip(indexFirstMask).Take(4);
Int32 indexFirstDataByte = indexFirstMask + 4;
Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
for (Int32 i = indexFirstDataByte, j = 0; i < bytes.Length; i++, j++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
}
return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}
Server -> Browser
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
{
frame[1] = (Byte)127;
frame[2] = (Byte)((length >> 56) & 255);
frame[3] = (Byte)((length >> 48) & 255);
frame[4] = (Byte)((length >> 40) & 255);
frame[5] = (Byte)((length >> 32) & 255);
frame[6] = (Byte)((length >> 24) & 255);
frame[7] = (Byte)((length >> 16) & 255);
frame[8] = (Byte)((length >> 8) & 255);
frame[9] = (Byte)(length & 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;
}
pimvdb's answer implemented in python:
def DecodedCharArrayFromByteStreamIn(stringStreamIn):
#turn string values into opererable numeric byte values
byteArray = [ord(character) for character in stringStreamIn]
datalength = byteArray[1] & 127
indexFirstMask = 2
if datalength == 126:
indexFirstMask = 4
elif datalength == 127:
indexFirstMask = 10
masks = [m for m in byteArray[indexFirstMask : indexFirstMask+4]]
indexFirstDataByte = indexFirstMask + 4
decodedChars = []
i = indexFirstDataByte
j = 0
while i < len(byteArray):
decodedChars.append( chr(byteArray[i] ^ masks[j % 4]) )
i += 1
j += 1
return decodedChars
An Example of usage:
fromclient = '\x81\x8c\xff\xb8\xbd\xbd\xb7\xdd\xd1\xd1\x90\x98\xea\xd2\x8d\xd4\xd9\x9c'
# this looks like "?ŒOÇ¿¢gÓ ç\Ð=«ož" in unicode, received by server
print DecodedCharArrayFromByteStreamIn(fromclient)
# ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!']
In addition to the PHP frame encoding function, here follows a decode function:
function Decode($M){
$M = array_map("ord", str_split($M));
$L = $M[1] AND 127;
if ($L == 126)
$iFM = 4;
else if ($L == 127)
$iFM = 10;
else
$iFM = 2;
$Masks = array_slice($M, $iFM, 4);
$Out = "";
for ($i = $iFM + 4, $j = 0; $i < count($M); $i++, $j++ ) {
$Out .= chr($M[$i] ^ $Masks[$j % 4]);
}
return $Out;
}
I've implemented this and also other functions in an easy-to-use WebSocket PHP class here.
PHP Implementation:
function encode($message)
{
$length = strlen($message);
$bytesHeader = [];
$bytesHeader[0] = 129; // 0x1 text frame (FIN + opcode)
if ($length <= 125) {
$bytesHeader[1] = $length;
} else if ($length >= 126 && $length <= 65535) {
$bytesHeader[1] = 126;
$bytesHeader[2] = ( $length >> 8 ) & 255;
$bytesHeader[3] = ( $length ) & 255;
} else {
$bytesHeader[1] = 127;
$bytesHeader[2] = ( $length >> 56 ) & 255;
$bytesHeader[3] = ( $length >> 48 ) & 255;
$bytesHeader[4] = ( $length >> 40 ) & 255;
$bytesHeader[5] = ( $length >> 32 ) & 255;
$bytesHeader[6] = ( $length >> 24 ) & 255;
$bytesHeader[7] = ( $length >> 16 ) & 255;
$bytesHeader[8] = ( $length >> 8 ) & 255;
$bytesHeader[9] = ( $length ) & 255;
}
$str = implode(array_map("chr", $bytesHeader)) . $message;
return $str;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With