Alexandru
Alexandru

Reputation: 12902

TcpClient not connecting to remote server

I'm trying to connect to a remote machine using the TcpClient class, but it keeps failing:

An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll

Additional information: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

Testing the code when the client and server are local works, but when I try connecting to a remote machine, it no longer works.

Here is the server code:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebSocketServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting a new WebSockets server.");
            WebSocketServer server = new WebSocketServer();
            Console.WriteLine("The WebSocket server has started.");
            bool userRequestedShutdown = false;
            while (!userRequestedShutdown)
            {
                Console.ReadLine();
                DialogResult result = MessageBox.Show("Do you want to shut the server down?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == DialogResult.Yes)
                {
                    userRequestedShutdown = true;
                }
            }
            server.Stop();
        }

        class WebSocketServer
        {

            TcpListener server;
            Thread connectionListener;
            ConcurrentDictionary<TcpClient, Thread> clients = new ConcurrentDictionary<TcpClient, Thread>();

            public WebSocketServer()
            {
                server = new TcpListener(IPAddress.Parse("127.0.0.1"), (int)Properties.Settings.Default["Port"]);
                try
                {
                    server.Start();
                }
                catch (Exception exception)
                {
                    Console.WriteLine("Error while trying to start the server: {0}", exception.ToString());
                }
                connectionListener = new Thread(() =>
                {
                    while (true)
                    {
                        Console.WriteLine("Waiting for a new client.");
                        try
                        {
                            TcpClient client = server.AcceptTcpClient();
                            Thread clientListener = new Thread(() =>
                            {
                                try
                                {
                                    NetworkStream stream = client.GetStream();
                                    byte[] buffer = new byte[1024];
                                    Console.WriteLine("Wating for the client to write.");
                                    while (client.Connected)
                                    {
                                        try
                                        {
                                            int bytesRead = stream.Read(buffer, 0, buffer.Length);
                                            Console.WriteLine("Read {0} bytes from the client.", bytesRead);
                                            string data = Encoding.UTF8.GetString(buffer).Substring(0, bytesRead);
                                            Console.WriteLine("Read the following string from the client: {0}", data);
                                        }
                                        catch (Exception exception)
                                        {
                                            Console.WriteLine("Error while trying to read from a TCP client: {0}", exception.ToString());
                                            break;
                                        }
                                    }
                                    Console.WriteLine("Client disconnected. Removing client.");
                                }
                                catch (Exception exception)
                                {
                                    Console.WriteLine("Error while trying to connect to a TCP client: {0}", exception.ToString());
                                }
                                client.Close();
                                clients.TryRemove(client, out clientListener);
                            });
                            clientListener.Start();
                            clients.TryAdd(client, clientListener);
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine("Error while trying to accept a TCP client: {0}", exception.ToString());
                        }
                        Console.WriteLine("A client has connected.");
                    }
                });
                connectionListener.Start();
            }

            public void Stop()
            {
                server.Stop();
                connectionListener.Abort();
            }
        }
    }
}

Here is the client code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WebSocketClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Opening up a TcpClient.");
            TcpClient client = new TcpClient();
            client.Connect("<Remote Hostname>", <RemotePortNumber>);
            Console.WriteLine("TcpClient has connected.");
            NetworkStream stream = client.GetStream();
            bool closed = false;
            new Thread(() =>
            {
                while (!closed)
                {
                    Console.WriteLine("Writing data to the stream.");
                    byte[] bytes = Encoding.UTF8.GetBytes("Hello, world.");
                    stream.Write(bytes, 0, bytes.Length);
                    Thread.Sleep(1000);
                }
            }).Start();
            Console.ReadLine();
            closed = true;
        }
    }
}

So what is the problem here? I am hosting the server on an Azure Virtual Machine, I have opened up the TCP port I am trying to use as the <RemotePortNumber> in Windows Firewall on my remote server by setting both inbound and outbound rules to allow all traffic in and out of the machine on that port, and I have created a TCP endpoint on my Azure portal that maps the external port of my Virtual Machine's hostname to the internal, private port, of my Virtual Machine, both set to map the same port number of <RemotePortNumber> for consistency. To connect, I am using a <Remote Hostname> value of <MyServiceName>.cloudapp.net. I have also tried connecting the stream by using IPAddress.Parse(<Public IP of my Azure Server>) but have had no luck...it just keeps timing out, as if I am not formatting the hostname correctly or something. What am I missing? If anyone can provide some clues as to how to debug the issue, that would also be very helpful.

Update: Running a WireShark trace, I see a lot of these messages (is this bad? I think the TCP Retransmission might be okay if we take into account that for Azure you have to route packets from the public domain's port to the private port of the VM, but not sure about whatever the RST, ACK's are):

enter image description here

Update: Running Microsoft Message Analyzer, I see these messages on the Local Link Layer:

enter image description here

Note: My VM has an Internal IP of 100.75.20.78 and a Public IP of 191.238.37.130. It has the public domain name of ovidius.cloudapp.net. I am trying to host the application on TCP port 6490. I blacked out my personal IP address for the sake of not giving it up.

I have mapped the TCP port in the Azure portal from domain to VM as follows:

enter image description here

Upvotes: 1

Views: 5294

Answers (1)

Alexandru
Alexandru

Reputation: 12902

After spending two mother _______ days on this (fill in the blanks), I got a little bit creative in ways of exploring alternative methods to try and see if I could rule routing issues out. I can pretty much safely conclude that Microsoft's Virtual Machine routing fails when you map a Public Port to the same Private Port.

For example, I tried setting up the new Socket endpoint below, and it worked because it didn't map the same domain port to the same Virtual Machine port like I had previously done with the WebSocketServer:

enter image description here

Update: Also, when hosting, I had to set up the server, not on the IP of 127.0.0.1, but the Internal IP, which in my case is 100.75.20.78.

Update Again: Contrary to the above solution, I tried to delete the old endpoint at 6490 and recreated it, and it seems to be working when I connect to that address now. I'm not entirely sure why, I can only say that the only difference here is that I had firewall rules on to allow that endpoint's port before creating the endpoint this time...not sure if that would make a difference. I'm honestly not sure what was causing the issues.

Update Yet Again: Just thought about it some more...I think it comes down to the following two issues:

  1. You need to host the server on the Internal IP of your Azure Virtual Machine, not localhost or 127.0.0.1 like I was doing.
  2. You need to not have the "ENABLE DIRECT SERVER RETURN" feature enabled on your Azure endpoint.

Upvotes: 3

Related Questions