下面我详细分享了我的代码。我阅读了有关握手的文档和所有内容。我遵循了文档中给出的所有步骤和互联网上的许多例子,但我仍然有这个问题。奇怪的是,当我关闭服务器时,id为websocket.onlosse()被触发了。
// Simple Websocket server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
namespace WebSocketServer
{
class Program
{
//port
private static int _port = 8181;
static void Main(string[] args)
{
TcpListener t = new TcpListener(IPAddress.Loopback, _port);
t.Start();
Console.WriteLine("Server is started and waiting for clientnn");
byte[] buff = new byte[255];
NetworkStream stream;
TcpClient client;
while(true)
{
client = t.AcceptTcpClient();
if (!client.Connected)
return;
// I need form a proper mechanism to get all the data out of network stream.
// If I wait too long client get disconnected and we dont get stream and if
// if we dont wait at all then data doesnt reach server port and hence cant
// read the handshake.
stream = client.GetStream();
while ((stream.Read(buff, 0, buff.Length)) != 0)
{
break;
}
if (0 != buff.Length)
break;
}
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = true;
//while (stream.DataAvailable)
//stream.Read(buff, 0, buff.Length);
Console.WriteLine(System.Text.ASCIIEncoding.ASCII.GetString(buff));
string clientHandshake = System.Text.ASCIIEncoding.ASCII.GetString(buff);
char[] separators = new char[1];
separators[0] = 'n';
string[] temp = clientHandshake.Split(separators, 100);
string keyword = "Sec-WebSocket-Key";
string key = "";
foreach (string s in temp)
{
if (s.Contains(keyword))
{
string keyTemp= s.Substring(keyword.Length + 2);
key = keyTemp.Remove(keyTemp.Length - 1);
break;
}
}
string responseKey = GetServerResponseKey(key);
// Send Server handshake
string handshake =
"HTTP/1.1 101 Switching Protocolsrn" +
"Upgrade: websocketrn" +
"Connection: Upgradern" +
"Sec-WebSocket-Accept: " + responseKey + "rn";
writer.Write(handshake);
writer.Flush();
Console.WriteLine(handshake);
while ((stream.Read(buff, 0, buff.Length)) != 0)
{
break;
}
Console.WriteLine(System.Text.ASCIIEncoding.ASCII.GetString(buff));
// Keep Server alive
while (true)
{ }
}
//Helper method to convert string into Byte[]
private static byte[] GetByteArray(string str)
{
UTF8Encoding encoding = new UTF8Encoding();
return encoding.GetBytes(str);
}
//This method is requuired because it combines key(got it from client)
//with GUID. Then takes SHA1 hash of that string and then encode to base64.
//This is all required because Handshake mechanism can be done by only this
//way according to Websocket Protocol(http://datatracker.ietf.org/doc/rfc6455/)
private static string GetServerResponseKey(string key)
{
Console.WriteLine("original key = " + key);
string keyForHash = String.Concat(key, Guid.NewGuid());
Console.WriteLine("text version of server response key = " + keyForHash);
UTF8Encoding encoding = new UTF8Encoding();
byte[] temp = encoding.GetBytes(keyForHash);
SHA1 hashProvider = new SHA1CryptoServiceProvider();
byte[] keyForBase64 = hashProvider.ComputeHash(temp);
return Convert.ToBase64String(keyForBase64);
}
}
}
// Simple WebSocket Client
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebSocketClient._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
<script language="javascript" type = "text/javascript">
var ws;
function btnConnectSend_onclick() {
if ("WebSocket" in window) {
ws = new WebSocket("ws://localhost:8181");
ws.onopen = function() {
alert("Connection Open");
ws.send("Hello Server");
};
ws.onmessage = function(evt) {
form1.txtMessage.value = evt.data;
alert("Server says:" + evt.data);
};
ws.onclose = function() {
alert("Socket Closed!!!");
};
ws.onerror = function() {
alert("WTF!");
};
}
}
function btnClose_onclick() {
ws.close();
};
</script>
</head>
<body>
<form id="form1" runat="server">
<div style="height: 350px">
<input id="btnConnectSend" type="button" value="Connect/Send" onclick ="return btnConnectSend_onclick ()"/>
<br />
<input id="txtMessage" type="text"/>
<br />
<input id="btnClose" type="button" value="Close" onclick="return btnClose_onclick()"/>
</div>
</form>
</body>
</html>
我认为GetServerResponseKey()
中有一个错误。keyForHash
应分配给String.Concat(key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
附加到客户端密钥的值必须经过硬编码,并且不能是动态生成的guid。参见规范第4.2.2节第5点
另一点,您应该考虑检查Sec-WebocketProtocol标头的请求。如果这是由客户端发送的,它会期望您在握手响应中回显标头(当然,总是假设您的服务器支持子协议)。这不会导致握手暂停,但稍后可能会导致客户端拒绝您的握手响应。