WCF self-hosted WebSocket Service with Javascript client

后端 未结 1 1833
迷失自我
迷失自我 2020-12-23 23:17

I have this WCF self-hosted WebSocket service code:

Main:

//Create a URI to serve as the base address
Uri httpUrl = new Uri(\"http:/         


        
相关标签:
1条回答
  • 2020-12-23 23:50

    After spending a day with the same task I finally got working solution. Hope it will help someone in the future.

    Client JS script:

    <!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

    As of .net 4.5 new way of writing server side have emerged. The benefits are cleaner code and possibility to support secure web sockets (WSS) over https.

    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
    }  
    
    0 讨论(0)
提交回复
热议问题