How to Define an Instant Messaging Protocol Similar to websocket from scratch

  css, html, html5, javascript, node.js

clipboard.png

Shennan Avenue Town Building

It is not difficult to define a communication protocol of one’s own, the key lies in the usability, expandability and practicability of complex business scenarios of this protocol.

In instant messaging applications, both the client and the server can be regarded as a server.

Let’s review it togetherwebsocket

  • WebSocketIs a kind of in a singleTCPProtocol for full duplex communication over a connection. The WebSocket communication protocol was set as standard RFC 6455 by IETF in 2011 and supplemented by RFC7936. The WebSocket API has also been set as a standard by W3C.
  • WebSocketSo that the data exchange between the client and the server becomes simpler,Allows the server to actively push data to the clientInWebSocket APIIn, the browser and the server only need to complete a handshake, and the two can directly create a persistent connection and carry out two-way data transmission.

say somethingwsAdvantages of the protocol:

  • Speaking of advantages, the comparison reference here is HTTP protocol, in a nutshell: it supports two-way communication, which is more flexible, efficient and scalable.
  • It supports two-way communication and has stronger real-time performance.
  • Better binary support.
  • Less control overhead. After the connection is created, when ws client and server exchange data, the packet header controlled by the protocol is small. Without * including header, the packet header from the server to the client is only 2~10 bytes (depending on the packet length). If the client to the server, an additional mask of 4 bytes needs to be added. The HTTP protocol needs to carry a complete header for each communication.
  • Support extension.wsThe protocol defines extensions, and users can extend the protocol or implement customized sub-protocols. (For example, support custom compression algorithm, etc.)

Let’s take a look first.web socketThe implementation process of the protocol, and then use code abstraction, define their own instant messaging protocol:

  • Connection handshake process

    • There is a very common saying about WebSocket: Websocket reuses HTTP handshake channel, which specifically refers to:
    • The client negotiates the upgrade protocol with the WebSocket server through HTTP request. after the protocol upgrade is completed, the subsequent data exchange follows the WebSocket protocol
    • Client: Apply for Agreement Upgrade
    • First, the client initiates the protocol upgrade request. According to the WebSocket protocol specification, the request header must contain the following contents
    GET / HTTP/1.1
    Host: localhost:8080
    Origin: http://127.0.0.1:3000
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Version: 13
    Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw
  • Request header details

    • Request line: request method must be GET, HTTP version is at least 1.1
    • The request must contain Host
    • If the request comes from a browser client, it must contain Origin
    • The request must contain Connection and its value must contain the “Upgrade” token
    • The request must contain an Upgrade and its value must contain the “websocket” keyword
    • The request must contain Sec-Websocket-Version and its value must be 13
    • Requests must contain Sec-Websocket-Key to provide basic protection, such as unintentional connections
  • 1.2 Server: Response Protocol Upgrade
  • The response header returned by the server must contain the following

    • HTTP/1.1 101 Switching Protocols
    • Connection:Upgrade
    • Upgrade: websocket
    • Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
    • Response line:HTTP/1.1 101 Switching Protocols
    • Response must containUpgradeA with a value of"weboscket"
    • Response must containConnectionA with a value of"Upgrade"
    • The response must contain Sec-Websocket-Accept, calculated from Sec-Websocket-key in the request header.

Sec-WebSocket-Key/Accept calculation

  • The specification mentions:
  • The Sec-WebSocket-Key value is obtained by base64 encoding a randomly generated 16-byte random number.
  • Key can prevent the server from receiving illegal WebSocket connection, such as http request to connect to websocket, and the server can directly refuse at this time
  • Key can be used to initially ensure that the server knows ws protocol, but it cannot be excluded that some http servers only handle Sec-WebSocket-Key and do not implement ws protocol.
  • Key can avoid reverse proxy caching
  • When an ajax request is initiated in a browser, Sec-Websocket-Key and related header are prohibited, which can prevent clients from accidentally requesting protocol upgrades when sending ajax requests.
  • Finally, it should be emphasized that Sec-WebSocket-Key/Accept is not used to ensure the safety of data, because its calculation/conversion formulas are public and very simple, and its main function is to prevent some unexpected situations.

WebSocketThe minimum unit of communication is frames, and one or more frames form a complete message. During data exchange, the sender and receiver need to do the following:

  • Sending end: the message is cut into multiple frames and sent to the server
  • Receiving end: receiving message frames and reassembling the associated frames into complete messages

Detailed Explanation of Data Frame Format

clipboard.png

  • FIN: 1bit
  • 0 indicates that it is not the last fragment of the message
  • 1 indicates the last fragment of the message
  • RSV1, RSV2, RSV3: 1bit each, all 0 in general, related to Websocket expansion. if there is a non-zero value and no WebSocket expansion is adopted, the connection is wrong
Opcode: 占4bit

%x0: 表示本次数据传输采用了数据分片, 当前数据帧为其中一个数据分片
%x1: 表示这是一个文本帧
%x2: 表示这是一个二进制帧
%x3-7: 保留的操作代码, 用于后续定义的非控制帧
%x8: 表示连接断开
%x9: 表示这是一个心跳请求(ping)
%xA: 表示这是一个心跳响应(pong)
%xB-F: 保留的操作代码, 用于后续定义的非控制帧
  • Mask: 1bit

    • 0 means no mask XOR operation is performed on the data payload
    • 1 indicates mask XOR operation on data payload
  • Payload length: 7 or 7+16 or 7+64 bits

    • 0~125: the data length is equal to this value
    • 126: The next 2 bytes represent a 16-bit unsigned integer, and the value is the length of the data
    • 127: The next 8 bytes represent a 64-bit unsigned integer, and the value is the length of the data
  • Masking-key: 0 or 4bytes

    • 1: Masking-key with 4 bytes
    • 0: no Masking-key
    • Masking is not used to prevent data leakage, but to prevent proxy cache pollution attacks and other problems existing in earlier versions of the protocol.
  • Payload data: payload data
  • Data transfer

    • Each message of WebSocket may be divided into multiple data frames. When receiving a data frame, it will determine whether it is the last data frame according to the FIN value.
    • Examples of Data Frame Delivery:
    • FIN=0, Opcode=0x1: Send text type, the message has not been sent yet, and there are still subsequent frames.
    • FIN=0, Opcode=0x0: The message has not been sent completely, and there are still subsequent frames, following the previous one.
    • FIN=1, Opcode=0x0: The message is sent completely without subsequent frames, and the complete message is formed after the previous one.

Formally begin to define our own communication protocol:

  • Why should we customize TCP application layer transport protocol?
  • Aiming at specific user groups, the real encryption of communication information is realized, and more flexible communication is realized under complex scenes
  • Because in the process of TCP streaming, there may be subcontracting and packet sticking. In order to solve these problems, we need to customize the communication protocol for packet and unpacking.
  • What are subcontracting and sticking?
  • Subcontracting: refers to that the recipient has not received a complete package but only accepted part of it.
  • Sticky packet: refers to a packet of data sent by the sender when it is received by the receiver. Seen from the receiving buffer, the head of the next packet of data follows the tail of the previous packet of data.
  • PS: Because TCP is byte stream oriented and has no boundary concept, strictly speaking, there are no concepts of subcontracting and sticky packets. However, in order to better understand and describe the phenomenon, I will use these two nouns to explain the phenomenon. I think it’s enough for everyone to know this concept. It’s enough to solve the problem without deduction.
  • What are the causes of subcontracting and package sticking?
  • Reasons for subcontracting:
  • It may be caused by IP fragment transmission, it may also be a half packet caused by partial packet loss during transmission, or it may be that a packet may be divided into two transmissions. When fetching data, a portion is first fetched (which may also be related to the size of the buffer received), in short, a packet is divided into multiple receptions.
  • The reasons for the sticky package are as follows:
  • Due to the mechanism of TCP protocol itself (reliable connection-oriented protocol-three-way handshake mechanism), the client and the server maintain a connection (Channel), and data can continuously send multiple packets to the server under the condition that the connection is not broken. However, if the network packets sent are too small, he will enable Nagle algorithm (configurable whether to enable or not) to merge the smaller packets (based on which, TCP’s network delay is higher than UDP) and then send them (timeout or packet size is sufficient). Then, when the server receives the message (data stream), it cannot distinguish which data packets are sent separately by the client itself, thus generating sticky packets. After receiving the data, the server puts it into the buffer area. If the message is not taken out of the buffer area in time, the next time the data is taken out, multiple data packets may be taken out at one time, resulting in packet sticking.
  • What are packets and unpacking?
  • TCP/ IP network data is transmitted in the form of a stream. The data stream is composed of packets. How to determine whether a packet received by the recei ver is a complete packet requires the packet to be processed at the time of transmission. This is packet technology, which processes the packet into a packet header and a packet body.

Baotou is the beginning mark of the package, and the size of the whole package is the end mark of the package.

  • How to customize the agreement?
  • When sending, the data packet is composed of packet header+data: the packet header content is divided into packet type+packet length.
  • When receiving, we only need to ensure that the packet header of the data packet is read completely, and we can judge what type and length of data we will receive by the data length and packet type in the packet header of the received data packet. Th en we will receive the data circularly until the size of the received data is equal to the data length. At this time, we will finish receiving a complete data packet.

Write a common decrypted package in code:

{
header:{
cmdid:oxa212,
msgid:xxxxxx,
sessionid:xxxx
....
},
body:{
sessiontype:1,
datalength:100,
formid:xxx,
told:xxxx,
msgid:xxxxxxx,
content:'dear'
}
}
  • Today, in order to reduce the difficulty, it was not used.probFormat transmission.
  • Of course, there are heartbeat contracts and return packages, which are similar to the above, but the contents are inconsistent.

Only the client is written today.node.jsPart of the code, the server code, intends to use latergolangWriting.

  • As mentioned above,WebSocketThe minimum unit of communication is frames, and one or more frames form a complete message. During data exchange, the sender and receiver need to do the following:
  • Sending end: the message is cut into multiple frames and sent to the server
  • Receiving end: receiving message frames and reassembling the associated frames into complete messages
  • There are problems of package sticking and subcontracting, which can be easily understood as creatingbufferBuffer, cut out the binary data bit by bit, and then become specificjsObject.

The first step is to establish with the servertcpLink

const {Socket} = require('net') 
const tcp = new Socket()
tcp.setKeepAlive(true);
tcp.setNoDelay(true);
//保持底层tcp链接不断,长连接

The second step is to specify the corresponding domain name port number link


tcp.connect(80,142.122.0.0)

The third step is to send the heartbeat packet after the successful link is established, and the server will reply to heartbeat packet.

  • Each person’s customized heartbeat packet is different. For the specific format, please refer to the above and customize the content and detection time of heartbeat packet. How long can the heartbeat not be detected?

The fourth step is to receive the server data, unpack it, give different processing mechanisms according to different data types, and decide which to render on the page, which to put into the database, and make persistent storage, etc.

  • Write a little unpacking code here.
根据后端传送的数据类型 使用对应不同的解析
readUInt8 readUInt16LE readUInt32LE readIntLE等处理后得到myBuf 

const myBuf = buffer.slice(start);//从对应的指针开始的位置截取buffer
const header = myBuf.slice(headstart,headend)//截取对应的头部buffer
const body = JSON.parse(myBuf.slice(headend-headstart,bodylength).tostring()) 
//精确截取body的buffer,并且转化成js对象

How to unpack the bag and how long it is depends on how you define it. Please refer towebsocketDefinition format of:

bVbuevC

Node.jsThe currently supported character codes include:

  • Ascii-Only 7-bit ASCII data is supported. This encoding is very fast if the high order bits are removed.
  • Utf8-Multibyte Encoded Unicode Character. Many web pages and other document formats use UTF-8.
  • Utf16le-2 or 4 bytes, Unicode character encoded in small byte order. Support agent pairs (U+10000 to U+10FFFF).
  • Alias for ucs2-utf16le.
  • Base64-Base64 encoding.
  • Latin1-A way to encode Buffer into a byte-coded string.
  • Alias for binary-latin1.
  • Hex-Encodes each byte into two hexadecimal characters.

Group package

  • Create Buffer class

    • Buffer provides the following apis to create Buffer classes:
    • Buffer. alloc (size [,fill [,encoding]]): returns a buffer instance of the specified size. if fill is not set, the default fill is 0
    • Buffer.allocUnsafe(size): returns a buffer instance of the specified size, but it will not be initialized, so it may contain sensitive data
    • Buffer.allocUnsafeSlow(size)
    • Buffer.from(array): Returns a new buffer instance initialized by the value of array (the elements of the passed-in array can only be numbers, otherwise they will be automatically overwritten by 0)
    • From (ArrayBuffer [,byte offset [,length]]): returns a newly created Buffer that shares the same memory as the given arraybuffer.
    • From (Buffer): Copies the data of the incoming Buffer instance and returns a new buffer instance
    • Buffer.from(string[, encoding]): Returns a new buffer instance initialized by the value of string

The so-called group package, is the correspondingjsObject, into binary data, and then push to the server

  • Write a simple package here.
    const obj = {
        header:{
            datalength:123,
            sessiontype:1,
            cmdid:xxx,},
         body:{
            content:"hello",
            sessionid:xxx,
            fromid:xxx,
            toid:xxx
                }
            }
    将上面的js对象转化成buf后,推送给服务端 
    tcp.write(buf,cb)
    
    cb是一个异步回掉,当数据推送完后才会调用。

Common ones are given below.bufferOperationapi

  • Method reference manual
  • The following is a list of methods commonly used by the Node.js Buffer module (note that some methods were not available in earlier versions):
  • Serial Number Method & Description
  • 1 new Buffer(size)

    • Allocates a new buffer with a size unit of 8 bytes. Note that size must be less than kMaxLength, otherwise, an exception RangeError will be thrown. Abandoned: use Buffer.alloc () instead (or Buffer.allocUnsafe ()).
  • 2 new Buffer(buffer)

    • Copy data of parameter Buffer to buffer instance. Abandoned: use Buffer.from(buffer) instead.
  • 3 new Buffer(str[, encoding])

Assign a new buffer that contains the str string passed in. Encoding encoding method defaults to’ utf8′. Abandoned: use Buffer.from(string[, encoding]) instead.

  • 4 buf.length

    • Returns the bytes number of this buffer. Note that this is not necessarily the size of the contents in buffer. Length is the amount of memory allocat ed by the buffer object and will not change with the change of the buffer object content.
  • 5 buf.write(string[, offset[, length]][, encoding])

    • According to the offset of the parameter offset and the specified encoding encoding method, the parameter string data is written into buffer. The default value of offset offset is 0, and the encoding method is utf8 by default. Length is the bytes size of the string to be written. Returns a number type indicating how many 8-bit by te streams have been written. If buffer does not have enough space for the entire string, it will only write some strings. Length defaults to buffer.length-offset. This method does not appear to write some characters.
  • 6 buf.writeUIntLE(value, offset, byteLength[, noAssert])

Write value into buffer, which is determined by offset and byteLength, and supports up to 48-bit unsigned integers with small-end alignment, for example:

const buf = Buffer.allocUnsafe(6);

buf.writeUIntLE(0x1234567890ab, 0, 6);

// 输出: <Buffer ab 90 78 56 34 12>
console.log(buf);
noAssert 值为 true 时,不再验证 value 和 offset 的有效性。 默认是 false。
  • 7 buf.writeUIntBE(value, offset, byteLength[, noAssert])

    • Write value into buffer, which is determined by offset and byteLength, and supports up to 48-bit unsigned integers with large-end alignment. When noAssert value is true, the validity of value and offset will no longer be verified. The default is false.
const buf = Buffer.allocUnsafe(6);

buf.writeUIntBE(0x1234567890ab, 0, 6);

// 输出: <Buffer 12 34 56 78 90 ab>
console.log(buf);
  • 8 buf.writeIntLE(value, offset, byteLength[, noAssert])

    • Write value into buffer, which is determined by offset and byteLength, and supports up to 48 signed integers with small-end alignment. When noAssert value is true, the validity of value and offset will no longer be verified. The default is false.
  • 9 buf.writeIntBE(value, offset, byteLength[, noAssert])

    • Write value into buffer, which is determined by offset and byteLength, and supports up to 48 signed integers with large-end alignment. When noAssert value is true, the validity of value and offset will no longer be verified. The default is false.
  • 10 buf.readUIntLE(offset, byteLength[, noAssert])

    • Supports reading unsigned numbers below 48 bits with small-end alignment. When noAssert value is true, offset no longer verifies whether the buffer length is exceeded; the default is false.
  • 11 buf.readUIntBE(offset, byteLength[, noAssert])

    • Supports reading unsigned numbers below 48 bits with large-end alignment. When noAssert value is true, offset no longer verifies whether the buffer length is exceeded; the default is false.
  • 12 buf.readIntLE(offset, byteLength[, noAssert])

    • Supports reading signed numbers below 48 bits with small ends aligned. When noAssert value is true, offset no longer verifies whether the buffer length is exceeded; the default is false.
  • 13 buf.readIntBE(offset, byteLength[, noAssert])

    • Supports reading signed numbers below 48 bits with large-end alignment. When noAssert value is true, offset no longer verifies whether the buffer length is exceeded; the default is false.
  • 14 buf.toString([encoding[, start[, end]]])

    • Returns a decoded string based on the encoding parameter (default is’ utf8′). The value range will also be based on the passed parameters start (default is 0) and end (default is buffer.length).
  • 15 buf.toJSON()

    • Converts a Buffer instance to a JSON object.
  • 16 buf[index]

    • Gets or sets the specified byte. The return value represents one byte, so the legal range of the return value is hexadecimal 0x00 to 0xFF or decimal 0 to 255.
  • 17 buf.equals(otherBuffer)

    • Compares whether the two buffers are equal, if it is true, otherwise it is false.
  • 18 buf.compare(otherBuffer)

    • Compares two Buffer objects and returns a number that indicates buf is before, after, or the same as otherBuffer.
  • 19 buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])

    • Buffer copy, source and destination can be the same. TargetStart target start offset and sourceStart source start offset are both 0 by default. The sourcend so ur ceEnd position offset defaults to the source’s length buffer.length
  • 20 buf.slice([start[, end]])

    • Cut Buffer object, offset and trim index according to start (default is 0) and end (default is buffer.length). The negative index is calculated from the end of buffer.
  • 21 buf.readUInt8(offset[, noAssert])

    • Reads an unsigned 8-bit integer based on the specified offset. If the parameter noAssert is true, the offset offset parameter will not be validated. If so, offset may exceed the end of buffer. The default is false.
  • 22 buf.readUInt16LE(offset[, noAssert])

    • Reads an unsigned 16-bit integer using a special endian byte order format based on the specified offset. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 23 buf.readUInt16BE(offset[, noAssert])

    • According to the specified offset, an unsigned 16-bit integer is read using a special endian byte order format with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 24 buf.readUInt32LE(offset[, noAssert])

    • According to the specified offset, an unsigned 32-bit integer is read using the specified endian byte order format, with small ends aligned. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 25 buf.readUInt32BE(offset[, noAssert])

    • According to the specified offset, an unsigned 32-bit integer is read using the specified endian byte order format, with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 26 buf.readInt8(offset[, noAssert])

    • Reads a signed 8-bit integer based on the specified offset. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 27 buf.readInt16LE(offset[, noAssert])

    • According to the specified offset, a signed 16-bit integer is read using a special endian format, with small ends aligned. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 28 buf.readInt16BE(offset[, noAssert])

According to the specified offset, a signed 16-bit integer is read using a special endian format, with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.

  • 29 buf.readInt32LE(offset[, noAssert])

    • According to the specified offset, a signed 32-bit integer is read using the specified endian byte order format, with small ends aligned. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 30 buf.readInt32BE(offset[, noAssert])

    • According to the specified offset, a signed 32-bit integer is read using the specified endian byte order format, with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 31 buf.readFloatLE(offset[, noAssert])

    • According to the specified offset, a 32-bit double floating point number is read using the specified endian byte order format, with small-endian alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 32 buf.readFloatBE(offset[, noAssert])

    • According to the specified offset, a 32-bit double floating point number is read using the specified endian byte order format, with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 33 buf.readDoubleLE(offset[, noAssert])

    • According to the specified offset, a 64-bit double-precision number is read using the specified endian byte order format, with small-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 34 buf.readDoubleBE(offset[, noAssert])

    • According to the specified offset, a 64-bit double-precision number is read using the specified endian byte order format, with large-end alignment. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that offset may exceed the end of buffer. The default is false.
  • 35 buf.writeUInt8(value, offset[, noAssert])

    • Writes value to buffer based on the incoming offset. Note: value must be a legal unsigned 8-bit integer. If the parameter noAssert is true, the offset offset parameter will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Do not use this parameter unless you are very sure of it. The default is false.
  • 36 buf.writeUInt16LE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a legal unsigned 16-bit integer with small-e nd alignment. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 37 buf.writeUInt16BE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a legal unsigned 16-bit integer with large-e nd alignment. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 38 buf.writeUInt32LE(value, offset[, noAssert])

    • Writes value to buffer based on the incoming offset and the specified endian format (LITTLE-ENDIAN: nibble order) . Note: value must be a legal unsigned 32-bit integer with small-end alignment. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 39 buf.writeUInt32BE(value, offset[, noAssert])

    • Writes value to buffer based on the incoming offset and the specified endian format (Big-Endian: big byte order) . Note: value must be a valid signed 32-bit integer. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 40 buf.writeInt8(value, offset[, noAssert])
  • 41 buf.writeInt16LE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid signed 16-bit integer. If the param eter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 42 buf.writeInt16BE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid signed 16-bit integer. If the param eter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 43 buf.writeInt32LE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid signed 32-bit integer. If the param eter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 44 buf.writeInt32BE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid signed 32-bit integer. If the param eter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 45 buf.writeFloatLE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: When value is not a 32-bit floating point number type valu e, the result will be uncertain. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 46 buf.writeFloatBE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: When value is not a 32-bit floating point number type valu e, the result will be uncertain. If the parameter noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 47 buf.writeDoubleLE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid 64-bit double value. If the paramet er noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 48 buf.writeDoubleBE(value, offset[, noAssert])

    • Writes value to buffer based on the offset passed in and the specified endian format. Note: value must be a valid 64-bit double value. If the paramet er noAssert is true, the value and offset offset parameters will not be validated. This means that value may be too large, or offset may exceed the end of buffer, causing value to be discarded. Unless you are very sure about this parameter, try not to use it. The default is false.
  • 49 buf.fill(value, offset)

    • Fills this buffer with the specified value. If offset is not specified (default is 0) and end (default is buffer.length), the entire buffer will be filled.

The final conclusion:

  • Real-time communication, especially three-terminal encryption and message synchronization, is very complicated. I only wrote about 1/10
  • Grouping and unpacking should be carried out according to your specific business scenario and the company’s customized protocol. This is just a general description.
  • I will try my best to use the backstage code in the future.golangAndnode.jsWrite one copy each.
  • There are too many high concurrency scenarios, computer room deployment and overall architecture not written here, because once the concurrency comes up, there are too many things to be done at both the front and back ends.
  • Fortunately, the company was invited to Shenzhen Wanxiang City yesterday.BilibiliMr. Mao Jian, the architect of, gave us training. he made me have a deep understanding of the back end, especiallyIM's Overall Architecture and Optimization, after the summary, will also share with you
  • If there is anything wrong, please point it out, thank you.
  • The encryption and decryption process has not been written up, and this is also the core.