Linux下c語言實驗Websocket通訊 含客戶端和伺服器測試程式碼
Websocket是一種可雙向通訊的網路協議,其底層的資料收發是基於socket的,所以使用c語言來實現理論上是沒有問題的,主要難點在於協議中要求對個別資料進行加密處理,這些加密方法(庫)在java、c#等專門開發web的平臺中都是自帶的API(隨調隨到),而在用到c語言時則苦於去尋找這些加密方法的原始碼和庫,這使得用c來實現Websocket變得繁瑣而吐血!所以非要用c語言來實現Websocket的童鞋,要做好刀耕火種的準備。。。
前面已經翻閱過很多博文,不管是協議還是c的程式碼都可以找到很多,本文也是參考了這些前輩的資料而得,但苦在蒐羅到的程式碼都是片段或不夠工整的,所以本文重點在嘗試整合c實現Websocket的程式碼,以方便後來的小白快速上手使用(大牛隨便喵喵留下點建議就好)。協議解析部分比較粗糙,已經瞭解過的人可以不用看一、二章。
一、建立連線
一切的開始,先上一張網路資料抓包圖(這裡用的Wireshark軟體,還不知道抓包的童鞋可自行百度先玩玩)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
這是一張websocket通訊下,伺服器和客戶端互動時的資料抓包,圖中紅色、藍色分別是客戶端、伺服器發出的資料。
websocket實現資料通訊的步驟:
1.client向server傳送http請求,資料內容如同圖中第一大段紅色字串,其中攜帶了3個引數。
①要呼叫server的介面的路徑字串(不明白先不管)
②伺服器的IP和埠
③握手Key
大家可以把這當作一個模版放到程式碼裡。
- 1
- 2
- 3
- 4
- 5
- 6
2.server收到http請求後,檢查3個引數內容OK,然後返回攜帶引數的特定內容
④迴應的握手Key
⑤時間字串(格式: Date: 星期, 日 月 年 時:分:秒 時區)
同樣大家可以作為模版使用
- 1
- 2
- 3
- 4
- 5
- 6
3.client收到返回後,檢查返回狀態(主要看”101”)以及拿自己發出的握手Key (J2BJc+GQuSw34hi2TjyVpg==) 和收到server返回的Key (uGY1yScptmHNgZqrDnpq1Ws1xho=) 按照一套加密方式(這裡用的sha1雜湊加密,只一次握手)進行匹配OK,之後client和server就可以互發資料了
二、資料傳輸
通過上面抓包的截圖還看到,建立連線之後互發的資料是比較奇怪的,很多字元並不是可見的ASCII碼,這是因為Websocket協議裡對資料的傳輸做了一些規定,簡單說就是有一定的打包格式,其權威的解釋請看這裡draft-ietf-hybi-thewebsocketprotocol-13 - The WebSocket Protocol,不權威的解釋請繼續往下看
把上面的抓包資料按Hex16顯示(這裡只看後面互發資料的內容)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
額~還是先放一張官方的圖。。。
翻譯一下
簡單概括一下,每包資料組成按順序歸類為:
資料頭(1位元組) + 是否掩碼標誌(1個二進位制位) + 資料長度(半位元組~8位元組) + 掩碼(4位元組或沒有) + 資料內容
如果覺得亂,看不明白,那這裡用對應的顏色標註再解釋
可以看到0x81是資料頭:最高位FIN必須置1即0x80,最低位用0x1代表該包資料型別為文字類,所以得到資料頭0x81。
先看server打包的資料的第二位0x07:最高位表示是否使用掩碼,這裡為0表示不用;後7位表示資料長度,這裡為0x07 = 7,也就是“48 65 6c 6c 6f 20 21”這7個數據的長度咯~
再看client打包的資料的第二位0x87:最高位這裡為0x80表示使用掩碼;後7位這裡為0x07 = 7,所以0x87後面緊跟著的是4位元組是掩碼“c3 6c 78 21”,然後才是7個數據“8b 09 14 4d ac 4c 59”。
以上博主測試的只是小資料包,還不能完全反映圖示的資訊,這裡記錄資料長度的方式有三種:
第一種: 0<資料長度<126(0x7E),使用半個位元組來記錄資料長度,也就是上面例子
- 1
- 2
第二種: 126=<資料長度<65536(0x10000),資料包的第二位強制為 0x7E(再並上掩碼位),然後緊跟的2個位元組表示資料長度,再接著才是掩碼和資料,例如
- 1
第三種: 65536=<資料長度<=0xFFFFFFFFFFFFFFFF(誰tm一包資料上G,8個位元組記錄長度),資料包的第二位強制為 0x7F(再並上掩碼位),然後緊跟的8個位元組表示資料長度,再接著才是掩碼和資料。
==注意== 協議嚴格規定資料量不足的必須使用少位元組的記錄方式,也就是你不能裝逼給個 7f 然後跟 00 00 00 00 00 00 00 01。
說完資料長度,再來解析下掩碼,這裡掩碼是固定4個位元組長度的,內容可以用隨機數生成。
這4個掩碼會依次和要傳送的資料進行異或處理,最後得到資料區裡的資料,例如
- 1
掩碼為”c3 6c 78 21”,資料區”8b 09 14 4d ac 4c 59”,實際資料是”Helo !”即”48 65 6C 6C 6F 20 21”,打包過程為:
0x8b = 0xc3 X0r 0x48
0x09 = 0x6c X0r 0x65
0x14 = 0x78 X0r 0x6c
0x4d = 0x21 X0r 0x6c
0xac = 0xc3 X0r 0x6f // 這裡迴圈使用4個掩碼
0x4c = 0x6c X0r 0x20
0x59 = 0x78 X0r 0x21
那麼解碼呢?異或的解碼任然是異或!
附帶一提,掩碼處理後的資料會出現大量的非可見ASCII字元,甚至資料0x00,不要隨便使用0x00來判斷資料結束。
==注意== 規定client發資料給server時必須使用掩碼處理,而server下發資料給client時一般不進行掩碼處理
三、程式碼實驗
websocket_common.h