我有这个WCF自托管的WebSocket服务代码:
主要
//Create a URI to serve as the base address
Uri httpUrl = new Uri("http://192.168.1.95:8080/service");
//Create ServiceHost
ServiceHost host = new ServiceHost(typeof(WebSocketService), httpUrl);
//Add a service endpoint
host.AddServiceEndpoint(typeof(IWebSocket), new NetHttpBinding(), "");
//Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
//Start the Service
host.Open();
Console.WriteLine("Service is host at " + DateTime.Now.ToString());
Console.WriteLine("Host is running... Press <Enter> key to stop");
Console.ReadLine();
接口
namespace IWebSocketHostTest
{
[ServiceContract]
interface IWebSocketCallBack
{
[OperationContract(IsOneWay = true)]
void Send(int num);
}
[ServiceContract(CallbackContract = typeof(IWebSocketCallBack))]
public interface IWebSocket
{
[OperationContract]
void StartSend();
}
}
服务
namespace IWebSocketHostTest
{
class WebSocketService : IWebSocket
{
Timer timer = null;
List<IWebSocketCallBack> callbackClientList = null;
public WebSocketService()
{
callbackClientList = new List<IWebSocketCallBack>();
timer = new Timer(3000);
timer.Elapsed += new ElapsedEventHandler(sendNumber);
timer.Start();
}
public void StartSend()
{
sender.addClient(OperationContext.Current.GetCallbackChannel<IWebSocketCallBack>());
}
private void sendNumber(Object o, ElapsedEventArgs eea)
{
timer.Stop();
var random = new Random();
int randomNum = random.Next(100);
foreach (IWebSocketCallBack callback in callbackClientList)
{
callback.Send(randomNum);
}
timer.Interval = random.Next(1000, 10000);
timer.Start();
}
}
}
如果我在另一个.NET应用程序中添加此服务的引用,这将非常有用。 但是,我需要的是从HTML + Javascript应用程序中使用此服务,我真的迷失了如何做到这一点。我找不到一个使用自托管WCF WebSocket服务的Javascript客户端的好例子或教程。 我能找到的所有Javascript WebSocket代码似乎都非常简单,但我无法使其工作。
这是我的简短JavaScript客户端测试:
var ws = new WebSocket("ws://192.168.1.95:8080/service");
ws.onopen = function () {
console.log("WEBSOCKET CONNECTED");
};
它返回“WebSocket错误:HTTP响应不正确。状态代码400,错误请求”用Fiddler测试它。
我缺少什么?您能否给我一些文档链接以获取更多信息或代码示例?
谢谢!
修改
现在我尝试使用“Microsoft.ServiceModel.WebSocket”库来尝试使其正常工作。
但是,首先,我不知道它是否仍由微软维护或是否已被弃用,因为我无法在MSDN上找到任何信息并且互联网上的信息很少。 第二,找不到“WebSocketHost”类的“Open()”方法,所以我不知道如何让服务器运行......
这是我的代码,我是从ASP.NET论坛的question获取的。
using System;
using Microsoft.ServiceModel.WebSockets;
namespace WebSocketTest
{
class Program
{
static void Main(string[] args)
{
var host = new WebSocketHost<EchoService>(new Uri("ws://localhost:8080/echo"));
host.AddWebSocketEndpoint();
host.Open();
Console.Read();
host.Close();
}
}
class EchoService : WebSocketService
{
public override void OnOpen()
{
base.OnOpen();
Console.WriteLine("WebSocket opened.");
}
public override void OnMessage(string message)
{
Console.WriteLine("Echoing to client:");
Console.WriteLine(message);
this.Send(message);
}
protected override void OnClose()
{
base.OnClose();
Console.WriteLine("WebSocket closed.");
}
protected override void OnError()
{
base.OnError();
Console.WriteLine("WebSocket error occured.");
}
}
}
但是,就像我之前说的那样,找不到“host.Open()”方法,所以我不知道我是否缺少一些参考或什么,因为我找不到关于WebSocketHost类的信息......有什么帮助吗?
答案 0 :(得分:29)
在完成相同任务的一天后,我终于得到了解决方案。希望它能帮助将来的某个人。
客户端JS脚本:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WebSocket Chat</title>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.1.js"></script>
<script type="text/javascript">
var ws;
$().ready(function ()
{
$("#btnConnect").click(function ()
{
$("#spanStatus").text("connecting");
ws = new WebSocket("ws://localhost:8080/hello");
ws.onopen = function ()
{
$("#spanStatus").text("connected");
};
ws.onmessage = function (evt)
{
$("#spanStatus").text(evt.data);
};
ws.onerror = function (evt)
{
$("#spanStatus").text(evt.message);
};
ws.onclose = function ()
{
$("#spanStatus").text("disconnected");
};
});
$("#btnSend").click(function ()
{
if (ws.readyState == WebSocket.OPEN)
{
var res = ws.send($("#textInput").val());
}
else
{
$("#spanStatus").text("Connection is closed");
}
});
$("#btnDisconnect").click(function ()
{
ws.close();
});
});
</script>
</head>
<body>
<input type="button" value="Connect" id="btnConnect" />
<input type="button" value="Disconnect" id="btnDisconnect" /><br />
<input type="text" id="textInput" />
<input type="button" value="Send" id="btnSend" /><br />
<span id="spanStatus">(display)</span>
</body>
</html>
自托管服务器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
namespace WebSocketsServer
{
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/hello");
// Create the ServiceHost.
using(ServiceHost host = new ServiceHost(typeof(WebSocketsServer), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new ByteStreamMessageEncodingBindingElement());
HttpTransportBindingElement transport = new HttpTransportBindingElement();
//transport.WebSocketSettings = new WebSocketTransportSettings();
transport.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always;
transport.WebSocketSettings.CreateNotificationOnConnection = true;
binding.Elements.Add(transport);
host.AddServiceEndpoint(typeof(IWebSocketsServer), binding, "");
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
[ServiceContract(CallbackContract = typeof(IProgressContext))]
public interface IWebSocketsServer
{
[OperationContract(IsOneWay = true, Action = "*")]
void SendMessageToServer(Message msg);
}
[ServiceContract]
interface IProgressContext
{
[OperationContract(IsOneWay = true, Action = "*")]
void ReportProgress(Message msg);
}
public class WebSocketsServer: IWebSocketsServer
{
public void SendMessageToServer(Message msg)
{
var callback = OperationContext.Current.GetCallbackChannel<IProgressContext>();
if(msg.IsEmpty || ((IChannel)callback).State != CommunicationState.Opened)
{
return;
}
byte[] body = msg.GetBody<byte[]>();
string msgTextFromClient = Encoding.UTF8.GetString(body);
string msgTextToClient = string.Format(
"Got message {0} at {1}",
msgTextFromClient,
DateTime.Now.ToLongTimeString());
callback.ReportProgress(CreateMessage(msgTextToClient));
}
private Message CreateMessage(string msgText)
{
Message msg = ByteStreamMessage.CreateMessage(
new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgText)));
msg.Properties["WebSocketMessageProperty"] =
new WebSocketMessageProperty
{
MessageType = WebSocketMessageType.Text
};
return msg;
}
}
}
<强>更新强>
截至.net 4.5已经出现了编写服务器端的新方法。优点是更清晰的代码以及通过https支持安全Web套接字(WSS)的可能性。
public class WebSocketsServer
{
#region Fields
private static CancellationTokenSource m_cancellation;
private static HttpListener m_listener;
#endregion
#region Private Methods
private static async Task AcceptWebSocketClientsAsync(HttpListener server, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var hc = await server.GetContextAsync();
if (!hc.Request.IsWebSocketRequest)
{
hc.Response.StatusCode = 400;
hc.Response.Close();
return;
}
try
{
var ws = await hc.AcceptWebSocketAsync(null).ConfigureAwait(false);
if (ws != null)
{
Task.Run(() => HandleConnectionAsync(ws.WebSocket, token));
}
}
catch (Exception aex)
{
// Log error here
}
}
}
private static async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancellation)
{
try
{
while (ws.State == WebSocketState.Open && !cancellation.IsCancellationRequested)
{
String messageString = await ReadString(ws).ConfigureAwait(false);
var strReply = "OK"; // Process messageString and get your reply here;
var buffer = Encoding.UTF8.GetBytes(strReply);
var segment = new ArraySegment<byte>(buffer);
await ws.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);
}
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Done", CancellationToken.None);
}
catch (Exception aex)
{
// Log error
try
{
await ws.CloseAsync(WebSocketCloseStatus.InternalServerError, "Done", CancellationToken.None).ConfigureAwait(false);
}
catch
{
// Do nothing
}
}
finally
{
ws.Dispose();
}
}
private static async Task<String> ReadString(WebSocket ws)
{
ArraySegment<Byte> buffer = new ArraySegment<byte>(new Byte[8192]);
WebSocketReceiveResult result = null;
using (var ms = new MemoryStream())
{
do
{
result = await ws.ReceiveAsync(buffer, CancellationToken.None);
ms.Write(buffer.Array, buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(ms, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
#endregion
#region Public Methods
public static void Start(string uri)
{
m_listener = new HttpListener();
m_listener.Prefixes.Add(uri);
m_listener.Start();
m_cancellation = new CancellationTokenSource();
Task.Run(() => AcceptWebSocketClientsAsync(m_listener, m_cancellation.Token));
}
public static void Stop()
{
if(m_listener != null && m_cancellation != null)
{
try
{
m_cancellation.Cancel();
m_listener.Stop();
m_listener = null;
m_cancellation = null;
}
catch
{
// Log error
}
}
}
#endregion
}