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><!DOCTYPE html></code>
<code><html></code>
<code><head></code>
<code> </code><code><title>websocket test</title></code>
<code></head></code>
<code><body></code>
<code> </code><code><script></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></script></code>
<code></body></code>
<code></html></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(&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->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(&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(&p, &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 <= 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 <= 0xffff){ </code><code>//255</code>
<code>send_buffer[1] = 126;</code>
<code>send_buffer[2] = (pkg_len & 0x000000ff);</code>
<code>send_buffer[3] = (pkg_len & 0x0000ff00>>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 & 0x0000ff00) >> 8);</code>
<code>send_buffer[4] = ((pkg_len & 0x00ff0000)>>16 );</code>
<code>send_buffer[5] = ((pkg_len & 0xff000000) >> 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->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,如需轉載請自行聯系原作者