手寫WebSocket協(xié)議(C#)
零.前置知識
????- C#
????- 網(wǎng)絡(luò)編程
????- 異步編程
一.前置準(zhǔn)備
????- 一個(gè)實(shí)現(xiàn)好的Tcp類
????可以使用微軟提供的TcpListener,本文使用自己用socket封裝的LRYHTcpServer
????- 一個(gè)編輯器(VS,VSCode等都可以)
????- dotnet sdk
二.WebSocket協(xié)議

三.實(shí)現(xiàn)思路
????構(gòu)建響應(yīng)數(shù)據(jù):客戶端在將Http協(xié)議升級為WebSocket協(xié)議時(shí),會向WebSocket服務(wù)端發(fā)送一個(gè)請求報(bào)文,格式如下:
????服務(wù)端接收到該請求報(bào)文后,要向其發(fā)送一個(gè)響應(yīng)報(bào)文,同意客戶端升級為WebSocket協(xié)議,響應(yīng)報(bào)文格式如下:
????其中服務(wù)器響應(yīng)報(bào)文必須以/r/n為結(jié)尾,[accept]為確認(rèn)密鑰,其計(jì)算方式為:
????當(dāng)客戶端接收到服務(wù)端發(fā)送的響應(yīng)報(bào)文后,客戶端與服務(wù)端之間就會建立WebSocket連接.此時(shí)雙方通信是基于WebSocket協(xié)議,需要遵循WebSocket的標(biāo)準(zhǔn),即第二大點(diǎn)(WebSocket協(xié)議).
????現(xiàn)在解釋一下WebSocket報(bào)文中各個(gè)字段的含義(所涉及數(shù)字01均為二進(jìn)制):
????FIN:如果為0,則代表此數(shù)據(jù)包不是該數(shù)據(jù)的最后一個(gè)數(shù)據(jù)包;如果為1,則代表該數(shù)據(jù)包是該數(shù)據(jù)的最后一個(gè)數(shù)據(jù)包或唯一一個(gè)數(shù)據(jù)包.
????RSV:保留字段,用于擴(kuò)展(通常不用,不用時(shí)必須指定為000).
????opcode:數(shù)據(jù)類型,當(dāng)其為0001,則為Text;為0010,則為Binary;為1001,則為Ping;為1010,則為Pong;為1000,則為Close.
????MASK:如果為0,則無掩碼,masking-key位沒有數(shù)據(jù);如果為1,則有掩碼,即后面masking-key不為空.
????Payload Len:Payload Data的長度.當(dāng)其小于等于125時(shí),則Payload Len占7 bit;當(dāng)數(shù)據(jù)長度大于125,小于等于65535時(shí),其為126,Payload Len需要再往后讀16 bit(即一個(gè)ushort的長度);當(dāng)數(shù)據(jù)長度大于65535時(shí),氣為127,Payload Len需要再往后讀64 bit(即一個(gè)ulong的長度).
????masking-key:如果有掩碼,則會有4個(gè)字節(jié)作為masking-key;如果沒有掩碼,則此處無數(shù)據(jù)(即此地址上為Payload Data的數(shù)據(jù)).
????Payload Data:傳輸?shù)臄?shù)據(jù),在masking-key之后,如果沒有masking-key,則在Payload Len之后.這段數(shù)據(jù)的長度通過Payload Len獲取.
????好了,現(xiàn)在我們可以獲取到傳輸?shù)臄?shù)據(jù)并按照響應(yīng)格式將其解碼和編碼,但是在獲取到Payload Data且有掩碼時(shí),我們需要對其進(jìn)行進(jìn)一步解碼,解碼方式如下:
????此外,需要說明一下,掩碼并不是用于加密的,如需加密,則需要使用自己的加密方式,如先將數(shù)據(jù)進(jìn)行Aes加密,然后將其編碼為WebSocket協(xié)議數(shù)據(jù)包的格式.如果客戶端使用掩碼發(fā)送數(shù)據(jù),服務(wù)端無需使用掩碼進(jìn)行編碼(當(dāng)然也可以選擇使用掩碼).
????最后,在實(shí)現(xiàn)完基本功能后,我們可以對其進(jìn)行性能優(yōu)化,比如有掩碼時(shí)進(jìn)一步解碼Payload Data時(shí),可以使用向量化的方法來計(jì)算它,以提升性能.
四.代碼實(shí)現(xiàn)
五.最后
????-?如果大家要拿去用的話,編碼部分可以直接照抄,但是Tcp的實(shí)現(xiàn)部分要自己寫,或使用TcpListener替代