Click here to Skip to main content
15,897,704 members
Articles / Programming Languages / C#

C# WebServer Using Sockets

Rate me:
Please Sign up or sign in to vote.
5.00/5 (27 votes)
23 Jan 2014CPOL7 min read 103.9K   9.9K   161  
How to make a simple web server which supports GZIP compression, applications, and sessions.
using System;
using System.Text;
using System.Collections.Concurrent;
using BizApplication;
using BizApplication.http;
using Newtonsoft.Json;
using ServerCommonLibrary;
using BizApplication.Http;


namespace Chat.HttpApplication
{

    /// <summary>
    /// Define the chatServer application settings 
    /// </summary>
    public class ChatServerConfiguration : ApplicationSettings
    {
        public ChatServerConfiguration()
        {
            this.SessionType = ApplicationSessionMode.BrowserSession;
            this.UniqueApplicationName = "chat";
            ///
            ///  Share is a way to comunicate with other istances
            ///
            this.ResponseMode = ApplicationResponseBehavior.ShareAndSend;
            this.InactivityTimeToLive = 10;
        }
    }

    /// <summary>
    /// ChatServer Application share a simple text message in json format to all connected clients (users).
    ///     One session of ChatServer identifies one logged user. The chat messages broadcasting 
    ///     is possible using the ApplicationResponseBehavior in 'Share' mode that's mean every response sent to the browser is 
    ///     delivered to any ChatServer sesssion (see OnNewShareResponse(...)).  
    ///     
    ///     To enter in the chat room is require a foo login and password.
    /// 
    /// </summary>
    public class ChatServer : HttpApplicationBase, IDisposable
    {
        
        #region COMUNICATION DATA
        /// <summary>
        /// In this region are define the structure of the comunication messages 
        /// </summary>

       
        public enum MessageType : int { alert = 0, eval = 1, chatmessage = 2,adduser = 3,skip=4};

        /// <summary>
        /// Chat Message is used to comunicate with the browser.        
        ///     - MessageCode property define the type of the message, see  MessageType value.
        ///     - Value property change the purpose depending on the messagecode.
        ///         MessageCode alert   ->  the value is  the text to show
        ///         MessageCode eval  ->  the Value is js script to eval
        ///         MessageCode chatmessage ->  the Value is  the content of the message 
        ///         MessageCode Adduser ->  the Value is empty
        ///     - user property get the user involve in the message, result empty if the messageType is alert or eval.
        /// </summary>
        public class ChatMessage
        {
            public int MessageCode { get; set; }
            public string Value { get; set; }
            public string User { get; set; }
        }

        /// <summary>
        /// SharedChatMessage is designed to comunicate with others chatServer session.        
        /// </summary>
        public class SharedChatMessage : HttpResponse
        {
            public SharedChatMessage(byte[] response, ApplicationRequest request, ChatMessage message)
                : base(response, request)
            {
                this.Message = message;
            }

            public ChatMessage Message { get; set; }
        }

        #endregion 

        
        public static string loginPage = "index.html";
        public static string roomPage = "room2.html";

        //### User Nickname
        string currentUsername;
        //### User Password
        string currentPassowrd;
        //### IsValid User
        bool isValidUser;
        //### Send User
        bool sendAdduser;
        //### chat message queue
        ConcurrentQueue<ChatMessage> localqueuemessages;

        /// <summary>
        /// Chat application main directory
        /// </summary>
        /// <returns></returns>
        public override string ApplicationDirectory()
        {
            return Demos.Properties.Settings.Default.ChatServerRootDirectory;            
        }


        public ChatServer()
        {
            currentUsername = "";
            currentPassowrd = "";
            isValidUser = false;
            sendAdduser = false;
            localqueuemessages = new ConcurrentQueue<ChatMessage>();
        }

        /// <summary>
        /// Page load event.                        
        /// </summary>
        /// <param name="req">
        /// The request from the browser.
        /// </param>
        protected override void PageLoad(HttpRequest req)
        {

            ///           
            /// The ChatServer application is strictly related with the javascript implementation, 
            /// so if the request contains query url string parameters means that invoking by the XMLHttpRequest object (ajax pattern).
            ///   You can choose a different way to distinguish 'pure' pageload event (called in Asp.net postback) from ajax event.                       
            /// 
            ///  The query url request has this pattern: ?op=<operation>&<par1>=<value1>&<par2>=<value2> where op stands for 'operation'.                        
            ///
            string page = Request.Paths[Request.Paths.Count - 1];

            if (req.UrlParameters.Count > 0)
            {
                SharedChatMessage sharedresponse = null;
                ChatMessage message = null;
                string operation = req.GetQueryStringValue("op");
                switch (operation)
                {
                    case "login":            
                        ///
                        /// Login operation process steps:                        
                        ///         1) Check username and password , if one of these is empty we respond with an alert
                        ///         2) Validation of the user 
                        ///         3) Respond with a redirect 
                        ///
                        string username = req.GetQueryStringValue("username");
                        string password = req.GetQueryStringValue("password");
                        if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
                        {
                            BuildChatResponse(new ChatMessage() { MessageCode = (int)MessageType.alert, Value = "Login request error." }, true);
                            return;
                        }
                        currentUsername = username;
                        currentPassowrd = password;
                        isValidUser = true;
                        BuildChatResponse(new ChatMessage() { MessageCode = (int)MessageType.eval, Value = "window.location=\"" + roomPage + "\"" }, true);
                        return;
                    case "listen":
                        ///
                        ///  When the room page is loaded start to send 'listen' operation request in loop,
                        ///  at every listen request we respond with a chatmessage getting from the queue or,if is empty ,with a skip action message.
                        ///    But firstable we sent adduser action message with SharedChatMessage envelop for notify the new user.                        
                        ///
                        if (!sendAdduser)
                        {
                            sendAdduser = true;
                            message = new ChatMessage() { MessageCode = (int)MessageType.adduser, User = currentUsername, };
                            BuildChatResponse(message, false);
                            this.response = sharedresponse = new SharedChatMessage(Response.ResponseData, Response.AppRequest, message);
                            return;
                        }
                        if (localqueuemessages.Count == 0)
                        {                            
                            System.Threading.Thread.Sleep(500);
                            BuildChatResponse(new ChatMessage() { MessageCode = (int)MessageType.skip, Value = "" }, false);
                        }
                        else
                        {
                            ChatMessage msg = null;
                            if (localqueuemessages.TryDequeue(out msg))
                                BuildChatResponse(msg, false);
                        }
                        return;
                    case "message":
                        ///
                        ///   A chat message is sent by the user, 
                        ///     firstable we build ChatMessage packet replacing the response with SharedChatMessage envelop, 
                        ///     that is why SharedChatMessage is visible to the other session (see OnNewShareResponse).
                        ///                             
                        ///

                        string value = req.GetQueryStringValue("value");
                        message = new ChatMessage() { MessageCode = (int)MessageType.chatmessage, Value = value, User = currentUsername };
                        BuildChatResponse(message, false);
                        sharedresponse = new SharedChatMessage(Response.ResponseData, Response.AppRequest, message);
                        Response = sharedresponse;
                        return;
                    
                    default:
                        throw new InvalidOperationException("Invalid request");
                }
            }            
            if (page == roomPage)
            {
                /// 
                /// if the user not perform the login the roomPage will not be loaded, will be sent a login page                
                ///                
                if (!isValidUser)
                {
                    BuildResponseFile(ApplicationDirectory() + "\\" + loginPage,MimeType.text_html);
                    return;
                }
                else
                {
                    byte[] room = FSHelper.GetFile(ApplicationDirectory() + "\\" + roomPage);
                    string msg = new string(Encoding.UTF8.GetChars(room));
                    msg = msg.Replace("<%ws_username%>", this.currentUsername);
                    BuildResponse(msg);
                }
            }
        }

        /// <summary>
        /// An Application instace share a response.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="response"></param>
        /// <param name="req"></param>
        public override void OnNewShareResponse(ApplicationInstanceBase sender, ApplicationResponse response, ApplicationRequest req)
        {
            if (response is SharedChatMessage)
            {
                ///
                /// if the response type is SharedChatMessage we simple get the chat message and put it into the queue 
                ///
                SharedChatMessage sharedresponse = (SharedChatMessage)response;
                localqueuemessages.Enqueue(sharedresponse.Message);
            }
        }


        public HttpResponse BuildChatResponse(ChatMessage msg, bool drop_connection)
        {
            BuildResponse(JsonConvert.SerializeObject(msg), drop_connection);
            return (HttpResponse)this.response;
        }



        public override ApplicationSettings Info
        {
            get
            {
                return new ChatServerConfiguration();
            }
        }

        public void Dispose()
        {
            localqueuemessages = null;
        }
    }



}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
Alberto Biafelli,
Software Developer

Comments and Discussions