简体   繁体   English

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

[英]WCF self-hosted WebSocket Service with Javascript client

I have this WCF self-hosted WebSocket service code: 我有这个WCF自托管的WebSocket服务代码:

Main: 主要:

//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();

Interface: 接口:

namespace IWebSocketHostTest
{
    [ServiceContract]
    interface IWebSocketCallBack
    {
        [OperationContract(IsOneWay = true)]
        void Send(int num);
    }

    [ServiceContract(CallbackContract = typeof(IWebSocketCallBack))]
    public interface IWebSocket
    {
        [OperationContract]
        void StartSend();
    }
}

Service: 服务:

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();
    }

}
}

This works perfect if i add a reference of this service in another .NET application. 如果我在另一个.NET应用程序中添加此服务的引用,这非常有用。 But, what i need is to consume this service from an HTML+Javascript application, and i´m realy lost in how to do that. 但是,我需要的是从HTML + Javascript应用程序中使用此服务,我真的迷失了如何做到这一点。 I couldn´t find a good example or tutorial with a Javascript client consuming a self-hosted WCF WebSocket service. 我找不到一个使用自托管WCF WebSocket服务的Javascript客户端的好例子或教程。 All the Javascript WebSocket code that i could find seems to be very simple, but i couldn´t make it work. 我能找到的所有Javascript WebSocket代码似乎都非常简单,但我无法使其工作。

Here is my short JavaScript client test: 这是我的简短JavaScript客户端测试:

var ws = new WebSocket("ws://192.168.1.95:8080/service");
            ws.onopen = function () {
                console.log("WEBSOCKET CONNECTED");
            };

it returns "WebSocket Error: Incorrect HTTP response. Status code 400, Bad Request" testing it with Fiddler. 它返回“WebSocket错误:错误的HTTP响应。状态代码400,错误请求”用Fiddler测试它。

What am i missing? 我错过了什么? Could you please give me some doc links to get more information or a code example? 您能否给我一些文档链接以获取更多信息或代码示例?

Thank you! 谢谢!

EDIT: 编辑:

Now i´ve tried using the "Microsoft.ServiceModel.WebSocket" library to try to make it work. 现在我尝试使用“Microsoft.ServiceModel.WebSocket”库来尝试使其工作。

But, first, i don´t know if it´s still maintained by Microsoft or if it is deprecated, because i couldn´t find any information at MSDN and there is few info at internet. 但是,首先,我不知道它是否仍然由微软维护或是否被弃用,因为我无法在MSDN上找到任何信息,而且互联网上的信息很少。 And second, the "Open()" method of the "WebSocketHost" class is not found, so i don´t know how to make the server run... 第二,找不到“WebSocketHost”类的“Open()”方法,所以我不知道如何让服务器运行...

Here is my code, i´ve taken it from a question at the ASP.NET forum. 这是我的代码,我从ASP.NET论坛的一个问题中得到了它。

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.");
    }
}
}

But, like i said before, the "host.Open()" method is not found, so i don´t know if i´m missing some reference or what, because i couldn´t find info about the WebSocketHost class... Any help? 但是,就像我之前说的那样,找不到“host.Open()”方法,所以我不知道我是否缺少一些参考或什么,因为我找不到关于WebSocketHost类的信息...有帮助吗?

After spending a day with the same task I finally got working solution. 在完成同样的任务一天后,我终于得到了解决方案。 Hope it will help someone in the future. 希望它能帮助将来的某个人。

Client JS script: 客户端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>

Self hosted server: 自托管服务器:

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;
        }
    }
}

UPDATE UPDATE

As of .net 4.5 new way of writing server side have emerged. 截至.net 4.5已经出现了编写服务器端的新方法。 The benefits are cleaner code and possibility to support secure web sockets (WSS) over https. 优点是更清晰的代码以及通过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
}  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM