C# - Port Mapping with .Net Core 5.0 - c#

Expected Outcome
I'm attempting to make an ASP.NET Core 5.0 application using Kestrel, and I would like to auto port forward the server's port.
Package
I'm currently using the OpenNat.Core package. I have also tried the regular OpenNat and the same issue arises.
Issue
When port forwarding, the program says it has mapped everything correctly and the map even shows when listing all mapped ports on my router. Yet, when I attempt to view its status via CanYouSeeMe.org it returns a timed out error, and I am unable to access the server outside the network.
What I've Tried
I thought that the port mapping might have been opening after the server started, so I manually opened the port and then restarted the Kestrel server.
I made sure that my router supported UPnP
I also have a Synology NAS that I port forward from, and it works just fine.
I had a friend use ZenMap to check the port.
The port shows that it's filtered but not open (and no service was specified).
Code
using Open.Nat;
using System;
using System.Threading;
using System.Threading.Tasks;
...
public static async Task OpenPort(int port, string description)
{
try
{
NatDiscoverer discoverer = new NatDiscoverer();
CancellationTokenSource cts = new CancellationTokenSource(10000);
NatDevice device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts);
Mapping map = new(Protocol.Tcp, port, port, description);
await device.CreatePortMapAsync(map);
Console.WriteLine($"Created {map}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
}

Nevermind my firewall was blocking the application. I was able to dynamically add a firewall rule to fix the issue.

Related

Does TCP Socket Implementation Work Differently in Xamarin.Android?

I am trying to make a TCP connection in my Xamarin Android app using System.Net.Sockets.TcpClient.
The connection is perfectly fine when I do it on a console application. However, when I used the exact same code in my android app, I get an exception on tcpClient.Connect("127.0.0.1", 6121); saying "System.Net.Sockets.SocketException (0x80004005): Connection refused". Is there something I need to do differently since it's Xamarin Android? Below is my code. Thank you for the help!
using Android.App;
using Android.OS;
using System;
using System.Net.Sockets;
namespace App1
{
[Activity(Label = "App1", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
Connect();
}
public void Connect()
{
try
{
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("127.0.0.1", 6121);
}
catch(Exception){}
}
}
}
Regardless of whether or not you can do network operations on the main thread (as commented by #ÖmerBaş,) you have a much more basic problem with network concepts causing your error.
127.0.0.1 always refers to the machine the program is running on.
When you run your code in the console, 127.0.0.1 refers to the PC you are working on and presumably where the TCP server is running.
When you run the code on Android, 127.0.0.1 refers to the Android device itself. Your error message says "Connection refused." That's because your TCP-Server isn't running on the Android device. It is running on your PC.
You need to know what IP address your Android device can use to connect to your PC.
If you are using a real Android device, you will need to have your Android device connect to the same network as your PC via WiFi. Then you can use your PC's IP address in your code.
For the simulators, there's a couple of possibilities:
Google simulators: 10.0.2.2 is the address of your PC as seen from the Android.
Microsoft simulators: 169.254.80.80 is the address of your PC as seen from the Android.
(Simulator addresses take from here.)
Make sure to bind your TCP service to all IP addresses, not just 127.0.0.1.
An alternative solution is to use adb to set up port forwarding on your Android device (whether simulated or real.)
I think the "network access on the main thread" thing is more a problem of "should not" rather than "cannot." The question referred to by Ömer Baş shows "permission denied" errors rather than something pointing to a threading problem.

Android client cannot connect to C# MQTT Server, error code 32103

I'm developing a really simple MQTT Android client, so I can connect to it with an MQTT Server I created in C# using the MQTTnet library.
I first tested the C# broker with a C# client and Node-RED and it worked just fine.
I also tested the Android Client with CloudMQTT following this tutorial, and it also worked perfectly fine.
But when I tried to connect my Android Client to the C# Server, the Android Client gave me the following error:
Mqtt: Failed to connect to: tcp://localhost:1883 It's not possible to connect to server (32103) - java.net.ConnectException: Connection refused
I'm using BlueStacks as an emulator (I will try to test it in a real Android device asap). I tried to restart the emulator (as said in Paho Mqtt Android connecting to broker fails with (32103)) but it didn't work either.
The code for the Android Client is exactly the same as in the tutorial I mentioned earlier (using the Paho-MQTT library), but changing the serverUri and subscription topic:
final String serverUri = "tcp://localhost:1883";
final String subscriptionTopic = "step/time";
The app only has a TextView where I set the received messages.
As for the C# server, I'm sending a timestamp every 10 seconds. This is my code:
namespace MQTTServerExample
{
class Program
{
static void Main(string[] args)
{
serverAsync();
}
private static async Task serverAsync()
{
// Starting the MQTT Server
var mqttServer = new MqttFactory().CreateMqttServer();
var options = new MqttServerOptions();
//Saving retained application messages
options.Storage = new RetainedMessageHandler();
// Publishing messages
await mqttServer.StartAsync(options);
Console.WriteLine("### SERVER CONNECTED ###");
Console.WriteLine("Press ENTER to exit.");
MqttApplicationMessage message;
#pragma warning disable CS4014
Task.Run(async () =>
{
while (true)
{
message = new MqttApplicationMessageBuilder()
.WithTopic("step/time")
.WithPayload(DateTime.Now.ToString())
.WithExactlyOnceQoS()
.WithRetainFlag(true)
.Build();
await mqttServer.PublishAsync(message);
await Task.Delay(10000); // Every 10 seconds
}
});
#pragma warning restore CS4014
//await mqttServer.PublishAsync(message);
Console.ReadKey();
await mqttServer.StopAsync();
}
}
I'm new to connection protocols and I still don't understand them clearly, so I was hoping you could help me understand this problem.
A java.net.ConnectException: Connection refused exception means that there is no service listening on the host and port you are trying to connect to, for example because the host address or port is wrong, or the service is not started.
Using "tcp://localhost:1883" as server address only works if the server runs on the same machine as the client (i.e. in your case the Android device). You should use the server's name or IP address.
I'm going to guess that your C# broker is only listening on localhost not the external IP address of the machine hosting it.
This would work when ran the C# client on the same machine, but not when you use the external IP address from the Android client.
The other possible option is that you are running a firewall on the broker machine which is stopping the Android client connecting.

How do I properly clean up TcpListener, to avoid "Address already in use" errors?

I keep getting a SocketException: Address already in use when running my program multiple times.
Minimal example:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace test
{
class Program
{
static TcpListener listener;
static void Main(string[] args)
{
listener = new TcpListener(new IPEndPoint(IPAddress.Any, 3349));
listener.Start();
listener.BeginAcceptSocket(Accept, null);
Console.WriteLine("Started!");
// Simulate a random other client connecting, nc localhost 3349 does the same thing
var client = new TcpClient();
client.Connect("localhost", 3349);
Thread.Sleep(2000);
listener.Stop();
Console.WriteLine("Done!");
}
static void Accept(IAsyncResult result)
{
using(var socket = listener.EndAcceptSocket(result))
{
Console.WriteLine("Accepted socket");
Thread.Sleep(500);
socket.Shutdown(SocketShutdown.Both);
}
Console.WriteLine("Socket fully closed");
}
}
}
Run the program twice (dotnet run): the first time it will complete normally, but the second time it will fail saying "Address already in use".
Note that the missing dispose of client is not the problem here -- I can replicate the same error manually by using nc localhost 3349.
How can I clean up the listener so that I don't run into the error?
OS & .NET info:
dotnet --version
2.1.103
lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.4 LTS
Release: 16.04
Codename: xenial
This problem is not present on Windows. It also doesn't occur when using mono, so this seems to be specific to Microsoft's linux implementation.
This is actually intended behaviour. To fix the error, you should set the ReuseAddress socket option to true, if and only if the code is not running on Windows:
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
The difference between Windows and Linux (and presumably MacOS) is caused by the differences in the socket implementations:
On Windows, you can bind to an address and IP unless there's another connection active on the same address/IP combination.
On Linux, you can bind to an address unless there's any other connection on the same address/IP combination. This means that you cannot bind if there's still a connection in the TIME_WAIT or CLOSE_WAIT state.
As explained in this other question, SO_REUSEADDR can be used to relax these restrictions on Linux.
Mono has tried to emulate the behaviour of Windows by setting SO_REUSEADDR to true by default. The official .NET core runtime does not do this, and thus will cause the "Address already in use" error on Linux.
However this does not mean that it's safe to always set SO_REUSEADDR! On Windows, SO_REUSEADDR means something slightly different (source):
A socket with SO_REUSEADDR can always bind to exactly the same source
address and port as an already bound socket, even if the other socket
did not have this option set when it was bound. This behavior is
somewhat dangerous because it allows an application "to steal" the
connected port of another application.
Therefore, SO_REUSEADDR should only be set on non-Windows systems.

SuperSocket can't set up a server

I am writing this to find out why the code below is resulting in failed setup for supersocket server.
var appServer = new AppServer();
if (!appServer.Setup(8080)){
MessageBox.Show("Failed!");
}
I have added rule in firewall that allows port 8080 since my firewall is enabled by company IT. Don't know why the setup fails. Is there an explanation?
In testing this locally in a console application using the following (requires NuGet packages SuperSocket and SuperSocket.Engine;
namespace SupersocketTest
{
using System;
using SuperSocket.SocketBase;
class Program
{
static void Main(string[] args)
{
var server = new AppServer();
bool success = server.Setup(8080);
Console.WriteLine($"Server setup: {success}");
Console.ReadKey();
}
}
}
The operation completes successfully.
Looking at the code online, the underlying connection is still Socket based (as the name of the package implies). As such, it's subject to all the rules around how sockets work normally in .NET.
Things that can cause a Socket fail to be set up are (but not limited to)
The socket is in use
The socket has been closed
The OS has run out of available sockets (not likely but technically possible)
Without more detail on the exception you're getting I can only guess but I suspect that the socket is in use as 8080 is a common alternative to 80 that another application could be using.

'No connection could be made because the target machine actively refused it'

I am working on a 'Smart Device Project' using .Net Framework 3.5. I am trying to connect to some Java SOAP services on a remote server.
In order to do that, I added 'Web References' to my project.
When I try to call my web service I get a WebException 'Unable to connect to the remote server' with the inner exception being 'No connection could be made because the target machine actively refused it'.
I searched quite a lot on the Web and StackOverflow and found a lot of ASP configuration and 'Unavaliable port' answers, but as I have another application using the exact same Service successfully, I can't get why the new one isn't getting through (It did sometimes through my tests so I suppose my client implementation isn't that bad)
I tried to look if there was some connection issue on the port by using some TcpClient:
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient();
try
{
client.Connect("myServerName", 8087);
MessageBox.Show("Success");
} catch (Exception ex)
{
MessageBox.Show("Failure");
}
finally
{
client.Close();
}
This connection succeed.
Here is a sample on how I call my WebService:
WSServiceExtended srv = new WSServiceExtended();
srv.Proxy = new System.Net.WebProxy();
ServeurWSI wsi = new ServeurWSI();
srv.Url = "http://myServerName:8087/myServerApp/services/myService";
wsr = srv.login(wsi);
The service is called 'Extended' because I overrided the auto-generated one in order to add Cookie managment since I am using the Compact Framework. Following the sample in this thread:
https://social.msdn.microsoft.com/Forums/en-US/34d88228-0b68-4fda-a8cd-58efe6b47958/no-cookies-sessionstate-in-compact-framework?forum=vssmartdevicesvbcs
EDIT:
I made some new tests with the Web references and got it to work.
When I add the Web Reference, I have to put some Url to the Web Service. When I set it with the actual hostname instead of the 'localhost' everything is fine.
But then, since I set it manually to the real address just before the call, it shouldn't matter
srv.Url = "http://myServerName:8087/myServerApp/services/myService";
EDIT2:
I might have forgotten some specifics about my environnement.
The Web Services are exposed on my computer on some Tomcat Server.
The application I am working on is also developped on this computer (That's why I can add Web References by putting 'localhost' in the address)
The application is then deployed on a distant device (Windows CE) that will make calls the Web Services through WIFI (There, localhost wouldn't work then)
I tried calling the Web services from other computers successfully.
I'm beginning to think that there might be some differential between the called Url and the one that is set, otherwise, how would I have a difference in behaviour such as the one described in the first edit?
EDIT3:
Well..Seems like it's not a network issue but a .Net compact framework (usage?) issue...
The Url property of the Web Service implementation is simply ignored and the one in the Reference.cs is used in place.
If someone had some idea on how I could troubleshot this, I would really appreciate it.
That error means that you reached a server and the server said "no way". So you're either hitting the wrong server or the wrong port.
I find the telnet client is useful for testing stuff like this. From the command line, you can do:
telnet [servername] [port]
So something like:
telnet myServerName 8087
If it goes to a blank screen, then it connected successfully. If it does not connect, it'll tell you.
The telnet client is no longer installed by default in Windows 7+, so you'll have to install it. See here for instructions: https://technet.microsoft.com/en-ca/library/cc771275
If the connection does open, you could paste in an actual HTTP request to see what happens. A simple GET would look something like this:
GET /myServerApp/services/myService HTTP/1.1
Host: myServerName:8087
One reason for this error can be that the service binds to only a certain IP address. It could well be that the service only listens on the IP that is assigned to the host name, but not on the localhost IP (127.0.0.1).
For example:
If the host myServerName has the public IP 192.168.0.1, your service can choose to listen on all IPs assigned to the host (sometimes specifying 0.0.0.0), or it can specifically listen on 192.168.0.1 only. In that case you will not be able to connect through 127.0.0.1, because the service simply doesn't listen on that IP.
You can "use" this inverse of this feature to make a service accessible only to local clients, not on the public IP-Address, by listening on 127.0.0.1 only, but not on the public IP. This is sometimes used on Linux for example to make MySQL only accessible on the host itself.
I was starting to forget this post but I finally found the problem that was messing things up and it has nothing to do with programmation.
I was doing the calls while the device was connected to the computer via the 'Windows Mobile Device Center' allowing to access the device from Windows.
While connected, the host provided is ignored and all calls on the specified port are handled by the connected computer.
Disconnecting the device allows to communicate properly...

Categories