one GET /realtime HTTP/1.1\r\n
two Host: 127.0.0.1:9989\r\n
three Connection: Upgrade\r\n
four Pragma: no-cache\r\n
five Cache-Control: no-cache\r\n
six User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n
seven Upgrade: websocket\r\n
eight Origin: //xyz.com \r\n
nine Sec-WebSocket-Version: 13\r\n
ten Accept-Encoding: gzip, deflate, br\r\n
eleven Accept-Language: zh-CN,zh; q=0.9,en; q=0.8\r\n
twelve Sec-WebSocket-Key: IqcAWodjyPDJuhGgZwkpKg==\r\n
thirteen Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
fourteen \r\n
one HTTP/1.1 101 Switching Protocols\r\n
two Upgrade: websocket\r\n
three Connection: Upgrade\r\n
four Sec-WebSocket-Accept: 5wC5L6joP6tl31zpj9OlCNv9Jy4=\r\n
five \r\n
one mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ; //This is the fixed string used in the algorithm
two accept = base64( sha1( Sec-WebSocket-Key + mask ) );
1 namespace uWS {
2
3 struct WebSocketHandshake {
4 template < int N, typename T>
5 struct static_for {
6 void operator () ( uint32_t *a, uint32_t *b) {
7 static_for<N - one , T>()(a, b);
8 T:: template f<N - one >(a, b);
9 }
ten };
eleven
twelve template < typename T>
thirteen struct static_for <0, T> {
fourteen void operator () ( uint32_t *a, uint32_t *hash) {}
fifteen };
sixteen
seventeen template < int state>
eighteen struct Sha1Loop {
nineteen static inline uint32_t rol ( uint32_t value, size_t bits) { return (value << bits) | (value >> ( thirty-two - bits));}
twenty static inline uint32_t blk ( uint32_t b[ sixteen ], size_t i) {
twenty-one return rol(b[(i + thirteen ) & fifteen ] ^ b[(i + eight ) & fifteen ] ^ b[(i + two ) & fifteen ] ^ b[i], one );
twenty-two }
twenty-three
twenty-four template < int i>
twenty-five static inline void f ( uint32_t *a, uint32_t *b) {
twenty-six switch (state) {
twenty-seven case one :
twenty-eight a[i % five ] += ((a[( three + i) % five ] & (a[( two + i) % five ] ^ a[( one + i) % five ])) ^ a[( one + i) % five ]) + b[i] + 0x5a827999 + rol(a[( four + i) % five ], five );
twenty-nine a[( three + i) % five ] = rol(a[( three + i) % five ], thirty );
thirty break ;
thirty-one case two :
thirty-two b[i] = blk(b, i);
thirty-three a[( one + i) % five ] += ((a[( four + i) % five ] & (a[( three + i) % five ] ^ a[( two + i) % five ])) ^ a[( two + i) % five ]) + b[i] + 0x5a827999 + rol(a[( five + i) % five ], five );
thirty-four a[( four + i) % five ] = rol(a[( four + i) % five ], thirty );
thirty-five break ;
thirty-six case three :
thirty-seven b[(i + four ) % sixteen ] = blk(b, (i + four ) % sixteen );
thirty-eight a[i % five ] += (a[( three + i) % five ] ^ a[( two + i) % five ] ^ a[( one + i) % five ]) + b[(i + four ) % sixteen ] + 0x6ed9eba1 + rol(a[( four + i) % five ], five );
thirty-nine a[( three + i) % five ] = rol(a[( three + i) % five ], thirty );
forty break ;
forty-one case four :
forty-two b[(i + eight ) % sixteen ] = blk(b, (i + eight ) % sixteen );
forty-three a[i % five ] += (((a[( three + i) % five ] | a[( two + i) % five ]) & a[( one + i) % five ]) | (a[( three + i) % five ] & a[( two + i) % five ])) + b[(i + eight ) % sixteen ] + 0x8f1bbcdc + rol(a[( four + i) % five ], five );
forty-four a[( three + i) % five ] = rol(a[( three + i) % five ], thirty );
forty-five break ;
forty-six case five :
forty-seven b[(i + twelve ) % sixteen ] = blk(b, (i + twelve ) % sixteen );
forty-eight a[i % five ] += (a[( three + i) % five ] ^ a[( two + i) % five ] ^ a[( one + i) % five ]) + b[(i + twelve ) % sixteen ] + 0xca62c1d6 + rol(a[( four + i) % five ], five );
forty-nine a[( three + i) % five ] = rol(a[( three + i) % five ], thirty );
fifty break ;
fifty-one case six :
fifty-two b[i] += a[ four - i];
fifty-three }
fifty-four }
fifty-five };
fifty-six
fifty-seven /**
fifty-eight *Implementation of sha1 function
fifty-nine */
sixty static inline void sha1 ( uint32_t hash[ five ], uint32_t b[ sixteen ]) {
sixty-one uint32_t a[ five ] = {hash[ four ], hash[ three ], hash[ two ], hash[ one ], hash[ zero ]};
sixty-two static_for< sixteen , Sha1Loop< one >>()(a, b);
sixty-three static_for< four , Sha1Loop< two >>()(a, b);
sixty-four static_for< twenty , Sha1Loop< three >>()(a, b);
sixty-five static_for< twenty , Sha1Loop< four >>()(a, b);
sixty-six static_for< twenty , Sha1Loop< five >>()(a, b);
sixty-seven static_for< five , Sha1Loop< six >>()(a, hash);
sixty-eight }
sixty-nine
seventy /**
seventy-one *Base64 encoding function
seventy-two */
seventy-three static inline void base64 ( unsigned char *src, char *dst) {
seventy-four const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
seventy-five for ( int i = zero ; i < eighteen ; i += three ) {
seventy-six *dst++ = b64[(src[i] >> two ) & sixty-three ];
seventy-seven *dst++ = b64[((src[i] & three ) << four ) | ((src[i + one ] & two hundred and forty ) >> four )];
seventy-eight *dst++ = b64[((src[i + one ] & fifteen ) << two ) | ((src[i + two ] & one hundred and ninety-two ) >> six )];
seventy-nine *dst++ = b64[src[i + two ] & sixty-three ];
eighty }
eighty-one *dst++ = b64[(src[ eighteen ] >> two ) & sixty-three ];
eighty-two *dst++ = b64[((src[ eighteen ] & three ) << four ) | ((src[ nineteen ] & two hundred and forty ) >> four )];
eighty-three *dst++ = b64[((src[ nineteen ] & fifteen ) << two )];
eighty-four *dst++ = '=' ;
eighty-five }
eighty-six
eighty-seven public :
eighty-eight /**
eighty-nine *Generate Sec WebSocket Accept algorithm
ninety *@ param input Sec-WebSocket Key value transmitted from the opposite end
ninety-one *@ param output stores the generated Sec WebSocket Accept value
ninety-two */
ninety-three static inline void generate ( const char input[ twenty-four ], char output[ twenty-eight ]) {
ninety-four uint32_t b_output[ five ] = {
ninety-five 0x67452301 , 0xefcdab89 , 0x98badcfe , 0x10325476 , 0xc3d2e1f0
ninety-six };
ninety-seven uint32_t b_input[ sixteen ] = {
ninety-eight zero , zero , zero , zero , zero , zero , 0x32353845 , 0x41464135 , 0x2d453931 , 0x342d3437 , 0x44412d39 ,
ninety-nine 0x3543412d , 0x43354142 , 0x30444338 , 0x35423131 , 0x80000000
one hundred };
one hundred and one
one hundred and two for ( int i = zero ; i < six ; i++) {
one hundred and three b_input[i] = (input[ four * i + three ] & 0xff ) | (input[ four * i + two ] & 0xff ) << eight | (input[ four * i + one ] & 0xff ) << sixteen | (input[ four * i + zero ] & 0xff ) << twenty-four ;
one hundred and four }
one hundred and five sha1(b_output, b_input);
one hundred and six uint32_t last_b[ sixteen ] = { zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , zero , four hundred and eighty };
one hundred and seven sha1(b_output, last_b);
one hundred and eight for ( int i = zero ; i < five ; i++) {
one hundred and nine uint32_t tmp = b_output[i];
one hundred and ten char *bytes = ( char *) &b_output[i];
one hundred and eleven bytes[ three ] = tmp & 0xff ;
one hundred and twelve bytes[ two ] = (tmp >> eight ) & 0xff ;
one hundred and thirteen bytes[ one ] = (tmp >> sixteen ) & 0xff ;
one hundred and fourteen bytes[ zero ] = (tmp >> twenty-four ) & 0xff ;
one hundred and fifteen }
one hundred and sixteen base64(( unsigned char *) b_output, output);
one hundred and seventeen }
one hundred and eighteen };
one // 4 bits
two enum OpCode
three {
four //It means that there will be new frames in the future
five CONTINUATION_FRAME = 0x0 ,
six //The package is a frame of text type
seven TEXT_FRAME = 0x1 ,
eight //The package is a frame of binary type
nine BINARY_FRAME = 0x2 ,
ten //Reserved value
eleven RESERVED1 = 0x3 ,
twelve RESERVED2 = 0x4 ,
thirteen RESERVED3 = 0x5 ,
fourteen RESERVED4 = 0x6 ,
fifteen RESERVED5 = 0x7 ,
sixteen //It is recommended to close the frame at the opposite end
seventeen CLOSE = 0x8 ,
eighteen //Ping Frame in Heartbeat Package
nineteen PING = 0x9 ,
twenty //Pong Frame in heartbeat package
twenty-one PONG = 0xA ,
twenty-two //Reserved value
twenty-three RESERVED6 = 0xB ,
twenty-four RESERVED7 = 0xC ,
twenty-five RESERVED8 = 0xD ,
twenty-six RESERVED9 = 0xE ,
twenty-seven RESERVED10 = 0xF
twenty-eight };
To sum up, Payload length=0~125, Extended Payload Length Does not exist, 0 byte; Payload length=126, Extended Payload Length takes 2 bytes; When Payload length=127, Extended Payload Length takes 8 bytes.
In addition, it should be noted that when Payload length=125 or 126, the next two bytes or eight bytes of the actual packet length are stored, and their values must be converted to the network byte order (Big Endian).
Some materials on the Internet say that, The frame information (packet information) sent by the client (the party that initiatively initiates the handshake request) to the server (the other party that passively accepts the handshake). The mask flag must be 1 The mask flag in the frame information sent by the server to the client is 0. Therefore, there is a 4-byte masking key in the data frame sent from the client to the server, but there is no masking key information in the data frame sent from the server to the client.
I did not see such a mandatory provision in the RFC document of the Websocket protocol. In addition, after studying the implementation of some websocket libraries, I found that this conclusion is not necessarily true, and the data sent by the client may not have the mask flag set.
one Assumptions:
two Original octet-i: the i-th byte of the original data.
three Transformed octet-i: the i-th byte of the converted data.
four j: Is i mod four Results.
five masking- key -Octet-j: mask key The jth byte.
one j = i MOD four
two transformed-octet-i = original-octet-i XOR masking- key -octet-j
one /**
two *@ param src refers to the original data to be transferred before the function is called, and the content after the function is called is masked or unmasked
three *@ param maskingKey four bytes
four */
five void maskAndUnmaskData ( std :: string & src, const char * maskingKey)
six {
seven char j;
eight for ( size_t n = zero ; n < src.length(); ++n)
nine {
ten j = n % four ;
eleven src[n] = src[n] ^ maskingKey[j];
twelve }
thirteen }
one Section one Bytes zero Bit=>FIN
two Section one Bytes one ~ three Bit=>RSV1+RSV2+RSV3
three Section one Bytes four ~ seven Bit=>opcode
four Section two Bytes zero Bit=>mask (equal to one )
five Section two Bytes one ~ seven Bit=>inclusion length
six Section three ~ six Bytes=>masking key
seven Section seven Bytes and later=>packet content
one Section one Bytes zero Bit=>FIN
two Section one Bytes one ~ three Bit=>RSV1+RSV2+RSV3
three Section one Bytes four ~ seven Bit=>opcode
four Section two Bytes zero Bit=>mask (equal to one )
five Section two Bytes one ~ seven Bit=>enable the extended packet header length flag, the value is one hundred and twenty-six
six Section three ~ four Bytes=>packet header length
seven Section five ~ eight Bytes=>masking key
eight Section nine Bytes and later=>packet content
one Section one Bytes zero Bit=>FIN
two Section one Bytes one ~ three Bit=>RSV1+RSV2+RSV3
three Section one Bytes four ~ seven Bit=>opcode
four Section two Bytes zero Bit=>mask (equal to one )
five Section two Bytes one ~ seven Bit=>enable the extended packet header length flag, the value is one hundred and twenty-seven
six Section three ~ ten Bytes=>packet header length
seven Section eleven ~ fourteen Bytes=>masking key
eight Section fifteen Bytes and later=>packet content
one //After a certain unpacking, the package payloadData is obtained, which is judged according to the FIN flag,
two //If FIN=true, a complete business packet has been received,
three //Call the processPackage() function to process the business data
four //Otherwise, it is temporarily stored in m_strParsedData
five //After processing a complete business package data each time, the data in the staging area m_strParsedData will be cleared
six if (FIN)
seven {
eight m_strParsedData. append (payloadData);
nine processPackage(m_strParsedData);
ten m_strParsedData.clear();
eleven }
twelve else
thirteen {
fourteen m_strParsedData. append (payloadData);
fifteen }
one GET /realtime HTTP/1.1\r\n
two Host: 127.0.0.1:9989\r\n
three Connection: Upgrade\r\n
four Pragma: no-cache\r\n
five Cache-Control: no-cache\r\n
six User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)\r\n
seven Upgrade: websocket\r\n
eight Origin: //xyz.com \r\n
nine Sec-WebSocket-Version: 13\r\n
ten Accept-Encoding: gzip, deflate, br\r\n
eleven Accept-Language: zh-CN,zh; q=0.9,en; q=0.8\r\n
twelve Sec-WebSocket-Key: IqcAWodjyPDJuhGgZwkpKg==\r\n
thirteen Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
fourteen \r\n
one HTTP/1.1 101 Switching Protocols\r\n
two Upgrade: websocket\r\n
three Connection: Upgrade\r\n
four Sec-WebSocket-Accept: 5wC5L6joP6tl31zpj9OlCNv9Jy4=\r\n
five Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover
six \r\n
one bool MyWebSocketSession::processPackage(const std::string& data )
two {
three std::string out ;
four //M_bClientCompressed determines whether compression is supported during handshake
five if (m_bClientCompressed)
six {
seven //Decompress
eight if (!ZlibUtil::inflate( data , out ))
nine {
ten LOGE( "uncompress failed, dataLength: %d" , data .length());
eleven return false ;
twelve }
thirteen
fourteen }
fifteen else
sixteen out = data ;
seventeen
eighteen //If decompression is not required, out=data; otherwise, out is the decompressed data
nineteen LOGI( "receid data: %s" , out .c_str());
twenty
twenty-one
twenty-two return Process( out );
twenty-three }
one size_t dataLength = data .length();
two std::string destbuf;
three if (m_bClientCompressed)
four {
five //On-demand compression
six if (!ZlibUtil::deflate( data , destbuf))
seven {
eight LOGE( "compress buf error, data: %s" , data .c_str());
nine return ;
ten }
eleven }
twelve else
thirteen destbuf = data ;
fourteen
fifteen LOGI( "destbuf.length(): %d" , destbuf.length());
Since the maximum number of public account articles is 5000 words, there are 12000 words in the original text of this article, which is omitted when the public account is issued. If you want to get a complete article, please reply to the keyword [websocket protocol analysis] on the public account background. To obtain the complete source code in the article, please reply to the keyword [websocket source code] in the background of the public account.