Wie kann ich WebSocket-Nachrichten auf der Serverseite senden und empfangen?

85
  • Wie kann ich mit WebSocket gemäß Protokoll Protokolle auf der Serverseite senden und empfangen?

  • Warum erhalte ich scheinbar zufällige Bytes auf dem Server, wenn ich Daten vom Browser an den Server sende? Sind die Daten irgendwie verschlüsselt?

  • Wie funktioniert das Framing in den Richtungen Server → Client und Client → Server?

pimvdb
quelle

Antworten:

153

Hinweis: Dies ist eine Erklärung und ein Pseudocode zur Implementierung eines sehr einfachen Servers, der eingehende und ausgehende WebSocket-Nachrichten gemäß dem endgültigen Rahmenformat verarbeiten kann. Der Handshake-Prozess ist nicht enthalten. Darüber hinaus wurde diese Antwort zu Bildungszwecken gegeben; Es handelt sich nicht um eine Implementierung mit vollem Funktionsumfang.

Spezifikation (RFC 6455)


Nachrichten senden

(Mit anderen Worten, Server → Browser)

Die von Ihnen gesendeten Frames müssen gemäß dem WebSocket-Framing-Format formatiert sein. Zum Senden von Nachrichten lautet dieses Format wie folgt:

  • ein Byte, das den Datentyp enthält (und einige zusätzliche Informationen, die für einen einfachen Server nicht verfügbar sind)
  • ein Byte, das die Länge enthält
  • entweder zwei oder acht Bytes, wenn die Länge nicht in das zweite Byte passt (das zweite Byte ist dann ein Code, der angibt, wie viele Bytes für die Länge verwendet werden)
  • die tatsächlichen (Roh-) Daten

Das erste Byte ist 1000 0001(oder 129) für einen Textrahmen.

Für das zweite Byte ist das erste Bit gesetzt, 0da die Daten nicht codiert werden (die Codierung vom Server zum Client ist nicht obligatorisch).

Es ist notwendig, die Länge der Rohdaten zu bestimmen, um die Längenbytes korrekt zu senden:

  • Wenn 0 <= length <= 125, brauchen Sie keine zusätzlichen Bytes
  • Wenn 126 <= length <= 65535Sie zwei zusätzliche Bytes benötigen, ist das zweite Byte126
  • Wenn length >= 65536, benötigen Sie acht zusätzliche Bytes, und das zweite Byte ist127

Die Länge muss in separate Bytes aufgeteilt werden, was bedeutet, dass Sie die Bitverschiebung nach rechts (mit einer Menge von acht Bits) durchführen und dann nur die letzten acht Bits beibehalten müssen AND 1111 1111(dh 255).

Nach den Längenbytes folgen die Rohdaten.

Dies führt zu folgendem 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)

Nachrichten empfangen

(Mit anderen Worten, Browser → Server)

Die Frames, die Sie erhalten, haben das folgende Format:

  • Ein Byte, das den Datentyp enthält
  • ein Byte, das die Länge enthält
  • entweder zwei oder acht zusätzliche Bytes, wenn die Länge nicht in das zweite Byte passt
  • vier Bytes, die die Masken sind (= Decodierungsschlüssel)
  • die tatsächlichen Daten

Das erste Byte spielt normalerweise keine Rolle - wenn Sie nur Text senden, verwenden Sie nur den Texttyp. Es wird 1000 0001(oder 129) in diesem Fall sein.

Das zweite Byte und die zusätzlichen zwei oder acht Bytes müssen analysiert werden, da Sie wissen müssen, wie viele Bytes für die Länge verwendet werden (Sie müssen wissen, wo die realen Daten beginnen). Die Länge selbst ist normalerweise nicht erforderlich, da Sie die Daten bereits haben.

Das erste Bit des zweiten Bytes ist immer, 1was bedeutet, dass die Daten maskiert (= codiert) sind. Nachrichten vom Client an den Server werden immer maskiert. Sie müssen das erste Bit entfernen, indem Sie dies tun secondByte AND 0111 1111. Es gibt zwei Fälle, in denen das resultierende Byte nicht die Länge darstellt, weil es nicht in das zweite Byte passt:

  • Ein zweites Byte von 0111 1110oder 126bedeutet, dass die folgenden zwei Bytes für die Länge verwendet werden
  • Ein zweites Byte von 0111 1111oder 127bedeutet, dass die folgenden acht Bytes für die Länge verwendet werden

Die vier Maskenbytes werden zum Decodieren der tatsächlich gesendeten Daten verwendet. Der Algorithmus zum Decodieren ist wie folgt:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

Wo encodedByteist das ursprüngliche Byte in den Daten, encodedByteIndexist der Index (Offset) des Bytes, der vom ersten Byte der realen Daten mit Index zählt 0.masksist ein Array, das die vier Maskenbytes enthält.

Dies führt zu folgendem Pseudocode zum Decodieren:

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
pimvdb
quelle
Warum 1000 0001(129) für einen Textrahmen? Die Spezifikation sagt : %x1 denotes a text frame. Also sollte es 0000 0001( 0x01) sein, oder?
Dennis
3
@ Tennis: Der Frame-Opcode lautet 0001, wie im Header dieses Teils der Spezifikation angegeben: "Opcode: 4 Bit". Das erste Byte besteht aus FIN, RSV1-3 und Opcode. FIN ist 1, RSV1-3 sind alle drei 0und der Opcode 0001summiert sich 1000 0001für das erste Byte. Sehen Sie sich auch das Bildmaterial in der Spezifikation an, das zeigt, wie die Bytes in die verschiedenen Teile aufgeteilt sind.
Pimvdb
Sie haben ein paar Zeilen, die im Server-> Client-Modell wie folgt lauten: 'bytesFormatted [2] = (bytesRaw.length >> 56) AND 255'. Würde es Ihnen etwas ausmachen, das für mich aufzuschlüsseln? UND scheint mir ein logischer Operator zu sein, daher kann ich nicht erwarten, dass das einfache Setzen einer Zahl danach in C # etwas für mich bedeutet. Ebenso bin ich mir nicht sicher, was das ">>" in Ihrem Markup anzeigen soll - es wird jedoch auf C # übertragen ... Was auch immer das für mich bedeutet ...: P
DigitalJedi805
Wenn jemand dies für mich klären kann, werde ich gerne meine C # -Implementierung als Antwort veröffentlichen.
DigitalJedi805
1
@Neevek: Was sie bedeuten ist, dass die Maskenbytes selbst unvorhersehbar sein müssen. Wenn sie konstant sind, hat das nicht viel Sinn. Wenn ein böswilliger Benutzer über einen Datenabschnitt verfügt, sollte er diesen grundsätzlich nicht ohne die Masken dekodieren können. Wenn die Position der Maske nicht vorhersehbar ist, ist es für den echten Server etwas schwierig zu dekodieren :)
pimvdb
26

Java-Implementierung (falls erforderlich)

Lesen: Client zu 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];

            }
        }

Schreiben: Server zu 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();

}
Haribabu Pasupathy
quelle
3
Was wäre eine geeignete Pufferlänge für die Leseoperation?
Jackgerrits
Leider funktioniert es nicht. Ich habe gerade void Broadcast (vom Server zum Client) in mein Programm kopiert. Socket erfolgreich verbunden, Nachricht erfolgreich an Browser gesendet, aber nichts vom Browser empfangen.
Nick
18

JavaScript-Implementierung:

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;
}
Richard Astbury
quelle
5
Wahrscheinlich erwähnenswert, dass JavaScript das Verschieben mit Zahlen größer als nicht unterstützt 2^31 - 1.
Pimvdb
13

C # -Implementierung

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;
    }
Nitij
quelle
1
Die Dekodierungsfunktion gibt meine spezifische Nachricht immer mit einem für mich undefinierten Anhang wie hier zurück, test�c=ܝX[in dem "Test" meine Nachricht ist. Woher kommt der andere Teil?
Snickbrack
1
Entschuldigung für die späte Antwort. Ich habe eine kleine C # -Anwendung (Konsole und Web) erstellt, um Web-Sockets auszuprobieren. Sie können sie hier herunterladen, um zu sehen, wie sie codiert sind. Link: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij
Dies ist bei großen Nachrichten für mich fehlgeschlagen. Ich habe den Code für Länge> 65535 ersetzt durch: var l = Convert.ToUInt64 (Länge); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b.CopyTo (Rahmen, 2); ... was feste Dinge zu haben scheint.
Sean
Gut gemacht. Nur eines: Bei DecodeMessage berechne ich die "decodierte" Array-Länge basierend auf den Payload-Längen-Daten, die im Datenrahmen enthalten sind, da die "Bytes" -Array-Länge nicht genau sein konnte. Die Array-Länge "Bytes" hängt von der Art und Weise ab, wie der Stream gelesen wird.
user1011138
@ Sean, können Sie mir Ihr vollständiges Beispiel für die Behebung eines Problems mit großen Nachrichten zeigen? Ich kann diesen Code nicht in Ihr Beispiel ändern.
Ali Yousefi
6

Die Antwort von pimvdb wurde in Python implementiert:

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

Ein Anwendungsbeispiel:

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', '!']
Hunter Fernandes
quelle
Ich habe versucht, Ihren Code in meinem Skript zu verwenden, aber ohne Erfolg. Könnten Sie vielleicht helfen können? stackoverflow.com/questions/43748377/…
Yak
5

Zusätzlich zur PHP-Frame-Codierungsfunktion folgt hier eine Decodierungsfunktion:

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;
}

Ich habe diese und auch andere Funktionen in einer benutzerfreundlichen WebSocket PHP-Klasse hier implementiert .

Tacticus
quelle
4

PHP-Implementierung:

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;
}
DanBlack
quelle
4

Vielen Dank für die Antwort, ich möchte die Python-Version von hfern (oben) erweitern, um die Sendefunktion einzuschließen, wenn jemand interessiert ist.

def DecodedWebsockRecieve(stringStreamIn):
    byteArray =  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 ''.join(decodedChars)

def EncodeWebSockSend(socket,data):
    bytesFormatted = []
    bytesFormatted.append(129)

    bytesRaw = data.encode()
    bytesLength = len(bytesRaw)
    if bytesLength <= 125 :
        bytesFormatted.append(bytesLength)
    elif bytesLength >= 126 and bytesLength <= 65535 :
        bytesFormatted.append(126)
        bytesFormatted.append( ( bytesLength >> 8 ) & 255 )
        bytesFormatted.append( bytesLength & 255 )
    else :
        bytesFormatted.append( 127 )
        bytesFormatted.append( ( bytesLength >> 56 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 48 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 40 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 32 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 24 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 16 ) & 255 )
        bytesFormatted.append( ( bytesLength >>  8 ) & 255 )
        bytesFormatted.append( bytesLength & 255 )

    bytesFormatted = bytes(bytesFormatted)
    bytesFormatted = bytesFormatted + bytesRaw
    socket.send(bytesFormatted) 

Verwendung zum Lesen:

bufSize = 1024     
read = DecodedWebsockRecieve(socket.recv(bufSize))

Verwendung zum Schreiben:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
DR.
quelle
2

Implementierung in Go

Teil verschlüsseln (Server -> Browser)

func encode (message string) (result []byte) {
  rawBytes := []byte(message)
  var idxData int

  length := byte(len(rawBytes))
  if len(rawBytes) <= 125 { //one byte to store data length
    result = make([]byte, len(rawBytes) + 2)
    result[1] = length
    idxData = 2
  } else if len(rawBytes) >= 126 && len(rawBytes) <= 65535 { //two bytes to store data length
    result = make([]byte, len(rawBytes) + 4)
    result[1] = 126 //extra storage needed
    result[2] = ( length >> 8 ) & 255
    result[3] = ( length      ) & 255
    idxData = 4
  } else {
    result = make([]byte, len(rawBytes) + 10)
    result[1] = 127
    result[2] = ( length >> 56 ) & 255
    result[3] = ( length >> 48 ) & 255
    result[4] = ( length >> 40 ) & 255
    result[5] = ( length >> 32 ) & 255
    result[6] = ( length >> 24 ) & 255
    result[7] = ( length >> 16 ) & 255
    result[8] = ( length >>  8 ) & 255
    result[9] = ( length       ) & 255
    idxData = 10
  }

  result[0] = 129 //only text is supported

  // put raw data at the correct index
  for i, b := range rawBytes {
    result[idxData + i] = b
  }
  return
}

Teil dekodieren (Browser -> Server)

func decode (rawBytes []byte) string {
  var idxMask int
  if rawBytes[1] == 126 {
    idxMask = 4
  } else if rawBytes[1] == 127 {
    idxMask = 10
  } else {
    idxMask = 2
  }

  masks := rawBytes[idxMask:idxMask + 4]
  data := rawBytes[idxMask + 4:len(rawBytes)]
  decoded := make([]byte, len(rawBytes) - idxMask + 4)

  for i, b := range data {
    decoded[i] = b ^ masks[i % 4]
  }
  return string(decoded)
}
Rmonjo
quelle
2

Clojure, die Decodierungsfunktion setzt voraus, dass der Frame als Map von gesendet wird {:data byte-array-buffer :size int-size-of-buffer} , dass der , da die tatsächliche Größe je nach Blockgröße Ihres Eingabestreams möglicherweise nicht mit der Größe des Byte-Arrays übereinstimmt.

Code hier veröffentlicht: https://gist.github.com/viperscape/8918565

(defn ws-decode [frame]
  "decodes websocket frame"
  (let [data (:data frame)
        dlen (bit-and (second data) 127)
        mstart (if (== dlen 127) 10 (if (== dlen 126) 4 2))
        mask (drop 2 (take (+ mstart 4) data))
        msg (make-array Byte/TYPE (- (:size frame) (+ mstart 4)))]
   (loop [i (+ mstart 4), j 0]
      (aset-byte msg j (byte (bit-xor (nth data i) (nth mask (mod j 4)))))
      (if (< i (dec(:size frame))) (recur (inc i) (inc j))))
    msg))

(defn ws-encode [data]
  "takes in bytes, return websocket frame"
  (let [len (count data)
        blen (if (> len 65535) 10 (if (> len 125) 4 2))
        buf (make-array Byte/TYPE (+ len blen))
        _ (aset-byte buf 0 -127) ;;(bit-or (unchecked-byte 0x80) 
                                           (unchecked-byte 0x1)
        _ (if (= 2 blen) 
            (aset-byte buf 1 len) ;;mask 0, len
            (do
              (dorun(map #(aset-byte buf %1 
                      (unchecked-byte (bit-and (bit-shift-right len (*(- %2 2) 8))
                                               255)))
                      (range 2 blen) (into ()(range 2 blen))))
              (aset-byte buf 1 (if (> blen 4) 127 126))))
        _ (System/arraycopy data 0 buf blen len)]
    buf))
scape
quelle
0

C ++ Implementierung (nicht von mir) hier . Beachten Sie, dass Sie, wenn Ihre Bytes über 65535 liegen, mit einem langen Wert verschieben müssen, wie hier gezeigt .

pcunite
quelle