92 votes

Comment puis-je envoyer et recevoir des messages WebSocket sur le serveur?

  • Comment puis-je envoyer et recevoir des messages sur le serveur à l'aide de WebSocket, comme par le protocole?

  • Pourquoi puis-je obtenir apparemment octets aléatoires au niveau du serveur lors de l'envoi de données entre le navigateur et le serveur? Les données codées en quelque sorte?

  • Comment le cadrage de travail dans le serveur → client et client → serveur directions?

161voto

pimvdb Points 66332

Note: Ceci est une explication et une pseudo-code dans la mise en œuvre d'un très trivial serveur peut gérer entrant et sortant WebSocket messages par l'définitive format de trame. Il ne comprend pas l'établissement de la liaison. En outre, cette réponse a été faite à des fins éducatives; ce n'est pas une complète mise en œuvre.

Cahier des charges (RFC 6455)


L'envoi de messages

(En d'autres termes, le serveur → navigateur)

Les images que vous envoyez doivent être formatés selon les WebSocket format de trame. Pour l'envoi de messages, ce format est comme suit:

  • un octet qui contient le type de données (et quelques informations supplémentaires qui est hors de portée pour un trivial serveur)
  • un octet qui contient la longueur
  • deux ou huit octets si la longueur ne rentre pas dans le deuxième octet (le deuxième octet est alors un code à dire combien d'octets sont utilisés pour la longueur)
  • le réel (raw) de données

Le premier octet sera 1000 0001 (ou 129) pour un bloc de texte.

Le deuxième octet a son premier ensemble de bits de 0 parce que nous ne sommes pas de codage de données (encodage à partir du serveur vers le client n'est pas obligatoire).

Il est nécessaire de déterminer la longueur des données brutes de manière à envoyer les octets de longueur correctement:

  • si 0 <= length <= 125, vous n'avez pas besoin d'octets supplémentaires
  • si 126 <= length <= 65535, vous avez besoin de deux octets supplémentaires et le deuxième octet est - 126
  • si length >= 65536, vous avez besoin de huit octets supplémentaires, et le deuxième octet est - 127

La longueur doit être tranché en séparer octets, ce qui signifie que vous aurez besoin de décalage de bit vers la droite (avec un montant de huit bits), et ensuite seulement de conserver les huit derniers bits en faisant AND 1111 1111 (ce qui est 255).

Après la longueur en octets(s) entre les données brutes.

Cela conduit à la pseudo-code suivant:

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)

La réception de messages

(En d'autres termes, navigateur → serveur)

Les images que vous obtenez sont dans le format suivant:

  • un octet qui contient le type de données
  • un octet qui contient la longueur
  • deux ou huit octets supplémentaires si la longueur n'a pas de place dans la deuxième octet
  • quatre octets qui sont les masques (= décodage des clés)
  • les données réelles

Le premier octet d'habitude n'a pas d'importance si vous êtes juste à envoyer le texte que vous êtes seulement en utilisant le type de texte. Il sera 1000 0001 (ou 129) dans ce cas.

Le deuxième octet et la prolongation de deux ou huit octets besoin d'une certaine analyse, parce que vous avez besoin de savoir combien d'octets sont utilisés pour la longueur (vous avez besoin de savoir où sont les données réelles commence). La longueur elle-même n'est généralement pas nécessaire puisque vous avez les données déjà.

Les premiers bits du deuxième octet est toujours 1 ce qui signifie que les données sont masquées (= codé). Les Messages du client vers le serveur sont toujours masqués. Vous devez supprimer que le premier bit en faisant secondByte AND 0111 1111. Il y a deux cas dans lesquels l'octet qui en résulte ne représente pas la longueur, car il n'entrait pas dans le deuxième octet:

  • un deuxième octet de l' 0111 1110ou 126, soit deux octets sont utilisés pour la longueur
  • un deuxième octet de l' 0111 1111ou 127, signifie que les huit octets sont utilisés pour la longueur

Les quatre masque octets sont utilisés pour le décodage des données réelles qui ont été envoyées. L'algorithme de décodage est comme suit:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

encodedByte est à l'origine d'octets de données dans les données, encodedByteIndex est l'indice (offset) de l'octet en comptant à partir du premier octet de données réelles, ce qui est l'indice 0. masks est un tableau contenant des quatre masque octets.

Cela conduit à la pseudo-code suivant pour le décodage:

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

25voto

Java mise en œuvre (le cas échéant on exige)

Lecture : le Client vers le Serveur

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

            }
        }

L'écriture : le Serveur vers le 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();

}

18voto

Richard Astbury Points 1638

JavaScript mise en œuvre:

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

6voto

hfern Points 502

pimvdb la réponse de implémenté en 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

Un Exemple d'utilisation:

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', '!']

4voto

DanBlack Points 1

PHP de mise en Œuvre:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X