使用Javascript客户端的WCF自托管WebSocket服务

我有这个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  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 callbackClientList = null; public WebSocketService() { callbackClientList = new List(); timer = new Timer(3000); timer.Elapsed += new ElapsedEventHandler(sendNumber); timer.Start(); } public void StartSend() { sender.addClient(OperationContext.Current.GetCallbackChannel()); } 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论坛的一个问题中得到了它。

 using System; using Microsoft.ServiceModel.WebSockets; namespace WebSocketTest { class Program { static void Main(string[] args) { var host = new WebSocketHost(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类的信息……有帮助吗?

在完成同样的任务一天后,我终于得到了解决方案。 希望它能帮助将来的某个人。

客户端JS脚本:

    WebSocket Chat      

(display)

自托管服务器:

 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  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(); if(msg.IsEmpty || ((IChannel)callback).State != CommunicationState.Opened) { return; } byte[] body = msg.GetBody(); 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(Encoding.UTF8.GetBytes(msgText))); msg.Properties["WebSocketMessageProperty"] = new WebSocketMessageProperty { MessageType = WebSocketMessageType.Text }; return msg; } } } 

UPDATE

从.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(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 ReadString(WebSocket ws) { ArraySegment buffer = new ArraySegment(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 }