Làm cách nào để gửi và nhận các thông báo WebSocket ở phía máy chủ?


85
  • Làm cách nào tôi có thể gửi và nhận tin nhắn ở phía máy chủ bằng cách sử dụng WebSocket, theo giao thức?

  • Tại sao tôi nhận được các byte dường như ngẫu nhiên tại máy chủ khi tôi gửi dữ liệu từ trình duyệt đến máy chủ? Đó là dữ liệu được mã hóa bằng cách nào đó?

  • Khung hoạt động như thế nào trong cả hướng máy chủ → máy khách và máy khách → máy chủ?

Câu trả lời:


154

Lưu ý: Đây là một số giải thích và mã giả về cách triển khai một máy chủ rất tầm thường có thể xử lý các thông báo WebSocket đến và đi theo định dạng khung cuối cùng. Nó không bao gồm quá trình bắt tay. Hơn nữa, câu trả lời này đã được thực hiện cho mục đích giáo dục; nó không phải là một triển khai đầy đủ tính năng.

Đặc điểm kỹ thuật (RFC 6455)


Gửi tin nhắn

(Nói cách khác, máy chủ → trình duyệt)

Các khung bạn đang gửi cần được định dạng theo định dạng khung WebSocket. Để gửi tin nhắn, định dạng này như sau:

  • một byte chứa loại dữ liệu (và một số thông tin bổ sung nằm ngoài phạm vi của một máy chủ tầm thường)
  • một byte chứa độ dài
  • hai hoặc tám byte nếu độ dài không vừa với byte thứ hai (byte thứ hai sau đó là mã cho biết có bao nhiêu byte được sử dụng cho độ dài)
  • dữ liệu thực tế (thô)

Byte đầu tiên sẽ là 1000 0001(hoặc 129) cho một khung văn bản.

Byte thứ hai có bit đầu tiên được đặt thành 0vì chúng tôi không mã hóa dữ liệu (không bắt buộc phải mã hóa từ máy chủ đến máy khách).

Cần phải xác định độ dài của dữ liệu thô để gửi độ dài byte một cách chính xác:

  • nếu 0 <= length <= 125, bạn không cần thêm byte
  • nếu 126 <= length <= 65535, bạn cần thêm hai byte và byte thứ hai là126
  • nếu length >= 65536, bạn cần tám byte bổ sung và byte thứ hai là127

Độ dài phải được chia thành các byte riêng biệt, có nghĩa là bạn sẽ cần phải dịch chuyển bit sang phải (với số lượng là tám bit), và sau đó chỉ giữ lại tám bit cuối cùng bằng cách thực hiện AND 1111 1111(chính là 255).

Sau (các) byte độ dài là dữ liệu thô.

Điều này dẫn đến mã giả sau:

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)

Nhận tin nhắn

(Nói cách khác, trình duyệt → máy chủ)

Các khung bạn thu được có định dạng sau:

  • một byte chứa loại dữ liệu
  • một byte chứa độ dài
  • hai hoặc tám byte bổ sung nếu độ dài không vừa với byte thứ hai
  • bốn byte là mặt nạ (= khóa giải mã)
  • dữ liệu thực tế

Byte đầu tiên thường không quan trọng - nếu bạn chỉ gửi văn bản, bạn chỉ đang sử dụng kiểu văn bản. Nó sẽ là 1000 0001(hoặc 129) trong trường hợp đó.

Byte thứ hai và hai hoặc tám byte bổ sung cần một số phân tích cú pháp, vì bạn cần biết có bao nhiêu byte được sử dụng cho độ dài (bạn cần biết dữ liệu thực bắt đầu từ đâu). Bản thân độ dài thường không cần thiết vì bạn đã có dữ liệu.

Bit đầu tiên của byte thứ hai luôn luôn 1có nghĩa là dữ liệu bị che (= được mã hóa). Tin nhắn từ máy khách đến máy chủ luôn bị che. Bạn cần loại bỏ bit đầu tiên bằng cách thực hiện secondByte AND 0111 1111. Có hai trường hợp mà byte kết quả không đại diện cho độ dài vì nó không vừa với byte thứ hai:

  • một byte thứ hai của 0111 1110hoặc 126, có nghĩa là hai byte sau được sử dụng cho độ dài
  • một byte thứ hai của 0111 1111hoặc 127, có nghĩa là tám byte sau được sử dụng cho độ dài

Bốn byte mặt nạ được sử dụng để giải mã dữ liệu thực tế đã được gửi. Thuật toán giải mã như sau:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

đâu encodedBytelà byte gốc trong dữ liệu, encodedByteIndexlà chỉ số (độ lệch) của byte đếm từ byte đầu tiên của dữ liệu thực , có chỉ mục 0. maskslà một mảng chứa bốn byte mặt nạ.

Điều này dẫn đến mã giả sau để giải mã:

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

Tại sao 1000 0001(129) cho một khung văn bản? Spec nói nói: %x1 denotes a text frame. Vì vậy, nó phải là 0000 0001( 0x01), hoặc?
Dennis

3
@Dennis: Opcode khung là 0001, như nó nói ở tiêu đề của phần thông số kỹ thuật đó: "Opcode: 4 bit". Byte đầu tiên bao gồm FIN, RSV1-3 và opcode. FIN là 1, RSV1-3 là cả ba 0và opcode là mã 0001bổ sung 1000 0001cho byte đầu tiên. Ngoài ra, hãy xem tác phẩm nghệ thuật trong thông số kỹ thuật hiển thị cách các byte được chia thành các phần khác nhau.
pimvdb

Bạn có một vài dòng đọc như 'bytesFormatted [2] = (bytesRaw.length >> 56) AND 255' trong mô hình Máy chủ-> Máy khách - Bạn có phiền chia nhỏ điều đó giúp tôi không? AND dường như là một toán tử logic đối với tôi vì vậy tôi không thể mong đợi rằng chỉ cần đặt một số sau nó sẽ làm bất cứ điều gì cho tôi trong C #. Tương tự như vậy, tôi không chắc chắn những gì ">>" trong đánh dấu của bạn có nghĩa vụ phải chỉ ra - tuy nhiên nó không chuyển giao cho C # ... Bất cứ điều gì đó có nghĩa với tôi ...: P
DigitalJedi805

Nếu ai đó thực sự có thể giải quyết vấn đề này cho tôi, tôi sẽ rất vui được đăng việc triển khai C # của tôi như một câu trả lời.
DigitalJedi805

1
@Neevek: Ý của họ là bản thân các byte mặt nạ cần phải không thể đoán trước. Nếu chúng không đổi thì không có nhiều điểm trong chúng. Về cơ bản, khi một người dùng độc hại có một phần dữ liệu, anh ta sẽ không thể giải mã nó mà không có mặt nạ. Nếu mặt nạ vị trí là không thể dự đoán được thì đó là một chút khó khăn cho các máy chủ chính hãng để decode :)
pimvdb

26

Triển khai Java (nếu có yêu cầu)

Đọc: Máy khách đến Máy chủ

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

            }
        }

Viết: Máy chủ đến Máy khách

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();

}

3
Độ dài bộ đệm thích hợp cho thao tác đọc là bao nhiêu?
jackgerrits

Thật không may, nó không hoạt động. Tôi vừa sao chép void broadcast (từ Server sang Client) vào chương trình của mình. Socket kết nối thành công, thông báo gửi đến trình duyệt thành công, nhưng trình duyệt không nhận được gì.
nick

18

Triển khai JavaScript:

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

5
Có thể đáng lưu ý rằng JavaScript không thực sự hỗ trợ chuyển đổi với các số lớn hơn 2^31 - 1.
pimvdb

13

Thực hiện C #

Trình duyệt -> Máy chủ

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

Máy chủ -> Trình duyệt

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

1
Chức năng giải mã luôn trả về tin nhắn cụ thể của tôi với một phụ lục không xác định cho tôi như ở đây, test�c=ܝX[trong đó "test" là tin nhắn của tôi. Phần khác đến từ cái gì?
Snickbrack

1
Xin lỗi vì hồi âm muộn. Tôi đã tạo một ứng dụng C # nhỏ (Bảng điều khiển và Web) để thử các ổ cắm web. Bạn có thể tải chúng xuống từ đây để xem nó được mã hóa như thế nào. Liên kết: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij

Điều này không thành công đối với tôi trên các tin nhắn lớn. Tôi đã thay thế mã length> 65535 bằng: var l = Convert.ToUInt64 (length); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b.CopyTo (khung, 2); ... mà dường như có những thứ cố định.
Sean

Làm tốt lắm. Chỉ có một điều: Trên DecodeMessage, tôi đang tính toán độ dài mảng "đã giải mã" dựa trên dữ liệu độ dài Tải trọng được bao gồm trên độ dài mảng "byte" khung dữ liệu không thể chính xác. Độ dài mảng "byte" phụ thuộc vào cách đọc luồng.
user1011138

@Sean, bạn có thể cho tôi xem ví dụ đầy đủ của bạn để khắc phục sự cố tin nhắn lớn không? tôi không thể thay đổi mã đó thành mẫu của bạn.
Ali Yousefi

6

Câu trả lời của pimvdb được triển khai trong 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

Ví dụ về cách sử dụng:

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

Tôi đã cố gắng sử dụng mã của bạn trong tập lệnh của mình, nhưng không thành công. Bạn có thể có thể giúp đỡ? stackoverflow.com/questions/43748377/…
yak

5

Ngoài hàm mã hóa khung PHP, đây là một hàm giải mã:

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

Tôi đã triển khai chức năng này và các chức năng khác trong một lớp PHP WebSocket dễ sử dụng tại đây .


4

Triển khai PHP:

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

4

Cảm ơn bạn đã trả lời, tôi muốn thêm vào phiên bản Python của hfern (ở trên) để bao gồm chức năng Gửi nếu bất kỳ ai quan tâm.

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) 

Cách sử dụng để đọc:

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

Cách sử dụng để viết:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")

2

Triển khai trong Go

Phần mã hóa (máy chủ -> trình duyệt)

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
}

Phần giải mã (trình duyệt -> máy chủ)

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

2

Clojure, hàm giải mã giả định rằng khung được gửi dưới dạng bản đồ {:data byte-array-buffer :size int-size-of-buffer}, bởi vì kích thước thực tế có thể không cùng kích thước với mảng byte tùy thuộc vào kích thước phân đoạn của dòng đầu vào của bạn.

Code được đăng tại đây: 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))

0

Triển khai C ++ (không phải bởi tôi) ở đây . Lưu ý rằng khi byte của bạn lớn hơn 65535, bạn cần phải thay đổi với một giá trị dài như được hiển thị ở đây .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.