Romain Lambert
Romain Lambert

Reputation: 56

Socket Programming c# / Client-Server communication

I am trying to make 2 programs (in console application) in .NET that communicate together through the web (the code is from a video that you can find below, I am just trying to make the code work before adapting it). Forgive me, I'm not an experienced programmer.

I have a server and a client, they work perfectly when I run both of them on my computer (locally). I can connect multiple clients to the server, send requests and get a response. I am facing the problem when i run the server on a computer and the client on an other one and I try to connect through the internet.

I can connect to the server but communication does not work, there is no data exchange when try to request time as an example. I think that the problem is mostly coming from the network itself than the code.

Here's the code for the 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 = 50114;
    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()
    {
      //  IPAddress addip = GetBroadcastAddress();
        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");
    }

    /// <summary>
    /// Close all connected client (we do not need to shutdown the server socket as its connections
    /// are already closed with the clients)
    /// </summary>
    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 request");
            current.Send(data);
            Console.WriteLine("Warning Sent");
        }

        current.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
    }
}

And here the client code:

class Program
{
    private static readonly Socket _clientSocket = new Socket
        (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    private const int _PORT = 50114;

    static void Main()
    {
        Console.Title = "Client";
        ConnectToServer();
        RequestLoop();
        Exit();
    }

    private static void ConnectToServer()
    {
        int attempts = 0;

        while (!_clientSocket.Connected)
        {
            try
            {
                attempts++;
                Console.WriteLine("Connection attempt " + attempts);
                _clientSocket.Connect("IpAddr", _PORT);
            }
            catch (SocketException) 
            {
                Console.Clear();
            }
        }

        Console.Clear();
        Console.WriteLine("Connected");
    }

    private static void RequestLoop()
    {
        Console.WriteLine(@"<Type ""exit"" to properly disconnect client>");

        while (true)
        {
            SendRequest();
            ReceiveResponse();
        }
    }

    /// <summary>
    /// Close socket and exit app
    /// </summary>
    private static void Exit()
    {
        SendString("exit"); // Tell the server we re exiting
        _clientSocket.Shutdown(SocketShutdown.Both);
        _clientSocket.Close();
        Environment.Exit(0);
    }

    private static void SendRequest()
    {
        Console.Write("Send a request: ");
        string request = Console.ReadLine();
        SendString(request);

        if (request.ToLower() == "exit")
        {
            Exit();
        }
    }

    /// <summary>
    /// Sends a string to the server with ASCII encoding
    /// </summary>
    private static void SendString(string text)
    {
        byte[] buffer = Encoding.ASCII.GetBytes(text);
        _clientSocket.Send(buffer, 0, buffer.Length, SocketFlags.None);
    }

    private static void ReceiveResponse()
    {
        var buffer = new byte[2048];
        int received = _clientSocket.Receive(buffer, SocketFlags.None);
        if (received == 0) return;
        var data = new byte[received];
        Array.Copy(buffer, data, received);
        string text = Encoding.ASCII.GetString(data);
        Console.WriteLine(text);
    }
}
 static void Main()
    {
        Console.Title = "Client";
        ConnectToServer();
        RequestLoop();
        Exit();
    }

    private static void ConnectToServer()
    {
        int attempts = 0;

        while (!_clientSocket.Connected)
        {
            try
            {
                attempts++;
                Console.WriteLine("Connection attempt " + attempts);
                _clientSocket.Connect("IpAddr", _PORT);
            }
            catch (SocketException) 
            {
                Console.Clear();
            }
        }

        Console.Clear();
        Console.WriteLine("Connected");
    }

"IpAddr" is a placeholder for the public IP of the router.

This is the video my current code is based on: https://www.youtube.com/watch?v=xgLRe7QV6QI

I took the code directly from it (you can find the 2 code files on the site linked in description).

I run the server, it awaits for a connection. I then run the clients which tries to connect to the server. I connects and on the server it shows the successful connection. The client then sends a request like "get time" and the server is supposed to catch it and respond:

 byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
 current.Send(data);

Back to network side:

Here is a list of all the things I have tried, that were the usual main causes of the problem after looking for a solution for about a full day:

Thanks for you help.

Upvotes: 1

Views: 2921

Answers (1)

Romain Lambert
Romain Lambert

Reputation: 56

I learned about message framing, Thank you CodeCaster.

For people who are curious about it, here is an interesting link i found about it: http://blog.stephencleary.com/2009/04/message-framing.html

But before trying to change the code, I ran the server on an AWS vps and it worked instantly, the problem was probably comming from my isp or my router. I am just wondering how can it work without handling message framing.

Upvotes: 1

Related Questions