Question

I am new to the concept of dictionaries. I have found a way to create a simple server that is able to handle multiple clients. Is there a way that I could use a dictionary/ConcurrentDictionary to hold information such as the time the connection was established and the port it is using for the various clients. I have created a separate sample dictionary but I don't know how to make it work with the server application or if it will even work. (The server is created using localhost and port 9090. The clients are also run locally). Let me know if you need any clarification. How/Where would i place the dictionary code block to allow it to hold established connection information or is there a different/better way of doing it?

Dictionary<int, DateTime> ServerDictionary = new Dictionary<int, DateTime>();

        ServerDictionary.Add(9090, DateTime.Now.AddMinutes(5));
            Console.WriteLine("Time List");

            foreach (KeyValuePair<int, DateTime> time in ServerDictionary)
            {
                Console.WriteLine("Port = {0}, Time in 5 = {1}",
                    time.Key, time.Value);
            }
        Console.ReadKey();

Below is the Server code:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Multi_Server
{
    class Program
    {
        private static Socket _serverSocket;
        private static readonly List<Socket> _clientSockets = new List<Socket>();
        private const int _BUFFER_SIZE = 2048;
        private const int _PORT = 9090;
        private static readonly byte[] _buffer = new byte[_BUFFER_SIZE];

        static void Main()
        {
            Console.Title = "Server";
            SetupServer();
            Console.ReadLine(); // When we press enter close everything
            CloseAllSockets();
        }

        private static void SetupServer()
        {
            Console.WriteLine("Setting up server...");
            _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _serverSocket.Bind(new IPEndPoint(IPAddress.Any, _PORT));
            _serverSocket.Listen(5);
            _serverSocket.BeginAccept(AcceptCallback, null);
            Console.WriteLine("Server setup complete");
        }

        // Close all connected client 
        // (we do not need to shutdown the server socket 
        // as its connections are already closed with the clients)
        private static void CloseAllSockets()
        {
            foreach (Socket socket in _clientSockets)
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }

            _serverSocket.Close();
        }

        private static void AcceptCallback(IAsyncResult AR)
        {
            Socket socket;

            try
            {
                socket = _serverSocket.EndAccept(AR);
            }
            catch (ObjectDisposedException) // I cannot seem to avoid this (on exit when properly closing sockets)
            {
                return;
            }

            _clientSockets.Add(socket);
            socket.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
            Console.WriteLine("Client connected, waiting for request...");
            _serverSocket.BeginAccept(AcceptCallback, null);
        }

        private static void ReceiveCallback(IAsyncResult AR)
        {
            Socket current = (Socket)AR.AsyncState;
            int received;

            try
            {
                received = current.EndReceive(AR);
            }
            catch (SocketException)
            {
                Console.WriteLine("Client forcefully disconnected");
                current.Close(); // Dont shutdown because the socket may be disposed and its disconnected anyway
                _clientSockets.Remove(current);
                return;
            }

            byte[] recBuf = new byte[received];
            Array.Copy(_buffer, recBuf, received);
            string text = Encoding.ASCII.GetString(recBuf);
            Console.WriteLine("Received Text: " + text);

            if (text.ToLower() == "get time") // Client requested time
            {
                Console.WriteLine("Text is a 'get time' request");
                byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
                current.Send(data);
                Console.WriteLine("Time sent to client");
            }
            else if (text.ToLower() == "exit") // Client wants to exit gracefully
            {
                // Always Shutdown before closing
                current.Shutdown(SocketShutdown.Both);
                current.Close();
                _clientSockets.Remove(current);
                Console.WriteLine("Client disconnected");
                return;
            }
            else
            {
                //Console.WriteLine("Text is an invalid request");
                byte[] data = Encoding.ASCII.GetBytes("Invalid function request");
                current.Send(data);
                //Console.WriteLine("Warning Sent");
            }

            current.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
        }
    }
}
Was it helpful?

Solution

Since multiple threads can access your dictionary at the same time, you either need to manually serialize access to it (with a lock statement) or user a ConcurrentDictionary. The latter will give better performance, in general. You could declare the dictionary to be a member of your Program class and insert into it from the AcceptCallback function.

Your _clientSockets List is also shared between threads and so it too needs some synchronization. Perhaps you can get rid of it altogether once your dictionary is working.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top