4

I guess it's already time that I ask others. Is it possible to create a websocket server using C# and server request from HTML5 codes?

I am currently using the System package for websocket. I have a code that I downloaded over the internet and here it is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WebSocketChatServer
{
    using WebSocketServer;

    class ChatServer
    {
        WebSocketServer wss;
        List<User> Users = new List<User>();
        string unknownName = "john doe";


        public ChatServer()
        {

            // wss = new WebSocketServer(8181, "http://localhost:8080", "ws://localhost:8181/chat");

            wss = new WebSocketServer(8080, "http://localhost:8080", "ws://localhost:8080/dotnet/Chats");


            wss.Logger = Console.Out;
            wss.LogLevel = ServerLogLevel.Subtle;
            wss.ClientConnected += new ClientConnectedEventHandler(OnClientConnected);
            wss.Start();
            KeepAlive();




        }

        private void KeepAlive()
        {
            string r = Console.ReadLine();
            while (r != "quit")
            {
                if(r == "users")
                {
                    Console.WriteLine(Users.Count);
                }
                r = Console.ReadLine();
            }
        }



        void OnClientConnected(WebSocketConnection sender, EventArgs e)
        {
            Console.WriteLine("test");
            Users.Add(new User() { Connection = sender });
            sender.Disconnected += new WebSocketDisconnectedEventHandler(OnClientDisconnected);
            sender.DataReceived += new DataReceivedEventHandler(OnClientMessage);

        }

        void OnClientMessage(WebSocketConnection sender, DataReceivedEventArgs e)
        {
            Console.WriteLine(sender);
            User user = Users.Single(a => a.Connection == sender);
            if (e.Data.Contains("/nick"))
            {
                string[] tmpArray = e.Data.Split(new char[] { ' ' });
                if (tmpArray.Length > 1)
                {
                    string myNewName = tmpArray[1];
                    while (Users.Where(a => a.Name == myNewName).Count() != 0)
                    {
                        myNewName += "_";
                    }
                    if (user.Name != null)
                        wss.SendToAll("server: '" + user.Name + "' changed name to '" + myNewName + "'");
                    else
                        sender.Send("you are now know as '" + myNewName + "'");
                    user.Name = myNewName;
                }
            }
            else
            {
                string name = (user.Name == null) ? unknownName : user.Name;
                wss.SendToAllExceptOne(name + ": " + e.Data, sender);
                sender.Send("me: " + e.Data);
            }
        }

        void OnClientDisconnected(WebSocketConnection sender, EventArgs e)
        {
            try
            {

                User user = Users.Single(a => a.Connection == sender);
                string name = (user.Name == null) ? unknownName : user.Name;
                wss.SendToAll("server: "+name + " disconnected");
                Users.Remove(user);
            }
            catch (Exception exc)
            {

                Console.WriteLine("ehm...");
            }

        }
    }
}

And I have this code for client side:

<!HTML>
    <head>

        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
        </script>

        <script language="javascript" type="text/javascript">  

        jQuery(document).ready(function(){
            var socket = new WebSocket("ws://localhost:8080");  
            socket.onopen = function(){  
                alert("Socket has been opened!");  
            }  
        });
        </script>  
    </head>
</HTML>

As I run my C# console app and load the client page, the app tells me that there's someone who connected in the port it is listening to. But on my client side, as I look in firebug's console, it gives me the beginner's classic error:

Firefox can't establish a connection to the server at ws://localhost:8080/

What I would like to achieve is establish first a successful websocket connection and push value to my client coming from my server.

I have considered Alchemy but the version I have is Visual Studio express 2010, the free version, and it says that "solution folders are not supported in this version of application".

Any help will be very much appreciated.

Charmie
  • 2,468
  • 7
  • 33
  • 47

6 Answers6

3

I've been developing a server for a JavaScript/HTML 5 game for about 7 months now have you looked into Alchemy Websockets? its pretty easy to use.

Example:

using Alchemy;
using Alchemy.Classes;
namespace GameServer
{
    static class Program
    {
        public static readonly ConcurrentDictionary<ClientPeer, bool> OnlineUsers = new ConcurrentDictionary<ClientPeer, bool>();
        static void Main(string[] args)
        {
            var aServer = new WebSocketServer(4530, IPAddress.Any)
            {
                OnReceive = context => OnReceive(context),
                OnConnected = context => OnConnect(context),
                OnDisconnect = context => OnDisconnect(context),
                TimeOut = new TimeSpan(0, 10, 0),
                FlashAccessPolicyEnabled = true
            };
        }
        private static void OnConnect(UserContext context)
        {
            var client = new ClientPeer(context);
            OnlineUsers.TryAdd(client, false);
            //Do something with the new client
        }
    }
}

As you can see its pretty easy to work with and I find their documentation very good (note ClientPeer is a custom class of mine just using it as an example).

1

What you are trying to achieve will be far easier if you take a look at ASP.NET SignalR

It has support for high level hubs to implement realtime communication and also has a persistent connection low level class to have a finely grained control over the communication.

Support for multiple client types and fallback if websockets isn't supported at both ends of the communication (it can optionally use long polling or forever frames).

scartag
  • 17,548
  • 3
  • 48
  • 52
  • 1
    but the requirement is to use html5 and c# – Charmie Mar 13 '13 at 07:35
  • Look at http://stackoverflow.com/questions/11140164/signalr-console-app-example - SignalR is independend of ASP.Net and works with WebSockets. – weismat Mar 13 '13 at 07:50
  • 1
    @weismat SignalR is a part of asp.net .. it is not independent. check this link http://www.asp.net/signalr – scartag Mar 13 '13 at 08:03
  • 1
    Look for instance at the BasicChat.SelfHost example - no ASP.Net reference. https://github.com/SignalR/Samples/tree/master/BasicChat.SelfHost – weismat Mar 13 '13 at 08:41
1

The reason for this error is ( probably ) because you are not responding to the handshake. Once the connection is established browser sends some data and the server must respond appropriately ( otherwise browser will close the connection ). You can read more about this on wiki or directly in specification.

Community
  • 1
  • 1
freakish
  • 54,167
  • 9
  • 132
  • 169
0

I modified a code i downloaded online and here's what I got now:

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;




namespace WebSocketServer
{
    public enum ServerLogLevel { Nothing, Subtle, Verbose };
    public delegate void ClientConnectedEventHandler(WebSocketConnection sender, EventArgs e);

    public class WebSocketServer
    {
        #region private members
        private string webSocketOrigin;     // location for the protocol handshake
        private string webSocketLocation;   // location for the protocol handshake
        #endregion
        static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
        static IPEndPoint ipLocal;

        public event ClientConnectedEventHandler ClientConnected;

        /// <summary>
        /// TextWriter used for logging
        /// </summary>
        public TextWriter Logger { get; set; }     // stream used for logging

        /// <summary>
        /// How much information do you want, the server to post to the stream
        /// </summary>
        public ServerLogLevel LogLevel = ServerLogLevel.Subtle;

        /// <summary>
        /// Gets the connections of the server
        /// </summary>
        public List<WebSocketConnection> Connections { get; private set; }

        /// <summary>
        /// Gets the listener socket. This socket is used to listen for new client connections
        /// </summary>
        public Socket ListenerSocker { get; private set; }

        /// <summary>
        /// Get the port of the server
        /// </summary>
        public int Port { get; private set; }


        public WebSocketServer(int port, string origin, string location)
        {
            Port = port;
            Connections = new List<WebSocketConnection>();
            webSocketOrigin = origin;
            webSocketLocation = location;
        }

        /// <summary>
        /// Starts the server - making it listen for connections
        /// </summary>
        public void Start()
        {
            // create the main server socket, bind it to the local ip address and start listening for clients
            ListenerSocker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            ipLocal = new IPEndPoint(IPAddress.Loopback, Port);
            ListenerSocker.Bind(ipLocal);
            ListenerSocker.Listen(100);



            LogLine(DateTime.Now + "> server stated on " + ListenerSocker.LocalEndPoint, ServerLogLevel.Subtle);

            ListenForClients();
        }

        // look for connecting clients
        private void ListenForClients()
        {
            ListenerSocker.BeginAccept(new AsyncCallback(OnClientConnect), null);
        }

        private void OnClientConnect(IAsyncResult asyn)
        {

            byte[] buffer = new byte[1024];
            string headerResponse = "";

            // create a new socket for the connection
            var clientSocket = ListenerSocker.EndAccept(asyn);
            var i = clientSocket.Receive(buffer);
            headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
            //Console.WriteLine(headerResponse);


            if (clientSocket != null)
            {

                // Console.WriteLine("HEADER RESPONSE:"+headerResponse);
                var key = headerResponse.Replace("ey:", "`")
                              .Split('`')[1]                     // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
                              .Replace("\r", "").Split('\n')[0]  // dGhlIHNhbXBsZSBub25jZQ==
                              .Trim();
                var test1 = AcceptKey(ref key);
                var newLine = "\r\n";
                var name = "Charmaine";
                var response = "HTTP/1.1 101 Switching Protocols" + newLine
                         + "Upgrade: websocket" + newLine
                         + "Connection: Upgrade" + newLine
                         + "Sec-WebSocket-Accept: " + test1 + newLine + newLine
                         + "Testing lang naman po:" + name
                         ;

                // which one should I use? none of them fires the onopen method
                clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
            }





            // keep track of the new guy
            var clientConnection = new WebSocketConnection(clientSocket);
            Connections.Add(clientConnection);
            // clientConnection.Disconnected += new WebSocketDisconnectedEventHandler(ClientDisconnected);
            Console.WriteLine("New user: " + ipLocal);
            // invoke the connection event
            if (ClientConnected != null)
                ClientConnected(clientConnection, EventArgs.Empty);

            if (LogLevel != ServerLogLevel.Nothing)
                clientConnection.DataReceived += new DataReceivedEventHandler(DataReceivedFromClient);



            // listen for more clients
            ListenForClients();

        }

        void ClientDisconnected(WebSocketConnection sender, EventArgs e)
        {
            Connections.Remove(sender);
            LogLine(DateTime.Now + "> " + sender.ConnectionSocket.LocalEndPoint + " disconnected", ServerLogLevel.Subtle);
        }

        void DataReceivedFromClient(WebSocketConnection sender, DataReceivedEventArgs e)
        {
            Log(DateTime.Now + "> data from " + sender.ConnectionSocket.LocalEndPoint, ServerLogLevel.Subtle);
            Log(": " + e.Data + "\n" + e.Size + " bytes", ServerLogLevel.Verbose);
            LogLine("", ServerLogLevel.Subtle);
        }


        /// <summary>
        /// send a string to all the clients (you spammer!)
        /// </summary>
        /// <param name="data">the string to send</param>
        public void SendToAll(string data)
        {
            Connections.ForEach(a => a.Send(data));
        }

        /// <summary>
        /// send a string to all the clients except one
        /// </summary>
        /// <param name="data">the string to send</param>
        /// <param name="indifferent">the client that doesn't care</param>
        public void SendToAllExceptOne(string data, WebSocketConnection indifferent)
        {
            foreach (var client in Connections)
            {
                if (client != indifferent)
                    client.Send(data);
            }
        }

        /// <summary>
        /// Takes care of the initial handshaking between the the client and the server
        /// </summary>


        private void Log(string str, ServerLogLevel level)
        {
            if (Logger != null && (int)LogLevel >= (int)level)
            {
                Logger.Write(str);
            }
        }

        private void LogLine(string str, ServerLogLevel level)
        {
            Log(str + "\r\n", level);
        }

        private static string AcceptKey(ref string key)
        {
            string longKey = key + guid;
            byte[] hashBytes = ComputeHash(longKey);
            return Convert.ToBase64String(hashBytes);
        }
        static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        private static byte[] ComputeHash(string str)
        {
            return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
        }



        private void ShakeHands(Socket conn)
        {
            using (var stream = new NetworkStream(conn))
            using (var reader = new StreamReader(stream))
            using (var writer = new StreamWriter(stream))
            {
                //read handshake from client (no need to actually read it, we know its there):
                LogLine("Reading client handshake:", ServerLogLevel.Verbose);
                string r = null;
                while (r != "")
                {
                    r = reader.ReadLine();
                    LogLine(r, ServerLogLevel.Verbose);
                }

                // send handshake to the client
                writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
                writer.WriteLine("Upgrade: WebSocket");
                writer.WriteLine("Connection: Upgrade");
                writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
                writer.WriteLine("WebSocket-Location: " + webSocketLocation);
                writer.WriteLine("");
            }


            // tell the nerds whats going on
            LogLine("Sending handshake:", ServerLogLevel.Verbose);
            LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
            LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
            LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
            LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
            LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
            LogLine("", ServerLogLevel.Verbose);

            LogLine("Started listening to client", ServerLogLevel.Verbose);
            //conn.Listen();
        }


    }
}

Connection issue resolved, next would be SENDING DATA to client.

Charmie
  • 2,468
  • 7
  • 33
  • 47
  • Has you solved sending data issue? I downloaded same sample and got exactly same issues >_< I fixed connection issue by myself but it came out it has even sending data issues. Meh, who in this world ever called this piece of junkcode by "websockets server example"? – Kosmo零 Feb 02 '15 at 13:55
0

just change shakehands in WebSocketServer.cs file in solution with below code and your error will gone..

private void ShakeHands(Socket conn)
    {
        using (var stream = new NetworkStream(conn))
        using (var reader = new StreamReader(stream))
        using (var writer = new StreamWriter(stream))
        {
            //read handshake from client (no need to actually read it, we know its there):
            LogLine("Reading client handshake:", ServerLogLevel.Verbose);
            string r = null;
            Dictionary<string, string> headers = new Dictionary<string, string>();
            while (r != "")
            {
                r = reader.ReadLine();
                string[] tokens = r.Split(new char[] { ':' }, 2);
                if (!string.IsNullOrWhiteSpace(r) && tokens.Length > 1)
                {
                    headers[tokens[0]] = tokens[1].Trim();
                }
                LogLine(r, ServerLogLevel.Verbose);
            }


            //string line = string.Empty;
            //while ((line = reader.ReadLine()) != string.Empty)
            //{
            //    string[] tokens = line.Split(new char[] { ':' }, 2);
            //    if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1)
            //    {
            //        headers[tokens[0]] = tokens[1].Trim();
            //    }
            //}

            string responseKey = "";
            string key = string.Concat(headers["Sec-WebSocket-Key"], "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
            using (SHA1 sha1 = SHA1.Create())
            {
                byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
                responseKey = Convert.ToBase64String(hash);
            }



            // send handshake to the client
            writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
            writer.WriteLine("Upgrade: WebSocket");
            writer.WriteLine("Connection: Upgrade");
            writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
            writer.WriteLine("WebSocket-Location: " + webSocketLocation);
            //writer.WriteLine("Sec-WebSocket-Protocol: chat");
            writer.WriteLine("Sec-WebSocket-Accept: " + responseKey);
            writer.WriteLine("");
        }


        // tell the nerds whats going on
        LogLine("Sending handshake:", ServerLogLevel.Verbose);
        LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
        LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
        LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
        LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
        LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
        LogLine("", ServerLogLevel.Verbose);

        LogLine("Started listening to client", ServerLogLevel.Verbose);
        //conn.Listen();
    }
Robert
  • 5,278
  • 43
  • 65
  • 115
amitP
  • 1
  • 1
0

You may also take a look at the WebSocketRPC library which should be pretty simple to use, for both the "raw" connection and the RPC connections (the messages can also be mixed).


The following can be useful to you:
The code responsible for sending/receiving raw messages is located in the Connection class. In the repository you can also find how a base JavaScript client is implemented.

Disclaimer: I am the author.

dajuric
  • 2,373
  • 2
  • 20
  • 43