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?
Réponses
Trop de publicités?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.
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 1110
ou126
, soit deux octets sont utilisés pour la longueur - un deuxième octet de l'
0111 1111
ou127
, 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]
où 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
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();
}
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;
}
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', '!']
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;
}