ฉันจะส่งและรับข้อความ WebSocket ทางฝั่งเซิร์ฟเวอร์ได้อย่างไร?


86
  • ฉันจะส่งและรับข้อความทางฝั่งเซิร์ฟเวอร์โดยใช้ WebSocket ตามโปรโตคอลได้อย่างไร

  • เหตุใดฉันจึงดูเหมือนสุ่มไบต์ที่เซิร์ฟเวอร์เมื่อฉันส่งข้อมูลจากเบราว์เซอร์ไปยังเซิร์ฟเวอร์ ข้อมูลถูกเข้ารหัสอย่างใด?

  • การจัดเฟรมทำงานอย่างไรในทิศทางเซิร์ฟเวอร์→ไคลเอนต์และไคลเอนต์→เซิร์ฟเวอร์

คำตอบ:


154

หมายเหตุ: นี่คือคำอธิบายและรหัสเทียมเกี่ยวกับวิธีการใช้งานเซิร์ฟเวอร์ที่ไม่สำคัญซึ่งสามารถจัดการกับข้อความ WebSocket ขาเข้าและขาออกตามรูปแบบการจัดเฟรมขั้นสุดท้าย ยังไม่รวมขั้นตอนการจับมือ นอกจากนี้คำตอบนี้จัดทำขึ้นเพื่อวัตถุประสงค์ทางการศึกษา ไม่ใช่การใช้งานแบบเต็มรูปแบบ

ข้อมูลจำเพาะ (RFC 6455)


การส่งข้อความ

(กล่าวอีกนัยหนึ่งเซิร์ฟเวอร์→เบราว์เซอร์)

เฟรมที่คุณส่งจะต้องได้รับการจัดรูปแบบตามรูปแบบเฟรม WebSocket สำหรับการส่งข้อความรูปแบบนี้มีดังนี้:

  • หนึ่งไบต์ซึ่งมีประเภทของข้อมูล (และข้อมูลเพิ่มเติมบางอย่างที่อยู่นอกขอบเขตสำหรับเซิร์ฟเวอร์เล็กน้อย)
  • หนึ่งไบต์ซึ่งมีความยาว
  • สองหรือแปดไบต์หากความยาวไม่พอดีกับไบต์ที่สอง (ไบต์ที่สองจะเป็นรหัสที่บอกว่าใช้ความยาวกี่ไบต์)
  • ข้อมูลจริง (ดิบ)

ไบต์แรกจะเป็น1000 0001(หรือ129) สำหรับกรอบข้อความ

ไบต์ที่สองมีบิตแรกที่ตั้งค่าเป็น0เนื่องจากเราไม่ได้เข้ารหัสข้อมูล (การเข้ารหัสจากเซิร์ฟเวอร์ไปยังไคลเอนต์ไม่บังคับ)

จำเป็นต้องกำหนดความยาวของข้อมูลดิบเพื่อที่จะส่งไบต์ความยาวได้อย่างถูกต้อง:

  • ถ้า0 <= length <= 125คุณไม่ต้องการไบต์เพิ่มเติม
  • ถ้า126 <= length <= 65535คุณต้องการสองไบต์เพิ่มเติมและไบต์ที่สองคือ126
  • ถ้าlength >= 65536คุณต้องการแปดไบต์เพิ่มเติมและไบต์ที่สองคือ127

ความยาวจะต้องถูกแบ่งออกเป็นไบต์ที่แยกจากกันซึ่งหมายความว่าคุณจะต้องเลื่อนบิตไปทางขวา (ด้วยจำนวนแปดบิต) จากนั้นคงไว้เฉพาะแปดบิตสุดท้ายโดยทำAND 1111 1111(ซึ่งก็คือ255)

หลังจากความยาวไบต์มาข้อมูลดิบ

สิ่งนี้นำไปสู่ ​​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)

การรับข้อความ

(กล่าวอีกนัยหนึ่งคือเบราว์เซอร์→เซิร์ฟเวอร์)

เฟรมที่คุณได้รับอยู่ในรูปแบบต่อไปนี้:

  • หนึ่งไบต์ซึ่งมีประเภทของข้อมูล
  • หนึ่งไบต์ซึ่งมีความยาว
  • สองหรือแปดไบต์เพิ่มเติมหากความยาวไม่พอดีกับไบต์ที่สอง
  • สี่ไบต์ซึ่งเป็นมาสก์ (= คีย์ถอดรหัส)
  • ข้อมูลจริง

ไบต์แรกมักไม่สำคัญหากคุณแค่ส่งข้อความคุณใช้เฉพาะประเภทข้อความเท่านั้น มันจะเป็น1000 0001(หรือ129) ในกรณีนั้น

ไบต์ที่สองและสองหรือแปดไบต์เพิ่มเติมจำเป็นต้องมีการแยกวิเคราะห์เนื่องจากคุณจำเป็นต้องทราบจำนวนไบต์ที่ใช้สำหรับความยาว (คุณต้องทราบว่าข้อมูลจริงเริ่มต้นที่ใด) ความยาวมักไม่จำเป็นเนื่องจากคุณมีข้อมูลอยู่แล้ว

บิตแรกของไบต์ที่สองเสมอ1ซึ่งหมายความว่าข้อมูลจะถูกปิดบัง (= เข้ารหัส) ข้อความจากไคลเอนต์ไปยังเซิร์ฟเวอร์จะถูกมาสก์เสมอ secondByte AND 0111 1111คุณจำเป็นต้องเอาบิตแรกด้วยการทำ มีสองกรณีที่ไบต์ผลลัพธ์ไม่ได้แสดงถึงความยาวเนื่องจากไม่พอดีกับไบต์ที่สอง:

  • ไบต์ที่สองของ0111 1110หรือ126หมายถึงสองไบต์ต่อไปนี้ใช้สำหรับความยาว
  • ไบต์ที่สองของ0111 1111หรือ127หมายถึงแปดไบต์ต่อไปนี้ใช้สำหรับความยาว

มาสก์ไบต์ทั้งสี่ใช้สำหรับถอดรหัสข้อมูลจริงที่ถูกส่ง อัลกอริทึมสำหรับการถอดรหัสมีดังนี้:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

ที่encodedByteเป็นไบต์เดิมในข้อมูลที่encodedByteIndexเป็นดัชนี (offset) ของการนับไบต์จากไบต์แรกของข้อมูลจริง0ซึ่งมีดัชนี masksคืออาร์เรย์ที่มีมาสก์สี่ไบต์

สิ่งนี้นำไปสู่ ​​pseudocode ต่อไปนี้สำหรับการถอดรหัส:

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

ทำไม1000 0001(129) สำหรับกรอบข้อความ? ข้อกำหนดระบุว่า: %x1 denotes a text frame. ดังนั้นควรเป็น0000 0001( 0x01) หรือ?
Dennis

3
@ เดนนิส: opcode ของเฟรมคือ0001ตามที่ระบุไว้ที่ส่วนหัวของส่วนนั้นของข้อกำหนด: "Opcode: 4 bits" ไบต์แรกประกอบด้วย FIN, RSV1-3 และ opcode FIN คือ1RSV1-3 เป็นทั้งสามตัว0และ opcode 0001จะรวมกับ1000 0001ไบต์แรก ดูอาร์ตเวิร์กในข้อกำหนดที่แสดงวิธีแยกไบต์ในส่วนต่างๆ
pimvdb

คุณมีสองสามบรรทัดที่อ่านว่า 'bytesFormatted [2] = (bytesRaw.length >> 56) และ 255' ใน Server-> Client model - คุณช่วยอธิบายให้ฉันหน่อยได้ไหม และดูเหมือนว่าจะเป็นตัวดำเนินการทางตรรกะสำหรับฉันดังนั้นฉันจึงไม่สามารถคาดหวังได้ว่าการใส่ตัวเลขหลังจากนั้นจะทำทุกอย่างให้ฉันใน C # ในทำนองเดียวกันฉันไม่แน่ใจว่า ">>" ในมาร์กอัปของคุณควรจะระบุว่าอย่างไร - อย่างไรก็ตามมันจะโอนไปยัง C # ... ไม่ว่าฉันจะหมายถึงอะไร ... : P
DigitalJedi805

หากใครสามารถเคลียร์สิ่งนี้ให้ฉันได้จริงฉันยินดีที่จะโพสต์การใช้งาน C # ของฉัน
DigitalJedi805

1
@ นีเว็ก: สิ่งที่พวกเขาหมายถึงก็คือมาสก์ไบต์นั้นต้องไม่สามารถคาดเดาได้ หากค่าคงที่จะไม่มีจุดใดมากนัก โดยพื้นฐานแล้วเมื่อผู้ใช้ที่ประสงค์ร้ายมีข้อมูลจำนวนหนึ่งเขาไม่ควรจะถอดรหัสได้โดยไม่มีมาสก์ หากตำแหน่งมาสก์ไม่สามารถคาดเดาได้ก็เป็นเรื่องยากสำหรับเซิร์ฟเวอร์ของแท้ในการถอดรหัส :)
pimvdb

26

การใช้งาน Java (หากต้องการ)

การอ่าน: ไคลเอนต์ไปยังเซิร์ฟเวอร์

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

            }
        }

การเขียน: เซิร์ฟเวอร์ไปยังไคลเอนต์

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
ความยาวบัฟเฟอร์ที่เหมาะสมสำหรับการดำเนินการอ่านคืออะไร?
jackgerrits

น่าเสียดายที่มันไม่ทำงาน ฉันเพิ่งคัดลอกการออกอากาศเป็นโมฆะ (จากเซิร์ฟเวอร์ไปยังไคลเอนต์) ไปยังโปรแกรมของฉัน เชื่อมต่อซ็อกเก็ตสำเร็จข้อความส่งไปยังเบราว์เซอร์สำเร็จ แต่เบราว์เซอร์ไม่ได้รับอะไร
ชื่อเล่น

18

การใช้งาน 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
น่าจะเป็นที่น่าสังเกตว่า JavaScript 2^31 - 1ไม่จริงสนับสนุนขยับกับตัวเลขที่มีขนาดใหญ่กว่า
pimvdb

13

การใช้งาน C #

เบราว์เซอร์ -> เซิร์ฟเวอร์

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

เซิร์ฟเวอร์ -> เบราว์เซอร์

    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
ฟังก์ชันถอดรหัสจะส่งคืนข้อความเฉพาะของฉันด้วยภาคผนวกที่ไม่ได้กำหนดสำหรับฉันเสมอเช่นที่นี่test�c=ܝX[ซึ่ง "test" คือข้อความของฉัน ส่วนอื่นมาจากอะไร
Snickbrack

1
ขออภัยสำหรับการตอบกลับปลาย. ฉันสร้างแอปพลิเคชัน C # ขนาดเล็ก (คอนโซลและเว็บ) เพื่อทดลองใช้เว็บซ็อกเก็ต คุณสามารถดาวน์โหลดได้จากที่นี่เพื่อดูว่ามีการเข้ารหัสอย่างไร Link: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
นิจ

สิ่งนี้ล้มเหลวสำหรับฉันสำหรับข้อความขนาดใหญ่ ฉันแทนที่ความยาว> รหัส 65535 ด้วย: var l = Convert ToUInt64 (length); var b = BitConverter.GetBytes (l); อาร์เรย์ย้อนกลับ (b, 0, b.Length); ข CopyTo (เฟรม 2); ... ซึ่งดูเหมือนจะมีการแก้ไข
Sean

ทำได้ดีมาก มีเพียงสิ่งเดียวเท่านั้น: ใน DecodeMessage ฉันกำลังคำนวณความยาวอาร์เรย์ "ถอดรหัส" ตามข้อมูลความยาวของน้ำหนักบรรทุกที่รวมอยู่ในดาต้าเฟรมเนื่องจากความยาวอาร์เรย์ "ไบต์" อาจไม่แน่นอน ความยาวอาร์เรย์ "ไบต์" ขึ้นอยู่กับวิธีอ่านสตรีม
user1011138

@ คุณฌอนช่วยดูตัวอย่างการแก้ไขปัญหาข้อความขนาดใหญ่ได้ไหม ฉันไม่สามารถเปลี่ยนรหัสนั้นเป็นตัวอย่างของคุณได้
Ali Yousefi

6

คำตอบของ pimvdb ดำเนินการใน 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

ตัวอย่างการใช้งาน:

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

ฉันพยายามใช้รหัสของคุณในสคริปต์ของฉัน แต่ไม่ประสบความสำเร็จ คุณอาจจะช่วยได้ไหม? stackoverflow.com/questions/43748377/...
จามรี

5

นอกเหนือจากฟังก์ชันการเข้ารหัสเฟรม PHP แล้วยังมีฟังก์ชันถอดรหัสดังนี้

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

ฉันได้ดำเนินการนี้และยังมีฟังก์ชั่นอื่น ๆ ในที่ง่ายต่อการใช้งานระดับ WebSocket PHP ที่นี่


4

การติดตั้ง 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

ขอบคุณสำหรับคำตอบฉันต้องการเพิ่มลงในเวอร์ชัน Python ของ hfern (ด้านบน) เพื่อรวมฟังก์ชันการส่งหากมีผู้ใดสนใจ

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) 

การใช้งานสำหรับการอ่าน:

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

การใช้งานสำหรับการเขียน:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")

2

การนำไปใช้งานใน Go

เข้ารหัสส่วน (เซิร์ฟเวอร์ -> เบราว์เซอร์)

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
}

ส่วนถอดรหัส (เบราว์เซอร์ -> เซิร์ฟเวอร์)

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 ฟังก์ชันถอดรหัสจะถือว่าเฟรมถูกส่งเป็นแผนที่{:data byte-array-buffer :size int-size-of-buffer}เนื่องจากขนาดจริงอาจมีขนาดไม่เท่ากันกับไบต์อาร์เรย์ทั้งนี้ขึ้นอยู่กับขนาดชิ้นส่วนของอินพุตสตรีมของคุณ

รหัสโพสต์ที่นี่: 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

C ++ การดำเนินงาน (ไม่ได้โดยฉัน) ที่นี่ ทราบว่าเมื่อไบต์ของคุณมากกว่า 65535 คุณจะต้องเปลี่ยนมีมูลค่าตราบใดที่แสดงให้เห็นที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.