(a) WebSocket profile
Short Connection : in the traditional HTTP protocol, the client and server side of the communication mode is a short connection, that is, the server side does not maintain a connection with the client, after the message is sent, will disconnect the connection, the next time the client communication, must be established and the server New connection, This is the short connection. In the case of a short link, the client must actively initiate the request, and the server always responds passively to the request to push back the data. This approach is used in game development, which is obviously not appropriate.
Long Connection : Then the opposite is the long connection. In the case of a long connection, the client and server side always maintain a valid connection, then the client does not need to actively send messages, and the server side can also actively push the message to the client. Very similar to the previous introduction of the socket to send and receive methods. So obviously long connections are needed for our game network development.
WebSocket: It is with this demand that the WebSocket agreement has been created. Note thatWebSocket is just a protocol , not a socket. WebSocket can establish a full-duplex communication connection on the client and server side. Its protocol is implemented in TCP-based manner.
(ii) websocket basic knowledge
1. Handshake
WebSocket is actually using TCP to establish a connection, then when the terminal to establish a connection, how to know whether it is the general TCP or WebSocket protocol mode? Here we need to shake hands, simply say, through the handshake mechanism, the terminal can determine what kind of connection is established, so as to decide whether to deal with the message in a websocket way or TCP.
If we are to implement the server side, in fact, when we receive the packet, is the general TCP socket, and there is no difference, how to deal with or how to deal with. But it's not the same for clients. Because most of the situation, the client is using the existing browser as the client code of the JS runtime environment (unless you even the client browser environment is implemented yourself). The existing browser must clearly know the protocol type, in order to properly establish a long connection, and processing the WebSocket package, and the use of the relevant JS code, so the handshake becomes important.
When implementing our own server, the purpose of building a handshake is to properly notify the client that the server can receive and allow a connection based on the WebSocket protocol
A handshake request is similar to one of the following, and different browsers may not be the same because different browsers follow the WebSocket protocol version may not be the same.
accept:text/html,application/xhtml+xml,application/xml;q=0.9,/; q=0.8
accept-language:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-encoding:gzip, deflate
Sec-websocket-version:13
origin:http://localhost:5754
Sec-websocket-extensions:permessage-deflate
sec-websocket-key:dc8b7irs1rsydvp2iedsuq==
Connection:keep-alive, Upgrade
Pragma:no-cache
Cache-control:no-cache
Upgrade:websocket
For the above content, we do not need to know too much, the key is "Sec-websocket-key" in the content. As we'll explain later, let's look at how the server responds to such handshakes. When the server decides to receive this websocket connection, the server must send back a valid HTTP response message to the client. This is important, because only the correct response is sent, the client browser can confirm that the WebSocket request is received, in order to properly establish the WebSocket connection (in fact, it is because the browser is not our own development, if you have that moment, Develop your entire browser and websocket environment, the handshake protocol How to decide is your own thing, otherwise you will follow the standard).
The correct server return response is as follows:
http/1.1 101 Switching protocols
Upgrade:websocket
Connection:upgrade
sec-websocket-accept:s3pplmbitxaq9kygzzhzrbk+xoo=
Again, you don't have to focus too much on the specifics, just copy the previous three lines. We only need to focus on two places, one is a newline , one is sec-websocket-accept
newline: In the above message, the first three lines must be followed by a newline character, followed by two newline characters after the last line
sec-websocket-accept*: This value is an encrypted string, and the client will validate the value to determine if the WebSocket connection was successfully established because the value is important to be correct. The value is computed by connecting the Sec-websocket-key with the GUID value "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" when the request is sent, The new string is then SHA1 encrypted, and the encrypted result is BASE64 encoded. (Note that the GUID value of the connection is the value of bold, fixed, I first read the document, I thought this value is just an example, and later found that it was a constant string)
Tips:
When dealing with the handshake protocol, in addition to the above two points need to be noted, there are character encoding format will also affect the success of establishing a connection. So it's best to use Environment.NewLine instead of "\ r \ n" with a newline character. In addition, the resulting response message string, preferably using ENCODING.UTF8 encoding, is otherwise easily due to a coding problem, which causes the client to be unrecognized, causing the connection to be established unsuccessfully.
Attach code that generates an encrypted key value and generates a response return message
PrivateStaticByte[]Packhandshakedata (String seckeyaccept) {var Responsebuilder =New StringBuilder (); Responsebuilder.append ("http/1.1 101 Switching Protocols" + Environment.NewLine); Responsebuilder.append ("Upgrade:websocket" + Environment.NewLine); Responsebuilder.append ("Connection:upgrade" + Environment.NewLine); Responsebuilder.append ("Sec-websocket-accept:" + seckeyaccept + Environment.NewLine + Environment.NewLine);Return Encoding.UTF8.GetBytes (Responsebuilder.tostring ());}PrivateStaticstring getseckeyaccetp (byte[] Handshakebytes, int byteslength) {string HandShakeText = Encoding.UTF8.GetString (handshakebytes, 0, byteslength); string key = string. Empty; var heads = Handshaketext.split (foreach (var head in heads) {if (head. Contains ( "Sec-websocket-key:")) {Key = head; Key = head. Replace ( "Sec-websocket-key:", "). Trim (); }}}sc. Send (Packhandshakedata (GETSECKEYACCETP (buffer, length));
2. Frame Data
Because it is based on the TCP socket implementation, WebSocket actual data transmission is also transmitted in a streaming manner. Like TCP, WebSocket has its own transmission frame format. In this format, WebSocket defines the purpose and meaning of bytes in the beginning of the message byte stream. Below we can see
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------- -----+| F| r| r| r| opcode| m| Payload Len | Extended Payload Length | | i| s| s| s| (4) | a| (7) | (16/64) | | n| v| v| v| | s| | (if payload len==126/127) | | |1|2|3| | k| | |+-+-+-+-+-------+- +-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len = = 127 | +-----------------+-------------------------------+| | Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data | +-----------------------------------------------+: Pa lass= "Hljs-header" >| Payload Data Continued ... | +---------------------------------------------------------------+
byte1
(1) Fin represents the end of the data, WebSocket will be the larger data into slices sent, the last piece of data fin is 1, representing the end of the data slice
(2) Rsv1-rsv3 is reserved for, generally 0
(3) The last 4bit represents the type of opcode,opcode used to indicate the data frame. WebSocket frames are divided into two types, data frames and control frames.
0x0 stands for continuous frames, because this frame data is one piece in the Shard data, not the full data
0x1 represents data as text content
0x2 a binary stream when representing data
0X3-0X7 reserved for future non-control frame use
0x8 represents the data when a close command is notified (the shutdown is explained below)
0X9 represents a ping frame (also explained in the following ping)
0xA stands for Pong frames
0XB-0XF reserved for future control frame use
Byte2
(1) Mask represents the data in the frame sent, whether through the mask processing, 1 is true,0 to false, generally in the client to the server side of the data, the value is 1, that is, after the mask processing, the server sent to the client without a mask. (Note that the so-called client, the server is relative, the end of the receiving WebSocket connection, that is, the above mentioned postback encryption processing at the end of the servers.) This also explains why we have to follow the WebSocket standard to shake hands, otherwise the client how to know that the data they send to be masked processing it)
(2) The back 7 bits represent the data length of the data frame or a length indication. I myself understand that it is a length of pre-judgment. When the data length does not exceed 125 bytes, the value is the actual data length, when the length is 126~65535, the value is fixed 126, more than 65535, the value is fixed to 127
byte3~byte4
When payload len = 126, the 16-bit true length of the frame data is saved
Byte3~byte10
When payload len = 127, the true length of the 64 bits of the frame data is saved
Note that if the length does not exceed 125, then the BYTE3~BYTE10 does not represent the length of the data, that is, not reserved for the length of the data, but for the subsequent use of the frame header information, the subsequent frame header byte information shifted left
Byte11~byte14
These 4 bytes represent the mask value, specified by the client, each package is different, only through the decoding of the masked value to obtain the correct data
From this you can see that the WebSocket message packet, the server side needs at least 2 bytes, the client at least 6 bytes
The subsequent bytes are the actual bytes of data sent, and here is the sample code for data frame parsing
boolClose = (buffer[0] &0x08) = =0x08Temporarily does not handle, the server side temporarily only receives the ping, does not make the server-side active ping considerationBOOL Ping = (buffer[0] &0x09) = =0x09BOOL Pong = (buffer[0] &0x0A) = =0x0A;BOOL fin = (buffer[0] &0x80) = =0x801bit,1 represents the last frameBOOL Mask_flag = (buffer[1] &0x80) = =0x80Include Mask ...Enough read delimiterString data = null;try{int Payload_len = Buffer[1] &0x7F;Data lengthByte[] Masks =NewByte[4];Byte[] Payload_data;if (Payload_len = =126) {array.copy (buffer,4, masks,0,4); Payload_len = (UInt16) (buffer[2] <<8 | Buffer[3]); Payload_data =NewByte[payload_len]; Array.copy (Buffer,8, Payload_data,0, Payload_len); }Elseif (Payload_len = =127) {array.copy (buffer,Ten, masks,0,4);byte[] Uint64bytes =NewByte[8];for (int i =0; I <8; i++) {Uint64bytes[i] = buffer[9-i]; } UInt64Len = Bitconverter.touint64 (Uint64bytes,0); Payload_data =Newbyte[len]; for (UInt64 i = 0; i < len; i++) { Payload_data[i] = buffer[i + 14];}} else {array.copy (buffer, 2, Masks, 0, 4); Payload_data = new byte[payload_len]; Array.copy (Buffer, 6, Payload_data,for (var i = 0; i < Payload_len; i++) { Payload_data[i] = (byte) (Payload_data[i] ^ masks[i% 4]);}
3. Close the connection
There is a handshake, then of course it is closed, many tutorials on the web often only explain the establishment of the handshake, but to close the WebSocket connection to a word not mentioned. WebSocket Close, in the actual operation often encountered in three cases, one is the browser close, one is our JS code active shutdown, there is a browser refresh (yes, refresh, I did not pay attention to this problem at first). Either way, for WebSocket, it must send a closed control frame data to the peer. The opcode mentioned above must be 0x8.
After sending a closed control frame, the app should not continue sending data, and the peer must send a close frame response as soon as it receives a closed control frame. (the so-called as soon as possible, in fact, is controllable, not immediately, you can wait until your sending and receiving end, immediately send a close response). Sending the end of the closed frame will no longer process the received data.
The closed frame may contain data, and if it contains data, then the first two bytes must be a status code represented by an unsigned integer representing the cause of the shutdown
4.ping/pong
WebSocket is based on TCP, and it also improves some of the implementation characteristics of TCP. For example, WebSocket comes with a ping/pong, which enables it to maintain long-connection characteristics. When using TCP, we often want to achieve our own heartbeat, but WebSocket's ping/pong completely for us to achieve the heartbeat. However, ironically, although its websocket standard is clearly implemented Ping/pong but now the browser, or WebSocket library, and does not provide the API to send Ping/pong, that is, if you do not implement WebSocket's own protocol, This ping/pong is simply impossible to send.
But the current browser or JS library, although different for the Ping API, but they can receive ping processing, and postback pong data. So in my project, because we implement WebSocket's server-side protocol ourselves, we implement ping data and then process the pong data returned by the browser to detect the heartbeat.
Also, when one end receives multiple pings, it is not necessary to return each response, as long as the pong response of the most recent ping is returned
(iii) Misunderstanding of WebSocket understanding
1. Sub-package, sticky bag, bag, half bag
A lot of information on the internet said WebSocket will not stick bag, half bag. OK, this is correct, because the above will be the data frame when we have seen WebSocket will be large data, automatic shards sent. So WebSocket will automatically sub-packet send, because this sub-packet sent, websocket data will not overflow the receive buffer, so there will not be half a packet of the situation sent.
But on the sticky bag, and even the package, I saw a part of the data said no. Because WebSocket has frame header information, it does not stick to the package? This is not entirely correct, to know that the TCP message is also a header information, but the socket has been processed. And after my actual stress test on our Project Server, we found that WebSocket would stick and even pack. The difference is that the WebSocket data has Baotou information, but TCP does not (in actual development, we will add a Baotou to split the packet, WebSocket just for us to design a Baotou only), but the processing of this Baotou division, or we have to complete their own, WebSocket won't do it, if we don't handle it ourselves, I'm sorry, it's a wrapped bag.
Above is the websocket of some simple understanding of the experience and explanation, detailed content, we can go to the official website to download the standard documents to see, but must pay attention to the latest, I started the 06 version, the results how to find the control frame data code is not correct.
Personal understanding of the point of view, if there are errors, welcome to discuss
Game Network Programming (iii)--websocket Getting started and implementing your own WebSocket protocol