WebScoket支持safari+chrome+firefox的規范和協議


WebScoket 規范

4.1 握手協議

websocket 是 獨立的基於TCP的協議, 其跟http協議的關系僅僅是 WebSocket 的握手被http 服務器當做 Upgrade request http包處理。 websocket 有自己的握手處理。 TCP連接建立后,client 發送websocket 握手請求. 請求包需求如下:

  • 必須是有效的http request 格式
  • HTTP request method 必須是GET,協議應不小於1.1 如: Get /chat HTTP/1.1
  • 必須包括Upgrade 頭域,並且其值為“websocket”
  • 必須包括"Connection" 頭域,並且其值為 "Upgrade"
  • 必須包括"Sec-WebSocket-Key"頭域,其值采用base64編碼的隨機16字節長的字符序列, 服務器端根據該域來判斷client 確實是websocket請求而不是冒充的,如http。響應方式是,首先要獲取到請求頭中的Sec-WebSocket-Key的值,再把這一段GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"加到獲取到的Sec-WebSocket-Key的值的后面,然后拿這個字符串做SHA-1 hash計算,然后再把得到的結果通過base64加密,就得到了返回給客戶端的Sec-WebSocket-Accept的http響應頭的值。
  • 如果請求來自瀏覽器客戶端,還必須包括Origin頭域 。 該頭域用於防止未授權的跨域腳本攻擊,服務器可以從Origin決定是否接受該WebSocket連接。
  • 必須包括"Sec-webSocket-Version" 頭域,當前值必須是13.
  • 可能包括"Sec-WebSocket-Protocol",表示client(應用程序)支持的協議列表,server選擇一個或者沒有可接受的協議響應之。
  • 可能包括"Sec-WebSocket-Extensions", 協議擴展, 某類協議可能支持多個擴展,通過它可以實現協議增強
  • 可能包括任意其他域,如cookie

示例如下:

        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13

Server 接手到握手請求后應處理該請求包括:

  • 處理請求包括處理GET 方法
  • 驗證Upgrader頭域
  • 驗證Connection 頭域
  • 處理Sec-WebSocket-Key頭域,方法見上;
  • 處理Sec-WebSocket-Version
  • 處理Origin頭域,可選, 瀏覽器必須發送該頭域
  • 處理Sec-WebSocket-Protocol頭域,可選
  • 處理Sec-WebSocket-Extensions 頭域,可選
  • 處理其他頭域,可選
  • Server 發送握手響應,這里只介紹服務器接受該連接情況下,包括:
  • http Status-Line
  • Upgrade 頭域 ,值必須是"websocket"
  • Conntion頭域,值必須是:“Upgrade”
  • Sec-WebSocket-Accept” 頭域,該頭域的值即處理Sec-WebSocket-Key" 域后的結果。
  • 可選的"Sec-WebSocket-Protocol"頭域
  • 可選的"Sec-WebSocket-Extensions"頭域

響應可能如下:

        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat

4.2 數據傳輸

該節主要參考了 http://blog.csdn.net/fenglibing/article/details/6852497。  在WebSocket 協議中,使用序列frames方式來傳輸數據。一個frame的標准格式如下:

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

FIN:1位,是否是消息的結束幀(分片)

RSV1, RSV2, RSV3: 分別都是1位, 預留,用於約定自定義協議。 如果雙方之間沒有約定自定義協議,那么這幾位的值都必須為0,否則必須斷掉WebSocket連接;

Opcode:4位操作碼,定義有效負載數據,如果收到了一個未知的操作碼,連接也必須斷掉,以下是定義的操作碼:

%x0 表示連續消息分片
%x1 表示文本消息分片

%x2 表未二進制消息分片

%x3-7 為將來的非控制消息片斷保留的操作碼

%x8 表示連接關閉  %x9 表示心跳檢查的ping

%xA 表示心跳檢查的pong

%xB-F 為將來的控制消息片斷的保留操作碼

Mask: 定義傳輸的數據是否有加掩碼,如果設置為1,掩碼鍵必須放在masking-key區域,客戶端發送給服務端的所有消息,此位的值都是1;

Payload length: 傳輸數據的長度,以字節的形式表示:7位、7+16位、或者7+64位。如果這個值以字節表示是0-125這個范圍,那這個值就表示傳輸數據的長度;如果這個值是126,則隨后的兩個字節表示的是一個16進制無符號數,用來表示傳輸數據的長度;如果這個值是127,則隨后的是8個字節表示的一個64位無符合數,這個數用來表示傳輸數據的長度。多字節長度的數量是以網絡字節的順序表示。負載數據的長度為擴展數據及應用數據之和,擴展數據的長度可能為0,因而此時負載數據的長度就為應用數據的長度。注意Payload length不包括Masking-key在內。

Masking-key: 0或4個字節,客戶端發送給服務端的數據,都是通過內嵌的一個32位值作為掩碼的;掩碼鍵只有在掩碼位設置為1的時候存在。 數據Mask方法是,第 i byte 數據 = orig-data ^ (i % 4) .

Payload data: (x+y)位,負載數據為擴展數據及應用數據長度之和。

Extension data:x位,如果客戶端與服務端之間沒有特殊約定,那么擴展數據的長度始終為0,任何的擴展都必須指定擴展數據的長度,或者長度的計算方式,以及在握手時如何確定正確的握手方式。如果存在擴展數據,則擴展數據就會包括在負載數據的長度之內。

Application data:y位,任意的應用數據,放在擴展數據之后,應用數據的長度=負載數據的長度-擴展數據的長度。

把消息分片處理主要是處於以下兩個原因:

  • 消息接收方事先並不知道消息大小, 而且也沒必要預留一個足夠大的buffer來處理;
  • multiplexing

消息分片一些規則如下(不全):

  • 為分片消息(single-frame) 其FIN置為1,並且opcode code 不是 0;
  • 分片消息序列如下, 第一幀FIN置為0,opcode code不是0; 接着是FIN置為0,opcode code也是0; 最后幀 FIN為1,opcode code為0.
  • 在分片消息發送期間可能插入了控制幀
  • 控制幀不能分片

控制幀的opcode符號位為1, 目前控制幀包括 0×8(Close), 0×9(Ping) 0xA (Pong). 0xB-0xF 被預留。

詳細解析如下,來自http://blog.csdn.net/fenglibing/article/details/6852497

   ws-frame                = frame-fin
                             frame-rsv1
                             frame-rsv2
                             frame-rsv3
                             frame-opcode
                             frame-masked
                             frame-payload-length
                             [ frame-masking-key ]
                             frame-payload-data
   frame-fin               = %x0 ; 表示這不是當前消息的最后一幀,后面還有消息
                           / %x1 ; 表示這是當前消息的最后一幀
   frame-rsv1              = %x0
                             ; 1 bit, 如果沒有擴展約定,該值必須為0
   frame-rsv2              = %x0
                             ; 1 bit, 如果沒有擴展約定,該值必須為0
   frame-rsv3              = %x0
                             ; 1 bit, 如果沒有擴展約定,該值必須為0
   frame-opcode            = %x0 ; 表示這是一個連續幀消息
                           / %x1 ; 表示文本消息
                           / %x2 ; 表示二進制消息
                           / %x3-7 ; 保留
                           / %x8 ; 表示客戶端發起的關閉
                           / %x9 ; ping(用於心跳)
                           / %xA ; pong(用於心跳)
                           / %xB-F ; 保留
   frame-masked            = %x0 ; 數據幀沒有加掩碼,后面沒有掩碼key
                           / %x1 ; 數據幀加了掩碼,后面有掩碼key
   frame-payload-length    = %x00-7D
                           / %x7E frame-payload-length-16
                           / %x7F frame-payload-length-63
			   ; 表示數據幀的長度
   frame-payload-length-16 = %x0000-FFFF
			   ; 表示數據幀的長度
   frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
			   ; 表示數據幀的長度
   frame-masking-key       = 4( %0x00-FF ) ; 掩碼key,只有當掩碼位為1時出現
   frame-payload-data      = (frame-masked-extension-data
                              frame-masked-application-data)  
                  ; 當掩碼位為1時,這里的數據為帶掩碼的數據,擴展數據及應用數據都帶掩碼
                           / (frame-unmasked-extension-data
                              frame-unmasked-application-data) ;
                 當掩碼位為0時,這里的數據為不帶掩碼的數據,擴展數據及應用數據都不帶掩碼

   frame-masked-extension-data     = *( %x00-FF ) ; 目前保留,以后定義
   frame-masked-application-data   = *( %x00-FF )
   frame-unmasked-extension-data   = *( %x00-FF ) ; 目前保留,以后定義
   frame-unmasked-application-data = *( %x00-FF )

 

Close 處理

Close 幀的opcode是0×8. 接收到 Close 幀后,如果之前沒發送過Close幀,則其必須發送Close 幀響應,但其可以延遲發送Close響應幀,例如在其發送完數據之后發送;但是,協議不保證對方在發送Close 幀后仍會處理其后續的數據。Close幀可能Client發起也可能是Server發起。

Ping-Pong 幀

接收到Ping幀后將響應Pong幀, 主要用於檢測網絡連接情況。

Extensions

WebSocket 支持協議擴展。 例如增加一個認證處理或者速率控制等,這通過client-server 協商完成。在WebSocket 握手處理時,通過頭域Sec-WebSocket-Extensions來完成協商。 例如:

Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
          deflate-stream

服務器接收一個或多個extensiions 通過再起響應的Sec-WebSocket-Extensions頭域增加一個或多個extension完成。

 

說明:

 

服務器建立成功之后,如果有客戶端請求連接本服務器,需要用socket_accept等方法建立一個新的socket連接,並接收客戶端的請求信息,處理之后,返回響應信息,然后握手成功。  

接下來是字符串通信,客戶端send過來一段字符串信息,服務器端接收到並返回給客戶端這個字符串。   首先我們處理接收到的信息,根據上篇文章介紹的數據傳輸格式,並firefox的FIN一直為1,RSV1,2,3為0,如果是文本消息,那么opcode為1,所以數據包的第一個數據是0x81,然后是一位mask值,firefox發來的數據是加了掩碼的,所以mask值為1,后面跟7位是數據信息長度,我們以客戶端發送hi為例,那么長度就是2個字節,則第二個數據就是0x82,這里沒有約定擴展數據,所以不存在擴展數據長度字節,接下來是4個數據的掩碼(因為我們這里是發送hi,2個字節的信息,小於125個字節,所以掩碼是第3-第6個數據,根據數據長度的不同,掩碼的位置也不同,如果取到那7位表示的值是126,則掩碼為第5-第8個數據,如果取到那7位表示的值是127,則掩碼為第11-第14個數據),后面跟客戶端發送的內容數據,處理接收到的數據我們需要用取到的掩碼依次輪流跟內容數據做異或(^)運算,第一個內容數據與第一個掩碼異或,第二個內容數據與第二個掩碼異或……第五個內容數據與第一個掩碼異或……以此類推,一直到結束,然后對內容進行編碼。

 

根據數據長度的不同,掩碼的位置也不同:

從第9個字節開始是 1111101=125,掩碼是第3-第6個數據

從第9個字節開始是 1111110=126,掩碼是第5-第8個數據

從第9個字節開始是 1111111=126,掩碼是第11-第14個數據

 

舉例一:

 1 hi
2 1000000110000010 1101011011101001
3 111110 111000 10111110 10000000
4 111110 111001 11010110 11101001
5 1101000 1101001
6
7 [0] 129 byte
8 [1] 130 byte
9 [2] 214 byte
10 [3] 233 byte
11 [4] 62 byte
12 [5] 56 byte
13 [6] 190 byte
14 [7] 128 byte
15
16
17 1234567890
18 [0] 129 byte
19 [1] 138 byte
20
21 [2] 108 byte
22 [3] 255 byte
23 [4] 86 byte
24 [5] 166 byte
25
26 [6] 93 byte
27 [7] 205 byte
28 [8] 101 byte
29 [9] 146 byte
30 [10] 89 byte
31 [11] 201 byte
32 [12] 97 byte
33 [13] 158 byte
34 [14] 85 byte
35 [15] 207 byte


 舉例二:

 

  1 01234567890123456789012345678901234567890123456789
2 01234567890123456789012345678901234567890123456789
3 01234567890123456789012345678901234567890123456789
4 01234567890123456789012345678901234567890123456789
5
6 01234567890123456789012345678901
7 10000001111111100110010011001101
8
9 [0] 129 byte
10 [1] 254 byte
11 [2] 0 byte
12 [3] 201 byte
13
14 [4] 77 byte
15 [5] 175 byte
16 [6] 124 byte
17 [7] 107 byte
18
19 [8] 125 byte
20 [9] 158 byte
21 [10] 78 byte
22 [11] 88 byte
23 [12] 121 byte
24 [13] 154 byte
25 [14] 74 byte
26 [15] 92 byte
27 [16] 117 byte
28 [17] 150 byte
29 [18] 76 byte
30 [19] 90 byte
31 [20] 127 byte
32 [21] 156 byte
33 [22] 72 byte
34 [23] 94 byte
35 [24] 123 byte
36 [25] 152 byte
37 [26] 68 byte
38 [27] 82 byte
39 [28] 125 byte
40 [29] 158 byte
41 [30] 78 byte
42 [31] 88 byte
43 [32] 121 byte
44 [33] 154 byte
45 [34] 74 byte
46 [35] 92 byte
47 [36] 117 byte
48 [37] 150 byte
49 [38] 76 byte
50 [39] 90 byte
51 [40] 127 byte
52 [41] 156 byte
53 [42] 72 byte
54 [43] 94 byte
55 [44] 123 byte
56 [45] 152 byte
57 [46] 68 byte
58 [47] 82 byte
59 [48] 125 byte
60 [49] 158 byte
61 [50] 78 byte
62 [51] 88 byte
63 [52] 121 byte
64 [53] 154 byte
65 [54] 74 byte
66 [55] 92 byte
67 [56] 117 byte
68 [57] 150 byte
69 [58] 76 byte
70 [59] 90 byte
71 [60] 127 byte
72 [61] 156 byte
73 [62] 72 byte
74 [63] 94 byte
75 [64] 123 byte
76 [65] 152 byte
77 [66] 68 byte
78 [67] 82 byte
79 [68] 125 byte
80 [69] 158 byte
81 [70] 78 byte
82 [71] 88 byte
83 [72] 121 byte
84 [73] 154 byte
85 [74] 74 byte
86 [75] 92 byte
87 [76] 117 byte
88 [77] 150 byte
89 [78] 76 byte
90 [79] 90 byte
91 [80] 127 byte
92 [81] 156 byte
93 [82] 72 byte
94 [83] 94 byte
95 [84] 123 byte
96 [85] 152 byte
97 [86] 68 byte
98 [87] 82 byte
99 [88] 125 byte
100 [89] 158 byte
101 [90] 78 byte
102 [91] 88 byte
103 [92] 121 byte
104 [93] 154 byte
105 [94] 74 byte
106 [95] 92 byte
107 [96] 117 byte
108 [97] 150 byte
109 [98] 76 byte
110 [99] 90 byte
111 [100] 127 byte
112 [101] 156 byte
113 [102] 72 byte
114 [103] 94 byte
115 [104] 123 byte
116 [105] 152 byte
117 [106] 68 byte
118 [107] 82 byte
119 [108] 125 byte
120 [109] 158 byte
121 [110] 78 byte
122 [111] 88 byte
123 [112] 121 byte
124 [113] 154 byte
125 [114] 74 byte
126 [115] 92 byte
127 [116] 117 byte
128 [117] 150 byte
129 [118] 76 byte
130 [119] 90 byte
131 [120] 127 byte
132 [121] 156 byte
133 [122] 72 byte
134 [123] 94 byte
135 [124] 123 byte
136 [125] 152 byte
137 [126] 68 byte
138 [127] 82 byte
139 [128] 125 byte
140 [129] 158 byte
141 [130] 78 byte
142 [131] 88 byte
143 [132] 121 byte
144 [133] 154 byte
145 [134] 74 byte
146 [135] 92 byte
147 [136] 117 byte
148 [137] 150 byte
149 [138] 76 byte
150 [139] 90 byte
151 [140] 127 byte
152 [141] 156 byte
153 [142] 72 byte
154 [143] 94 byte
155 [144] 123 byte
156 [145] 152 byte
157 [146] 68 byte
158 [147] 82 byte
159 [148] 125 byte
160 [149] 158 byte
161 [150] 78 byte
162 [151] 88 byte
163 [152] 121 byte
164 [153] 154 byte
165 [154] 74 byte
166 [155] 92 byte
167 [156] 117 byte
168 [157] 150 byte
169 [158] 76 byte
170 [159] 90 byte
171 [160] 127 byte
172 [161] 156 byte
173 [162] 72 byte
174 [163] 94 byte
175 [164] 123 byte
176 [165] 152 byte
177 [166] 68 byte
178 [167] 82 byte
179 [168] 125 byte
180 [169] 158 byte
181 [170] 78 byte
182 [171] 88 byte
183 [172] 121 byte
184 [173] 154 byte
185 [174] 74 byte
186 [175] 92 byte
187 [176] 117 byte
188 [177] 150 byte
189 [178] 76 byte
190 [179] 90 byte
191 [180] 127 byte
192 [181] 156 byte
193 [182] 72 byte
194 [183] 94 byte
195 [184] 123 byte
196 [185] 152 byte
197 [186] 68 byte
198 [187] 82 byte
199 [188] 125 byte
200 [189] 158 byte
201 [190] 78 byte
202 [191] 88 byte
203 [192] 121 byte
204 [193] 154 byte
205 [194] 74 byte
206 [195] 92 byte
207 [196] 117 byte
208 [197] 150 byte
209 [198] 76 byte
210 [199] 90 byte
211 [200] 127 byte
212 [201] 156 byte
213 [202] 72 byte
214 [203] 94 byte
215 [204] 123 byte
216 [205] 152 byte
217 [206] 68 byte
218 [207] 82 byte
219 [208] 71 byte

 

 

代碼分析掩碼:

 1 /// <summary>
2 ///判斷傳入數據是否存在掩碼
3 /// 傳入數據:hi
4 /// socket接收到的二進制數據:
5 /// 1000000110000010 1101011011101001
6 /// 111110 111000 10111110 10000000
7 /// 掩碼異或的操作:
8 /// 111110 111000 10111110 10000000
9 /// 進行異或^ 111110 111001 11010110 11101001
10 /// 結果: 1101000 1101001
11 /// 數據樣例:
12 /// [0] 129 byte
13 /// [1] 130 byte
14 /// [2] 214 byte
15 /// [3] 233 byte
16 /// [4] 62 byte
17 /// [5] 56 byte
18 /// [6] 190 byte
19 /// [7] 128 byte
20 /// </summary>
21 /// <returns></returns>
22 private string UnWrap()
23 {
24 string result = string.Empty;
25
26 // 計算非空位置
27 int lastStation = GetLastZero();
28
29 // 利用掩碼對org-data進行異或
30 int frame_masking_key = 1;
31 for (int i = 6; i <= lastStation; i++)
32 {
33 frame_masking_key = i % 4;
34 frame_masking_key = frame_masking_key == 0 ? 4 : frame_masking_key;
35 frame_masking_key = frame_masking_key == 1 ? 5 : frame_masking_key;
36 receivedDataBuffer[i] = Convert.ToByte(receivedDataBuffer[i] ^ receivedDataBuffer[frame_masking_key]);
37 }
38
39 System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
40 result = decoder.GetString(receivedDataBuffer, 6, lastStation - 6 + 1);
41
42 return result;
43
44 }

 

 

 1 /// <summary>
2 /// 對傳入數據進行無掩碼轉換
3 /// </summary>
4 /// <returns></returns>
5 public static byte[] Wrap(string msg, int maxBufferSize)
6 {
7 // 掩碼開始位置
8 int masking_key_startIndex = 2;
9
10 byte[] msgByte = Encoding.UTF8.GetBytes(msg);
11
12 // 計算掩碼開始位置
13 if (msgByte.Length <= 125)
14 {
15 masking_key_startIndex = 2;
16 }
17 else if (msgByte.Length > 65536)
18 {
19 masking_key_startIndex = 10;
20 }
21 else if (msgByte.Length > 125)
22 {
23 masking_key_startIndex = 4;
24 }
25
26 // 創建返回數據
27 byte[] result = new byte[msgByte.Length + masking_key_startIndex];
28
29 // 開始計算ws-frame
30         // frame-fin + frame-rsv1 + frame-rsv2 + frame-rsv3 + frame-opcode
31 result[0] = 0x81; // 129
32
33 // frame-masked+frame-payload-length
34 // 從第9個字節開始是 1111101=125,掩碼是第3-第6個數據
35 // 從第9個字節開始是 1111110>=126,掩碼是第5-第8個數據
36 if (msgByte.Length <= 125)
37 {
38 result[1] = Convert.ToByte(msgByte.Length);
39 }
40 else if (msgByte.Length > 65536)
41 {
42 result[1] = 0x7F; // 127
43 }
44 else if (msgByte.Length > 125)
45 {
46 result[1] = 0x7E; // 126
47 result[2] = Convert.ToByte(msgByte.Length >> 8);
48 result[3] = Convert.ToByte(msgByte.Length % 256);
49 }
50
51 // 將數據編碼放到最后
52 Array.Copy(msgByte, 0, result, masking_key_startIndex, msgByte.Length);
53
54 return result;
55 }



 

 WebSocket 協議:

 

public enum WebSocketProtocol
{
/*
*
* Request
GET /WebIM5?uaid=200513807p8912-8de8c7e2-c963-4f67-8aca-8028797efbc1&re=0 HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: 10.10.150.60:5002
Origin:
https://localhost:444
Sec-WebSocket-Key1: 3+3 1 8kgV"m 0 8 64u43
Sec-WebSocket-Key2: 3_7891 6 4 `50 `8
*
* Response
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin:
https://localhost:444
Sec-WebSocket-Location: ws://192.168.110.....
Sec-WebSocket-Protocol: WebIM5
*
* asdfalskdfa
*
*/
draft_00 = 0,

/*
*
* Request
GET /WebIM5?uaid=200513807p8912-2e695e5b-9b46-4511-b59e-28981b4ab327&re=0 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 10.10.150.60:5002
Origin:
https://localhost:444
Sec-WebSocket-Key: 1o4Jk9XPGvTX66OxmNMaww==
Sec-WebSocket-Version: 13
*
* Response
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: WebIM5
*
*/
draft_17 = 17
}

 

 支持safari+chrome+firefox:

 1 // 開始連接到服務器
2 var pollingInterval;
3 _ws = new WebSocket("ws://192.168.0.103:5002/WebIM5?uaid=200513807p8912-5a78ae8a-cabb-46ee-8d8a-85874bbc942c&re=0");
4 //_ws = new window.MozWebSocket("ws://192.168.0.103:5002/WebIM5?uaid=200513807p8912-5a78ae8a-cabb-46ee-8d8a-85874bbc942c&re=0");
5 _ws.onopen = function () {
6 alert("onopen");
7
8 _socketCreated = true;
9 var args
10 _ws.send("1234567890");
11 _ws.send("33322233");
12 };
13 _ws.onmessage = function (event) {
14 console.log("event.data=" + event.data);
15
16 };
17 _ws.onclose = function () {
18 alert("onclose");
19 console.log("onclose");
20 };
21 _ws.onerror = function () {
22 console.log("onerror");
23 };
24 function send() {
25 _ws.send(document.getElementById("msg").value);
26 }

 

其中,safari和chrome都是 :_ws = new WebSocket("ws://ip:port");  但是firefox是:_ws = new window.MozWebSocket("ws://ip:port");

 

瀏覽器方面:

chrome可以查看WebSocket的訪問日志: chrome://net-internals/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM