WebSocket(13)とC#通信
(1)WebSocketとは
WebSocketは、HTML 5が提供し始めたブラウザとサーバ間のフルデュプレクス通信のネットワーク技術である.
WebSocket APIでは、ブラウザとサーバが握手をするだけで、ブラウザとサーバの間に高速チャネルが形成されます.両者の間で直接データを互いに転送することができる.
メリット:
a、サーバとクライアントの間で交換されるヘッダ情報は小さく、約2バイトしかありません
b、サーバーは自発的にクライアントにデータを転送できる
(2)WebSocket(13)握手
WebSocket握手はクライアントによって開始され、サンプルを報告します.
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
ここでSec-WebSocket-Versionはバージョン番号が13であることを示しています.注意Sec-WebSocket-Key、これはクライアントが送信した鍵で、サービス側はこの鍵を処理し、クライアントにフィードバックする必要があります.クライアントは鍵が正しいことを検証してから通信を開始します.その後、この鍵は役に立ちません.
サービス側フィードバックサンプル:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
C#サービス側はまず、Sec-WebSocket-Keyの文字列を抽出し、「258 EAFA 5-E 914-47 DA-95 CA-C 5 AB 0 DC 85 B 11」(固定GUID)を加え、SHA 1でハッシュコードを計算し、base 64で暗号化し、最終的にSec-WebSocket-Acceptの内容を生成する.握手コード:
(3)クライアントデータの受信
クライアントはsendメソッドを呼び出して、文字をサービス側に送信します.サービス側はframeの最初の2つのbyteをバイナリ(bit)で解析します.プロセスは次のとおりです.
1byte
1 bit:frame-fin、x 0はこのmessageの後続にframeがあることを示す.x 1はメッセージの最後のフレームを表す
3 bit:それぞれframe-rsv 1、frame-rsv 2、frame-rsv 3で、通常はx 0
4 bit:frame-opcode、x 0はframeの継続を表す.x 1はテキストframeを表す.x 2はバイナリフレームを表す.x 3-7は非制御frameに保持される.x 8は接続を閉じることを示す.x 9はpingを表す.xAはpongを表す.xB-Fは制御frameに保持
2byte
1 bit:Mask,1はこのframeがマスクを含むことを示す;0、マスクなし
7 bit、7 bit+2 byte、7 bit+8 byte:7 bitは整数値をとり、0-125の間であれば負荷データ長である.126で表される場合、後の2つのbyteは符号なし16ビットの整数値をとり、負荷長である.127は後の8 byteを表し、64ビットの符号なし整数値をとり、負荷長である
3-6 byte:ここで負荷長が0-125の間であり、Maskが1であると仮定すると、この4つのbyteはマスクである
7-end byte:長さは上から取り出した負荷長であり、拡張データとアプリケーションデータの2つの部分を含み、通常は拡張データがない.Maskが1の場合、このデータは復号する必要があり、復号規則は1-4 byteマスクサイクルとデータbyteが異種または動作する.
C#受信データコードは以下の通りです.
(3)クライアントへのデータ送信
サーバから送信されたデータは0 x 81で始まり、直後に送信されるコンテンツの長さ(長さが0-125の場合、1つのbyteが長さを表し、長さが0 x FFFFを超えない場合、後の2つのbyteが符号なし16ビット整数として長さを表し、0 x FFFFを超える場合、後の8つのbyteが符号なし64ビット整数として長さを表す)、最後にコンテンツのbyte配列である.
C#送信コード:
(4)htmlクライアントコード
添付ファイルはサービス側コード、htmlテストページです.
以上
WebSocketは、HTML 5が提供し始めたブラウザとサーバ間のフルデュプレクス通信のネットワーク技術である.
WebSocket APIでは、ブラウザとサーバが握手をするだけで、ブラウザとサーバの間に高速チャネルが形成されます.両者の間で直接データを互いに転送することができる.
メリット:
a、サーバとクライアントの間で交換されるヘッダ情報は小さく、約2バイトしかありません
b、サーバーは自発的にクライアントにデータを転送できる
(2)WebSocket(13)握手
WebSocket握手はクライアントによって開始され、サンプルを報告します.
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
ここでSec-WebSocket-Versionはバージョン番号が13であることを示しています.注意Sec-WebSocket-Key、これはクライアントが送信した鍵で、サービス側はこの鍵を処理し、クライアントにフィードバックする必要があります.クライアントは鍵が正しいことを検証してから通信を開始します.その後、この鍵は役に立ちません.
サービス側フィードバックサンプル:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
C#サービス側はまず、Sec-WebSocket-Keyの文字列を抽出し、「258 EAFA 5-E 914-47 DA-95 CA-C 5 AB 0 DC 85 B 11」(固定GUID)を加え、SHA 1でハッシュコードを計算し、base 64で暗号化し、最終的にSec-WebSocket-Acceptの内容を生成する.握手コード:
private void handShake(byte[] recBytes, int recByteLength)
{
string recStr = Encoding.UTF8.GetString(recBytes, 0, recByteLength);
string[] ss = recStr.Split(Environment.NewLine.ToCharArray());
string key = ss[10].Replace("Sec-WebSocket-Key: ", "");
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1 sha1 = SHA1.Create();
byte[] sha1bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
string acceptStr = Convert.ToBase64String(sha1bytes);
string sendStr = "HTTP/1.1 101 Switching Protocols" + NewLine +
"Upgrade: websocket" + NewLine +
"Connection: Upgrade" + NewLine +
"Sec-WebSocket-Accept: " + acceptStr + NewLine +
"Sec-WebSocket-Protocol: chat" + NewLine + NewLine;
client.Send(System.Text.Encoding.UTF8.GetBytes(sendStr));
isHandshaked = true;
}
(3)クライアントデータの受信
クライアントはsendメソッドを呼び出して、文字をサービス側に送信します.サービス側はframeの最初の2つのbyteをバイナリ(bit)で解析します.プロセスは次のとおりです.
1byte
1 bit:frame-fin、x 0はこのmessageの後続にframeがあることを示す.x 1はメッセージの最後のフレームを表す
3 bit:それぞれframe-rsv 1、frame-rsv 2、frame-rsv 3で、通常はx 0
4 bit:frame-opcode、x 0はframeの継続を表す.x 1はテキストframeを表す.x 2はバイナリフレームを表す.x 3-7は非制御frameに保持される.x 8は接続を閉じることを示す.x 9はpingを表す.xAはpongを表す.xB-Fは制御frameに保持
2byte
1 bit:Mask,1はこのframeがマスクを含むことを示す;0、マスクなし
7 bit、7 bit+2 byte、7 bit+8 byte:7 bitは整数値をとり、0-125の間であれば負荷データ長である.126で表される場合、後の2つのbyteは符号なし16ビットの整数値をとり、負荷長である.127は後の8 byteを表し、64ビットの符号なし整数値をとり、負荷長である
3-6 byte:ここで負荷長が0-125の間であり、Maskが1であると仮定すると、この4つのbyteはマスクである
7-end byte:長さは上から取り出した負荷長であり、拡張データとアプリケーションデータの2つの部分を含み、通常は拡張データがない.Maskが1の場合、このデータは復号する必要があり、復号規則は1-4 byteマスクサイクルとデータbyteが異種または動作する.
C#受信データコードは以下の通りです.
private bool recData(byte[] recBytes, int recByteLength)
{
if (recByteLength < 2)
return false;
bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1
if (!fin)
{
Console.WriteLine("recData exception: "); //
return false;
}
bool mask_flag = (recBytes[1] & 0x80) == 0x80; //
if (!mask_flag)
{
Console.WriteLine("recData exception: Mask"); //
return false;
}
int payload_len = recBytes[1] & 0x7F; //
byte[] masks = new byte[4];
byte[] payload_data;
if (payload_len == 126)
{
Array.Copy(recBytes, 4, masks, 0, 4);
payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 8, payload_data, 0, payload_len);
}
else if (payload_len == 127)
{
Array.Copy(recBytes, 10, masks, 0, 4);
byte[] uInt64Bytes = new byte[8];
for (int i = 0; i < 8; i++)
{
uInt64Bytes[i] = recBytes[9 - i];
}
UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
payload_data = new byte[len];
for (UInt64 i = 0; i < len; i++)
payload_data[i] = recBytes[i + 14];
}
else
{
Array.Copy(recBytes, 2, masks, 0, 4);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 6, payload_data, 0, payload_len);
}
for (var i = 0; i < payload_len; i++)
payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
string content = Encoding.UTF8.GetString(payload_data);
Console.WriteLine("client: {0}", content);
return true;
}
(3)クライアントへのデータ送信
サーバから送信されたデータは0 x 81で始まり、直後に送信されるコンテンツの長さ(長さが0-125の場合、1つのbyteが長さを表し、長さが0 x FFFFを超えない場合、後の2つのbyteが符号なし16ビット整数として長さを表し、0 x FFFFを超える場合、後の8つのbyteが符号なし64ビット整数として長さを表す)、最後にコンテンツのbyte配列である.
C#送信コード:
private void sendData(string content)
{
byte[] contentBytes = null;
byte[] temp = Encoding.UTF8.GetBytes(content);
if (temp.Length < 126)
{
contentBytes = new byte[temp.Length + 2];
contentBytes[0] = 0x81;
contentBytes[1] = (byte)temp.Length;
Array.Copy(temp, 0, contentBytes, 2, temp.Length);
}
else if (temp.Length < 0xFFFF)
{
contentBytes = new byte[temp.Length + 4];
contentBytes[0] = 0x81;
contentBytes[1] = 126;
contentBytes[2] = (byte)(temp.Length & 0xFF);
contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
Array.Copy(temp, 0, contentBytes, 4, temp.Length);
}
else
{
//
}
client.Send(contentBytes);
}
(4)htmlクライアントコード
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>WebSocket</title>
<script type="text/javascript">
if (!window.WebSocket)
alert("WebSocket not supported by this browser!");
var ws;
function connectWS() {
if (ws == null) {
ws = new WebSocket("ws://127.0.0.1:5001");
ws.onmessage = function (evt) {
alert(" :" + evt.data);
};
ws.onclose = function () {
alert(" 。");
ws = null;
};
ws.onerror = function (evt) {
alert(" :" + evt.data);
ws = null;
}
ws.onopen = function (evt) {
alert(" 。");
};
}
}
function sendWS() {
if (ws != null) {
try {
var sendstr = document.getElementById("txtSend").value;
if (sendstr == "")
return;
ws.send(sendstr);
} catch (err) {
alert(err.Data);
}
} else {
alert(" 。");
}
}
function closeWS() {
ws.send("exit");
ws.close();
}
</script>
</head>
<body style="text-align: center;">
<input type="button" value=" " onclick="connectWS()" />
<input type="button" value=" " onclick="closeWS()" />
<br />
<input type="text" id="txtSend" /><input type="button" id="btnSend" value=" " onclick="sendWS()" />
</body>
</html>
添付ファイルはサービス側コード、htmlテストページです.
以上