模拟一个 WebSocket 数据帧,使用 masked() 函数对数据进行掩码处理,然后将掩码后的数据传递给 processBuf() 函数,以验证它是否正确解析和解码数据。

//先看一个函数,看他们是怎么写入fin,mask,及payload有效数据的

//opcode表示一个数据是文本、二进制、close,ping,pong等
//payload表示数据内容
let encodeMessage = function(opcode, payload) {
    let buf;
    let b1 = 0x80 | opcode; // FIN 位(0x80) + 操作码(如 TEXT: 1 → 0x81)
    let b2 = 0;             // Mask 位为 0(服务器→客户端无需掩码)
    let length = payload.length; //数据的长度

    if (length < 126) { // 短消息:长度直接写入 b2
        buf = Buffer.alloc(payload.length + 2); // 头部 2 字节
        b2 |= length;                           // 设置长度字段, b2一直是0,所以用|=相当于=
        buf.writeUInt8(b1, 0);                  // 写入 FIN + 操作码
        buf.writeUInt8(b2, 1);                 // 写入 Mask 位 + 长度
        payload.copy(buf, 2);                   // 复制 Payload

    } else if (length < (1 << 16)) { // 中等消息:长度占 2 字节,(1<<16)就是65535
        buf = Buffer.alloc(payload.length + 4); // 头部 4 字节(2 + 2)
        b2 |= 126;                             // 长度标识符 126
        buf.writeUInt8(b1, 0); 					// 写入 FIN + 操作码
        buf.writeUInt8(b2, 1);					// 写入 Mask 位 + 长度
        buf.writeUInt16BE(length, 2);          // 写入 16 位大端长度,大端表示从后写入buffer,小端表示从前写入
        payload.copy(buf, 4);                  // 复制 Payload

    } else { // 长消息:长度占 8 字节
        buf = Buffer.alloc(payload.length + 10); // 头部 10 字节(2 + 8)
        b2 |= 127;                              // 长度标识符 127
        buf.writeUInt8(b1, 0);
        buf.writeUInt8(b2, 1);
        buf.writeUInt32BE(0, 2);                // 高 32 位(假设长度 ≤ 2^32-1)
        buf.writeUInt32BE(length, 6);           // 低 32 位,为什么是6?因为UInt32表示4个bit位+b1和b2两个Bit位,所以为6
        payload.copy(buf, 10);                  // 复制 Payload
    }

    return buf;
};

//理解了上面的创建FIN,MASK和数据的关系,我们就可以着手模拟创建一个 WebSocket 数据帧,包含掩码和掩码后的数据。
//模拟websocket数据帧的函数createWebsocketFrame()

//data表示数据内容
//maskBytes表示掩码,一般是固定的4个16进制,如:let maskBytes=[0x55, 0xAA, 0x33, 0xCC];里面内容可任意
function createWebSocketFrame(data,maskBytes)
{
	//将数据内容转化为buffer
    const payload=Buffer.from(data);
    //获取buffer的长度
    const payloadLength=payload.length;
    //获取Buffer内容的大小,第一个2表示头部的FIN和mask;
    //(payloadLength>125?(payloadLength <= 65535 ? 2 :8) : 0)判断内容是短消息,还是小于65535个字符还是大于65535
    //里面的4是表示直接添加到数据中的掩码,长度是4个
  
    let frameLength=2+(payloadLength>125?(payloadLength <= 65535 ? 2 :8) : 0)+4+payloadLength;
    //创建frameLength长度的缓冲区
    let buffer=Buffer.alloc(frameLength);
    //设置位置标识
    let offset=0;

    //设置FIN和opcode,这里FIN=1,opcode=1(文本帧),也就是0x80|1=0x81
    buffer.writeUInt8(0x81,offset);
    offset+=1;
    
    //设置MASK和payload length数据长度
    if(payloadLength<=125)//数据为短文本时
    {
        
        buffer.writeUInt8(0x80|payloadLength,offset);
        offset+=1;
    }else if(payloadLength<=(1<<16))//数据小于65535时
    {
        buffer.writeUInt8(0x80|126,offset);
        offset+=1;
        buffer.writeUInt16BE(payloadLength,offset);
        offset+=2;
    }else
    {
    	//数据大于65535时
        buffer.writeUInt8(0x80|127,offset);
        offset+=1;
        buffer.writeUInt32BE(0,offset);
        offset+=4;
        buffer.writeUInt32BE(payloadLength,offset);
        offset+=4;
    }

    //set直接从offset位置开始复制maskBytes到buffer中
    buffer.set(maskBytes,offset);
    offset+=4;

    //写入掩码后的数据,将数据进行掩码
    const maskedPayload=masked(maskBytes,payload);
    buffer.set(maskedPayload,offset);
    return buffer;
    
}

//接下来是对模拟出来的websocket数据帧进行解码
//解码processBuffer()函数

function processBuffer(buffer){
    if(buffer.length<2)
    {
        console.error('Buffer too short');
        return;
    }
    let idx=2;
    let b1=buffer.readUInt8(0);
    let fin=b1 & 0x80;
    let opcode=b1 & 0x0f;
    let b2=buffer.readUInt8(1);
    let mask=b2 & 0x80;
    let length=b2 & 0x7f; //表示有效数据长度

    //处理扩展长度
    if(length > 125)
    {
        //这里是(1<<16)小于65535的数据
        if(length ===126)
        {
            //fin标志1bit,mask标志1bit,数据长度在writeUInt16BE写入的大端序中,占2Bit,共4的长度
            if(buffer.length < 4)
            {
                console.error('Buffer too short for extended length (16-bit)');
                return;
            }
            length=buffer.readUInt16BE(2);
            idx+=2;
        }else if(length===127)
        {
            if(buffer.length<10)
            {
                console.error('Buffer too short for extended length (64-bit)');
                return;
            }
            //读取低32位长度(忽略高32位)
            length=buffer.readUInt32BE(6);
            idx+=8;
        }
    }


    //检查掩码标志
    if(mask)
    {
        if(buffer.length < idx + 4 + length)
        {
            console.error("Buffer too short for masked payload");
            return;
        }

        //提取掩码字节
        let maskBytes=buffer.subarray(idx,idx+4);
        idx+=4;

        //提取有效载荷并解掩码,就是提取有效的数据并进行解码
        let payload=buffer.subarray(idx,idx+length);
        payload=unmask(maskBytes,payload);
        
        //输出解码后的数据
        console.log('Decoded payload: ',payload.toString());
    }else
    {
        //如果没有掩码,直接提取有效载荷数据
        if(buffer.length<idx+length)
        {
            console.error('Buffer too short for payload');
            return;
        }

        let payload=buffer.subarray(idx,idx+length);
        
        console.log('解码后数据:',payload.toString());
    }

    //处理剩余数据(如果有的话)
    let remainingData=buffer.subarray(idx+length);
    if(remainingData.length>0)
    {
        console.log('剩余数据:',remainingData);
    }
}

//最后是掩码函数和解码函数,就不多做介绍,可以搜我的文章有掩码和解码的具体过程

//解码函数
let unmask=function(mask_bytes,buffer)
{
    let payload=Buffer.alloc(buffer.length);
    for(let i=0;i<buffer.length;i++)
    {
        payload[i]=mask_bytes[i%4]^buffer[i];
    }
    return payload;
}
//掩码函数
let maskBytes=[0x55, 0xAA, 0x33, 0xCC];
let masked=function(maskBytes,data)
{
    let d=Buffer.from(data);
    let dataload=Buffer.alloc(d.length);
    for(let i=0;i<d.length;i++)
    {
        dataload[i]=d[i] ^ maskBytes[i%4];
    }

    //const maskData=Buffer.from(d.map((b,i)=>b^maskBytes[i%4]));
    return dataload;

}

//综合测试

const data='hello world!';
//当然你要测试小65535的数据时你可以这样
//const data=Buffer.alloc(126)表示创建了小于65535个缓冲区
//const data=Buffer.alloc(70000)大于65535的数据
const wsFrame=createWebSocketFrame(data,maskBytes);

processBuffer(wsFrame);


masked() 函数:用于对数据进行掩码处理。

unmask() 函数:用于对掩码后的数据进行解掩码处理。

processBuffer() 函数:用于解析 WebSocket 数据帧并解掩码数据。

createWebSocketFrame() 函数:用于模拟创建一个 WebSocket 数据帧,包含掩码和掩码后的数据。

测试步骤:
使用 createWebSocketFrame() 函数创建一个 WebSocket 数据帧。

将生成的数据帧传递给 processBuffer() 函数。

processBuffer() 函数会解析数据帧并输出解码后的数据。

输出:
运行代码后,你应该会看到 Hello world! 被正确解码并输出到控制台。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值