天天看點

Websocket協定

1Websocket簡介

1WebSocket protocol 是HTML5一種新的協定。它實作了浏覽器與伺服器全雙工通信(full-duplex)。

HTML5定義了WebSocket協定,能更好的節省伺服器資源和帶寬并達到實時通訊

2Websocket原理

1他是基于TCP SOCKET上添加了一些上層協定。

2很多網站為了實作即時通信, 所用的技術都是輪詢(polling), 輪詢是在特定的時間間隔(比如1秒),

由浏覽器對伺服器發出HTTP request,然後由伺服器傳回最新資料給用戶端浏覽器,這種傳統的HTTP

request的模式帶來明顯的缺點, 浏覽器要不斷向伺服器發請求, 而HTTP request的header(頭資訊)非常長,

包含我們要用的資料卻很小, 這樣會占用很多寬帶,

3Websocket API,浏覽器和伺服器隻需要做一個握手的動作,然後浏覽器和伺服器之間就形成了一條

快速通道, 兩者之間就直接可以資料互相傳送。這樣互相溝通的header還是很小的,大概是2Bytes

3長連接配接和短連接配接

長連接配接: 初始化的時候建立連接配接,一直不關閉

1伺服器與用戶端能以最快的速度發送資料

2伺服器能主動的向用戶端發送資料

3長連接配接長期占用網絡資源

4長連接配接的伺服器不友善直接重新開機

短連接配接: 發起連接配接-->發送資料-->擷取資料-->關閉

1不會長期占用資源

2友善伺服器直接重新開機

3每次請求與擷取資料都要重建立立連接配接

4伺服器不能像用戶端主動發送資料

比如說正常通路百度,一旦連上百度,他就會get網頁資料,然後就斷開連接配接了

4websocket 握手連接配接

1 用戶端向伺服器發送請求websocket的http封包

然後伺服器來驗證這個http封包是不是符合websocket的協定

2如果符合,伺服器傳回握手封包,給用戶端連接配接成功

5使用js寫一個用戶端

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<code>&lt;!DOCTYPE html&gt;</code>

<code>&lt;html&gt;</code>

<code>&lt;head&gt;</code>

<code>    </code><code>&lt;title&gt;websocket test&lt;/title&gt;</code>

<code>&lt;/head&gt;</code>

<code>&lt;body&gt;</code>

<code>    </code><code>&lt;script&gt;</code>

<code>        </code><code>var</code> <code>ws = </code><code>new</code> <code>WebSocket(</code><code>'ws://127.0.0.1:8002/ws'</code><code>);  </code>

<code>        </code><code>//socket打開了</code>

<code>        </code><code>ws.onopen = </code><code>function</code><code>(){</code>

<code>        </code><code>alert(</code><code>"open"</code><code>);</code>

<code>        </code><code>ws.send(</code><code>'WebSocket'</code><code>);</code>

<code>        </code><code>};</code>

<code>        </code> 

<code>        </code><code>//websocket有資料過來了</code>

<code>        </code><code>ws.onmessage = </code><code>function</code><code>(ev){</code>

<code>        </code><code>alert(ev.data);</code>

<code>        </code><code>//socket關閉了</code>

<code>        </code><code>ws.onclose = </code><code>function</code><code>(ev){</code>

<code>        </code><code>alert(</code><code>"close"</code><code>);</code>

<code>        </code><code>//websocket有錯誤發生</code>

<code>        </code><code>ws.onerror = </code><code>function</code><code>(ev){</code>

<code>        </code><code>alert(</code><code>"error"</code><code>);</code>

<code>    </code><code>&lt;/script&gt;</code>

<code>&lt;/body&gt;</code>

<code>&lt;/html&gt;</code>

伺服器接到http封包

現在伺服器要做的就是驗證這個封包了,看他是否符合websocket規範

首先就是Upgrade:websocket  如果不是就要直接關掉這個請求

其中很重要的一點就是:Sec-WebSocket-key

這個key是用戶端随機生成的。

6解析http封包

使用c語言的庫,http_parser  這個庫 百度一下就可下載下傳到

我們最終要一部分就是要把Sec-WebSocket-key這個值取出來

我們用key+migic的方式,使用SHA-1加密,base-64加密編碼

migic是一個固定的值258EAFA5-E914-47DA-95CA-C5AB0DC85B11

然後生成封包發送給用戶端,這樣他們就建立了websocket的握手連接配接了.

初始化

<code>//第一步 借助http_parser,解析用戶端給我們發送來的http封包頭部</code>

<code>struct</code> <code>http_parser p;</code>

<code>//初始化  第二個參數是解析類型</code>

<code>//HTTP_REQUEST請求,HTTP_RESPONSE響應,HTTP_BOTH都解析</code>

<code>http_parser_init(&amp;p, HTTP_REQUEST);</code>

解析http的時候 解析到頭的時候 用這個結構體回調

http_parser_settings 這個結構體

<code>struct</code> <code>http_parser_settings {</code>

<code>  </code><code>http_cb      on_message_begin;  </code><code>//消息開始的時候</code>

<code>  </code><code>http_data_cb on_url;            </code><code>//解析到url的時候回調</code>

<code>  </code><code>http_data_cb on_status;</code>

<code>  </code><code>http_data_cb on_header_field;     </code><code>// key</code>

<code>  </code><code>http_data_cb on_header_value;     </code><code>//  值</code>

<code>  </code><code>http_cb      on_headers_complete;</code>

<code>  </code><code>http_data_cb on_body;</code>

<code>  </code><code>http_cb      on_message_complete;</code>

<code>  </code><code>/* When on_chunk_header is called, the current chunk length is stored</code>

<code>   </code><code>* in parser-&gt;content_length.</code>

<code>   </code><code>*/</code>

<code>  </code><code>http_cb      on_chunk_header;</code>

<code>  </code><code>http_cb      on_chunk_complete;</code>

<code>};</code>

要用到這個回調 on_header_field 就是解析key的時候

<code>static</code> <code>int</code> <code>on_header_field(http_parser* p, </code><code>const</code> <code>char</code><code>* at, </code><code>size_t</code> <code>length);</code>

<code>static</code> <code>int</code> <code>on_header_value(http_parser* p, </code><code>const</code> <code>char</code><code>* at, </code><code>size_t</code> <code>length);</code>

<code>struct</code> <code>http_parser_settings s;</code>

<code>http_parser_settings_init(&amp;s); </code><code>//初始化要用</code>

<code>s.on_header_field = on_header_field;</code>

<code>s.on_header_value = on_header_value;</code>

開始解析http封包  http_str就是你的封包

<code>http_parser_execute(&amp;p, &amp;s, http_str, </code><code>strlen</code><code>(http_str));</code>

//解析head封包 key回調函數

<code>//http 解析回調函數 key</code>

<code>static</code> <code>int</code> <code>on_header_field(http_parser* p, </code><code>const</code> <code>char</code><code>* at, </code><code>size_t</code> <code>length)</code>

<code>{</code>

<code>    </code><code>strncpy</code><code>(key_buffer,at,length);</code>

<code>    </code><code>key_buffer[length] = 0;</code>

<code>    </code><code>printf</code><code>(</code><code>"%s:"</code><code>,key_buffer);</code>

<code>    </code><code>return</code> <code>0;</code>

<code>}</code>

<code>//http 解析回調函數 value</code>

<code>static</code> <code>int</code> <code>on_header_value(http_parser* p, </code><code>const</code> <code>char</code><code>* at, </code><code>size_t</code> <code>length)</code>

<code>    </code><code>char</code> <code>buffer[128];</code>

<code>    </code><code>strncpy</code><code>(buffer, at, length);</code>

<code>    </code><code>buffer[length] = 0;</code>

<code>    </code><code>printf</code><code>(</code><code>"%s\n"</code><code>,buffer);</code>

成功解析出來了  至于怎麼拿到想要的 就不用多說了吧

通過加密後然後回給用戶端封包

buffer 就是你通過 migic+key通過  sha1 +  base64加密獲得的

這兩個加密 網上搜尋一大堆 c語言寫的就行了

//會給用戶端的封包

char  accept_str[256];

char* wb_accept = "HTTP/1.1 101 Switching Protocols\r\n" \

"Upgrade:websocket\r\n" \

"Connection:Upgrade\r\n" \

"Sec-WebSocket-Accept:%s\r\n" \

"WebSocket-Location: ws://%s:%d\r\n" \

"WebSocket-Protocol:\r\n\r\n";

sprintf(accept_str, wb_accept, buffer,"127.0.0.1",8002);

printf(accept_str);

//發送給用戶端

send(socket, accept_str,strlen(accept_str),0);

7websocket接收資料,

隻需要在開始的時候握手,後面就不用去動了2

websocket接收資料的協定

1 固定位元組(1000 0001 或 1000 0010) 也就是一個位元組 十進制:129-130  十六進制:0x81-0x82

2包長度位元組,第一位是1,他是固定的 也就是

剩下7位得到一個整數(0,127)資料範圍(表示資料的長度);

也就是他125以内就用一個位元組表示長度

如果是126就是2個位元組表示長度

如果是127就是8個位元組表示長度

就是最後面 資料的長度  不包含前面這些的長度

3mask掩碼為包長之後的4個位元組,掩碼後面才是資料,

利用這個掩碼,我們可以将這個資料,将他還原回來,

也就是伺服器收到的資料實際上,還是要自己還原的.

4兄弟資料: 得到真實資料的方法:将兄弟資料的每一個位元組x,和掩碼的

的第i%4位元組做異或運算,其中i是x在兄弟資料中的索引。

8websocket發送資料

33

34

35

36

37

38

39

40

<code>static</code> <code>void</code> 

<code>on_ws_send_data(</code><code>struct</code> <code>session*s, unsigned </code><code>char</code><code>* pkg_data, </code><code>int</code> <code>pkg_len)</code>

<code>//printf("================send_ws_data============================\n");</code>

<code>//</code>

<code>static</code> <code>unsigned </code><code>char</code> <code>send_buffer[8096];</code>

<code>//固定的頭</code>

<code>send_buffer[0] = 0x81;</code>

<code>unsigned </code><code>int</code> <code>ws_send_len;</code>

<code>if</code> <code>(pkg_len &lt;= 125){</code>

<code>//最高位是0  不用管</code>

<code>send_buffer[1] = pkg_len;</code>

<code>ws_send_len = 2;</code>

<code>else</code> <code>if</code> <code>(pkg_len &lt;= 0xffff){ </code><code>//255</code>

<code>send_buffer[1] = 126;</code>

<code>send_buffer[2] = (pkg_len &amp; 0x000000ff);</code>

<code>send_buffer[3] = (pkg_len &amp; 0x0000ff00&gt;&gt;8);</code>

<code>ws_send_len = 4;</code>

<code>else</code><code>{</code>

<code>send_buffer[1] = 127;</code>

<code>send_buffer[3] = ((pkg_len &amp; 0x0000ff00) &gt;&gt; 8);</code>

<code>send_buffer[4] = ((pkg_len &amp; 0x00ff0000)&gt;&gt;16 );</code>

<code>send_buffer[5] = ((pkg_len &amp; 0xff000000) &gt;&gt; 24);</code>

<code>send_buffer[6] = 0;</code>

<code>send_buffer[7] = 0;</code>

<code>send_buffer[8] = 0;</code>

<code>send_buffer[9] = 0;</code>

<code>ws_send_len = 10;</code>

<code>//原始資料</code>

<code>memcpy</code><code>(send_buffer + ws_send_len,pkg_data,pkg_len);</code>

<code>ws_send_len += pkg_len;</code>

<code>send(s-&gt;c_sock, send_buffer, ws_send_len,0);</code>

<code>//printf("send_len%d\n", pkg_len);</code>

<code>//printf("send_data%s\n", pkg_data);</code>

<code>//printf("============================================\n");</code>

 本文轉自超級極客51CTO部落格,原文連結:http://blog.51cto.com/12158490/2059082,如需轉載請自行聯系原作者