IPv4 mapped IPv6 addresses - c#

I'm trying to make a IP version-agnostic client/server. I've been playing around with this in C++ and came up with something that works using the IN6ADDR_SETV4MAPPED macro (as Microsoft so kindly recommends). I followed the code sample here to accomplish this; my code that converts the address is no different from the example and everything works. I can connect to the server from the client by typing in both an IPv4 and IPv6 address (application does the mapping accordingly).
Now I'm looking for a solution in C# to upgrade a simple chat server I made and I've been unable to find any resources on how to use mapped addresses. I haven't found a function that provides the equivalent of IN6ADDR_SETV4MAPPED or any other facility in .net. My question is: how can I go about using a IPv4-mapped IPv6 address in C# (client-side)?
What I've tried:
Prepend string "::ffff:" to dotted IPv4 notation, call Socket.Connect using this address. Resulting address string looks like ::ffff:127.0.0.1.
Prepend string "::ffff:". Convert each octect from dotted format into hex and separate with colons, call Socket.Connect. Resulting address string looks like ::ffff:7f:0:0:1.
Neither of these approaches have worked so far.
Code snippet for server:
this.m_endPoint = new IPEndPoint(IPAddress.IPv6Any, 1337);
this.m_server.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
this.m_server.Bind(this.m_endPoint);
this.m_server.Listen(10);
Code snippet for client:
public ClientNet(string host, short port)
{
IPAddress ip;
if(IPAddress.TryParse(host, out ip))
{
string[] octs = host.Split(new char[] { '.' });
host = "::ffff:";
for(int i = 0; i < octs.Length; ++i)
{
host += string.Format("{0:x}", int.Parse(octs[i]));
if(i + 1 != octs.Length)
{
host += ":";
}
}
}
else
{
throw new ClientCreateException("[in ClientNet.Constructor] Unable to create client; use IPv4 address");
}
Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
client.Connect(host, port);
. . . //More initialization
}

Came back to this today thinking I might be able to figure it out. And I did! The answer is quite easy and I feel like a fool for not getting it for a year.
Two things about the code I posted:
Should have used IPAddress.MaptoIPv6 (see MSDN link) instead of that silly, contrived loop I wrote that's more prone to errors.
a. I realized later while working in .NET 4.0 that the convenience functions I used in my sample are not available until .NET 4.5. A quick code sample I threw together is at the bottom of this post, in case anyone else is stuck in an earlier version of .NET.
Real solution: Needed to call client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); prior to calling client.Connect().
Here is a full code example of a sample application I wrote today to test it out. I was able to make a connection using both ::1 and 127.0.0.1 as addresses. Note that the server Socket is created for IPv6, and that the SocketOptionName.IPv6Only option is set to 0 on both the client and the server.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace sixsharp
{
class Program
{
static void Main(string[] args)
{
if(args.Length <= 0) //run as server
RunServer();
else
RunClient(args);
Console.WriteLine("Press enter to close.");
Console.ReadLine();
}
static void RunServer()
{
using(Socket serv = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
serv.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
serv.Bind(new IPEndPoint(IPAddress.IPv6Any, 1337));
serv.Listen(5);
Console.Write("Listening for client connection...");
using(Socket client = serv.Accept())
{
Console.WriteLine("Client connection accepted from {0}", client.RemoteEndPoint.ToString());
byte[] buf = new byte[128];
client.Receive(buf, 128, SocketFlags.None);
Console.WriteLine("Got \'{0}\' from client", Encoding.ASCII.GetString(buf));
Console.WriteLine("Echoing response");
client.Send(buf);
client.Shutdown(SocketShutdown.Both);
}
}
Console.WriteLine("Done.");
}
static void RunClient(string[] args)
{
using(Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
Console.WriteLine("Setting up address, input is {0}", args[0]);
IPEndPoint ep;
try
{
ep = new IPEndPoint(IPAddress.Parse(args[0]), 1337);
}
catch(FormatException fe)
{
Console.WriteLine("IP address was improperly formatted and not parsed.");
Console.WriteLine("Detail: {0}", fe.Message);
return;
}
if(ep.AddressFamily == AddressFamily.InterNetwork)
{
ep = new IPEndPoint(ep.Address.MapToIPv6(), ep.Port);
if(!ep.Address.IsIPv4MappedToIPv6 || ep.Address.AddressFamily != AddressFamily.InterNetworkV6)
{
Console.WriteLine("Error mapping IPv4 address to IPv6");
return;
}
}
Console.WriteLine("Connecting to server {0} ...", ep.ToString());
try
{
client.Connect(ep);
}
catch(Exception ex)
{
Console.WriteLine("Unable to connect.\n Detail: {0}", ex.Message);
return;
}
client.Send(Encoding.ASCII.GetBytes("This is a test message. Hello!"));
byte[] buf = new byte[128];
client.Receive(buf);
Console.WriteLine("Got back from server: {0}", Encoding.ASCII.GetString(buf));
client.Shutdown(SocketShutdown.Both);
}
Console.WriteLine("Done.");
}
}
}
Client output:
Setting up address, input is 10.2.6.179
Connecting to server [::ffff:10.2.6.179]:1337 ...
Got back from server: This is a test message. Hello!
Done.
Press enter to close.
Server output:
Listening for client connection...Client connection accepted from [::ffff:10.2.6.179]:56275
Got 'This is a test message. Hello!
' from client
Echoing response
Done.
Press enter to close.
Sample extension methods providing the missing convenience functions in earlier versions of .NET:
static class IPAddressExtensions
{
public static IPAddress MapToIPv6(this IPAddress addr)
{
if(addr.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("Must pass an IPv4 address to MapToIPv6");
string ipv4str = addr.ToString();
return IPAddress.Parse("::ffff:" + ipv4str);
}
public static bool IsIPv4MappedToIPv6(this IPAddress addr)
{
bool pass1 = addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6, pass2;
try
{
pass2 = (addr.ToString().StartsWith("0000:0000:0000:0000:0000:ffff:") ||
addr.ToString().StartsWith("0:0:0:0:0:ffff:") ||
addr.ToString().StartsWith("::ffff:")) &&
IPAddress.Parse(addr.ToString().Substring(addr.ToString().LastIndexOf(":") + 1)).AddressFamily == AddressFamily.InterNetwork;
}
catch
{
return false;
}
return pass1 && pass2;
}
}

Related

Receiving UDP packets in Unity Android application with broadcast

I am currently creating an Unity Android application (GearVR) that could receive UDP packets send with broadcast on the Wi-Fi.
Unfortunately I can't receive any packet in my application.
Here is the script which I attached to a 3d game object.
public class UDPSceneScript : MonoBehaviour {
Thread udpListeningThread;
Thread udpSendingThread;
public int portNumberReceive;
UdpClient receivingUdpClient;
private void initListenerThread()
{
portNumberReceive = 5000;
Console.WriteLine("Started on : " + portNumberReceive.ToString());
udpListeningThread = new Thread(new ThreadStart(UdpListener));
// Run in background
udpListeningThread.IsBackground = true;
udpListeningThread.Start();
}
public void UdpListener()
{
receivingUdpClient = new UdpClient(portNumberReceive);
while (true)
{
//Listening
try
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
//IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Broadcast, 5000);
// Blocks until a message returns on this socket from a remote host.
byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
if (receiveBytes != null)
{
string returnData = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine("Message Received" + returnData.ToString());
Console.WriteLine("Address IP Sender" + RemoteIpEndPoint.Address.ToString());
Console.WriteLine("Port Number Sender" + RemoteIpEndPoint.Port.ToString());
if (returnData.ToString() == "TextTest")
{
//Do something if TextTest is received
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
void Start()
{
initListenerThread();
}
}
With the program SocketTest 3.0, I send "TextTest" to the address 255.255.255.255, port 5000
(I also tried by targeting directly the IP of the smartphone, it didn't work either)
Thanks in advance
Ran a quick test on your code and came to conclusion that your client code is fine. Replace all Console.WriteLine with Debug.Log and your will receive the data you are broadcasting. Console.WriteLine doesn't display anything in Unity's console.
If the problem is still there, please understand that some OS will block you from broadcasting to 255.255.255.255. If this is the case then get the IP of the device you are broadcasting from and replace the last octet with 255. Broadcast to that IP Address and that will also work just like 255.255.255.255.
For example, if your IP is 192.168.1.13, replace 13 with 255. You should broadcast to 192.168.1.255 in this case.
Finally, put the code below in your client script to make sure you kill that Thread when you click the Stop button in the Editor or you will have many problems during development.
void OnDisable()
{
if (udpListeningThread != null && udpListeningThread.IsAlive)
{
udpListeningThread.Abort();
}
receivingUdpClient.Close();
}

Socket.Bind and Connect using local addresses

To test my server/client application, where each client is known by its IP address, I created several network adapters (see How to Create a Virtual Network Adapter in .NET?). Both 192.168.0.10 and 11 now correspond to local ethernet adaptors (10 being the "real" one, 11 being a loopback adapter).
The client can Connect itself to the server as long as it doesn't Bind its socket to a specific address. But if it does, the server doesn't notice anything and a timeout occurs in the client (I want to use Bind as for security reasons the server automatically detects which client is connecting itself by looking at the IP address of the remote end point of the new connection: the server will drop the connection at once if it doesn't know the IP address - previously I was using several virtual machines, but it uses a lot more RAM and is less practical to use).
Here's the code in my server, listening eg on 192.168.0.10:1234
IPEndPoint myEP = new IPEndPoint(myAddress, myPort);
Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);
Socket acceptedSocket = listeningSocket.Accept();
Here's the code in my client, binding eg to 192.168.0.11 (any port) and connecting to 192.168.0.10:1234
Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(myAddress, 0)); // Bind to local address using automatic port
socket.Connect(new IPEndPoint(svrAddress, svrPort)); // Works fine without Bind, timeout with Bind
I've tried the same using the corresponding IPv6 addresses but I get the exact same result.
If I Bind the client on the same address (using a different port than the server), it works fine.
Any idea what I'm doing wrong?
EDIT Here is my test projects (it might be useful to someone)
Server part:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Server
{
class Program
{
static void Main(string[] args)
{
IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
string line = string.Empty;
while (line != "q")
{
// Gets the IP address to listen on.
Console.WriteLine("IP to listen on:");
int count = 0;
foreach (IPAddress ip in ips)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
string numString = Console.ReadLine();
int pos = Convert.ToInt32(numString) - 1;
IPAddress myAddress = ips[pos]; // Removing or not the scope ID doesn't change anything as "localEndPoint" below will contain it no matter what
// Binds and starts listening.
IPEndPoint myEP = new IPEndPoint(myAddress, 12345);
Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);
IPEndPoint localEndPoint = (IPEndPoint)listeningSocket.LocalEndPoint;
Console.WriteLine("Listening on {0}:{1}", localEndPoint.Address, localEndPoint.Port);
Task.Factory.StartNew(() =>
{
try
{
// Accepts new connections and sends some dummy byte array, then closes the socket.
Socket acceptedSocket = listeningSocket.Accept();
IPEndPoint remoteEndPoint = (IPEndPoint)acceptedSocket.RemoteEndPoint;
Console.WriteLine("Accepted connection from {0}:{1}.", remoteEndPoint.Address, remoteEndPoint.Port);
acceptedSocket.Send(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
acceptedSocket.Close(5000);
Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue");
}
catch (Exception ex)
{ }
});
line = Console.ReadLine();
// Closes the listening socket.
listeningSocket.Close();
}
}
}
}
Client part
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Client
{
class Program
{
static void Main(string[] args)
{
IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
string line = string.Empty;
while (line != "q")
{
// Gets the IP address to connect to (removes the "scope ID" if it's an IPv6).
Console.WriteLine("IP to connect to:");
int count = 0;
foreach (IPAddress ip in ips)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
string numString = Console.ReadLine();
int pos = Convert.ToInt32(numString) - 1;
IPAddress svrAddress = ips[pos].AddressFamily == AddressFamily.InterNetworkV6
? new IPAddress(ips[pos].GetAddressBytes())
: ips[pos];
Console.WriteLine("Connecting to " + svrAddress);
// Gets the IP address to bind on (can chose "none" - also removes the "scope ID" if it's an IPv6).
Console.WriteLine("IP to bind to:");
Console.WriteLine("0: none");
count = 0;
IPAddress[] filteredIps = ips.Where(i => i.AddressFamily == svrAddress.AddressFamily).ToArray();
foreach (IPAddress ip in filteredIps)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
numString = Console.ReadLine();
pos = Convert.ToInt32(numString) - 1;
IPEndPoint localEndPoint = (pos == -1)
? null
: new IPEndPoint(
filteredIps[pos].AddressFamily == AddressFamily.InterNetworkV6
? new IPAddress(filteredIps[pos].GetAddressBytes())
: filteredIps[pos]
, 0);
Console.WriteLine("Binding to " + (localEndPoint == null ? "none" : localEndPoint.Address.ToString()));
// Binds to an address if we chose to.
Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
if (localEndPoint != null)
socket.Bind(localEndPoint);
Task.Factory.StartNew(() =>
{
try
{
// Connects to the server and receives the dummy byte array, then closes the socket.
socket.Connect(new IPEndPoint(svrAddress, 12345));
IPEndPoint remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
Console.WriteLine("Connected to {0}:{1}", remoteEndPoint.Address, remoteEndPoint.Port);
byte[] buffer = new byte[10];
Console.WriteLine((socket.Receive(buffer) == buffer.Length) ? "Received message" : "Incorrect message");
socket.Close();
}
catch (Exception ex)
{
// An exception occured: should be a SocketException due to a timeout if we chose to bind to an address.
Console.WriteLine("ERROR: " + ex.ToString());
}
Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue");
});
line = Console.ReadLine();
}
}
}
}
Actually it was a configuration issue with my network adapters and it has to do with "Weak and Strong Host model".
From what I've read ( Using a specific network interface for a socket in windows ) binding on Windows previous to Vista would only work for incoming traffic, and it wouldn't do anything for outgoing traffic.
Starting with Vista it's possible but by default it won't work: you need to allow the "weak host model" using
netsh interface ipv4 set interface "loopback" weakhostreceive=enabled
netsh interface ipv4 set interface "loopback" weakhostsend=enabled
See https://web.archive.org/web/20150402200610/http://blog.loadbalancer.org/direct-server-return-on-windows-2008-using-loopback-adpter/ for more info.
EDIT
Actually, instead of creating several loopback adapters and changing their host model, it's a lot better and easier to just create one loopback adapter, give it several IP addresses on a different network than your real IP, and then only use those IPs for your test. That way there's no routing issue, and you're sure everything stays local (as there's no routing between the real and loopback adapter).
Use below code in the server for binding the connection on all the interface on the same port.
// Binds and starts listening.
IPEndPoint myEP = new IPEndPoint(IPAddress.Any, 12345);

Communication from C# Application to Firefox Extension

I need to send some strings (name, number) from my C# application to my Firefox Extension and i have read some techniques of doing but no idea how to implement it.
Pipes
Sockets(HTTP request)
If its using HTTP Request its better.
UPDATE
onSocketAccepted : function(socket, transport)
{
alert("came")
var input =transport.openInputStream(0, 0, 0);
alert(input)
}
does the message i send from the C# application will be in var input???
In C# Side DO
using System.Net;
using System.Net.Sockets;
static Socket sck;
acceptClient(String str)
{
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 777);
try
{
sck.Connect(localEndPoint);
string text = str;
byte[] data = Encoding.ASCII.GetBytes(text);
sck.Send(data);
// MessageBox.Show("Data Sent!\r\n");
}
catch
{
MessageBox.Show("Unable to connect to remote end point!\r\n");
}
}
In the Extension DO
function startServer()
{
var reader =
{
onInputStreamReady : function(input) {
var sin = Cc["#mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
sin.init(input);
sin.available();
//alert('count:'+count)
var request = '';
vaulee='';
while (sin.available()) {
request = request + sin.read(5120);
}
careditcardnum=request;
//alert(request)
input.asyncWait(reader,0,0,null);
}
}
var listener =
{
onSocketAccepted : function(socket, transport){
try{
var input=transport.openInputStream(0,0,0).QueryInterface(Ci.nsIAsyncInputStream);
var output = transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0);
var tm = Cc["#mozilla.org/thread-manager;1"].getService();
input.asyncWait(reader,0,0,tm.mainThread);
//stream.close();
} catch(ex2){ dump("::"+ex2); }
},
onStopListening : function(socket, status){
}
};
try {
serverSocket = Components.classes["#mozilla.org/network/server-socket;1"]
.createInstance(Components.interfaces.nsIServerSocket);
serverSocket.init(777,true,-1);
serverSocket.asyncListen(listener);
} catch(ex){ dump(ex); }
}
It's easiest to use TCP sockets. Firefox add-ons can run TCP servers using nsIServerSocket. You can see a pretty simple server implementation here (belongs to the Extension Auto-Installer extension). This isn't an HTTP server - it merely throws away whatever it considers HTTP headers without looking at them. There is also a full HTTP server implementation that's being used for Firefox unit tests - but you probably don't want it that complicated.
You could try SignalR.
https://github.com/SignalR/SignalR

How to check if TcpClient Connection is closed?

I'm playing around with the TcpClient and I'm trying to figure out how to make the Connected property say false when a connection is dropped.
I tried doing
NetworkStream ns = client.GetStream();
ns.Write(new byte[1], 0, 0);
But it still will not show me if the TcpClient is disconnected. How would you go about this using a TcpClient?
I wouldn't recommend you to try write just for testing the socket. And don't relay on .NET's Connected property either.
If you want to know if the remote end point is still active, you can use TcpConnectionInformation:
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
if (tcpConnections != null && tcpConnections.Length > 0)
{
TcpState stateOfConnection = tcpConnections.First().State;
if (stateOfConnection == TcpState.Established)
{
// Connection is OK
}
else
{
// No active tcp Connection to hostName:port
}
}
client.Close();
See Also:
TcpConnectionInformation on MSDN
IPGlobalProperties on MSDN
Description of TcpState states
Netstat on Wikipedia
And here it is as an extension method on TcpClient.
public static TcpState GetState(this TcpClient tcpClient)
{
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
return foo != null ? foo.State : TcpState.Unknown;
}
As far as I know/remember there is no way to test if a socket is connected other than reading or writing to it.
I haven't used the TcpClient at all but the Socket class will return 0 from a call to Read if the remote end has been shutdown gracefully.
If the remote end doesn't shutdown gracefully [I think] you get a timeout exception, can't remember the type sorry.
Using code like 'if(socket.Connected) { socket.Write(...) } creates a race condition. You're better off just calling socket.Write and handling the exceptions and/or disconnections.
The solution of Peter Wone and uriel is very nice. But you also need to check on the Remote Endpoint, since you can have multiple open connections to your Local Endpoint.
public static TcpState GetState(this TcpClient tcpClient)
{
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
&& x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
);
return foo != null ? foo.State : TcpState.Unknown;
}
I have created this function and working for me to check if client is still connected with server.
/// <summary>
/// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
/// </summary>
/// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
public bool isClientConnected()
{
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in tcpConnections)
{
TcpState stateOfConnection = c.State;
if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
{
if (stateOfConnection == TcpState.Established)
{
return true;
}
else
{
return false;
}
}
}
return false;
}
#uriel's answer works great for me, but I needed to code it in C++/CLI, which was not entirely trivial. Here is the (roughly equivalent) C++/CLI code, with a few robustness checks added in for good measure.
using namespace System::Net::Sockets;
using namespace System::Net::NetworkInformation;
TcpState GetTcpConnectionState(TcpClient ^ tcpClient)
{
TcpState tcpState = TcpState::Unknown;
if (tcpClient != nullptr)
{
// Get all active TCP connections
IPGlobalProperties ^ ipProperties = IPGlobalProperties::GetIPGlobalProperties();
array<TcpConnectionInformation^> ^ tcpConnections = ipProperties->GetActiveTcpConnections();
if ((tcpConnections != nullptr) && (tcpConnections->Length > 0))
{
// Get the end points of the TCP connection in question
EndPoint ^ localEndPoint = tcpClient->Client->LocalEndPoint;
EndPoint ^ remoteEndPoint = tcpClient->Client->RemoteEndPoint;
// Run through all active TCP connections to locate TCP connection in question
for (int i = 0; i < tcpConnections->Length; i++)
{
if ((tcpConnections[i]->LocalEndPoint->Equals(localEndPoint)) && (tcpConnections[i]->RemoteEndPoint->Equals(remoteEndPoint)))
{
// Found active TCP connection in question
tcpState = tcpConnections[i]->State;
break;
}
}
}
}
return tcpState;
}
bool TcpConnected(TcpClient ^ tcpClient)
{
bool bTcpConnected = false;
if (tcpClient != nullptr)
{
if (GetTcpConnectionState(tcpClient) == TcpState::Established)
{
bTcpConnected = true;
}
}
return bTcpConnected;
}
Hopefully this will help somebody.
As of 2019, in a cross-platform and async environment, I use the code below to continuosly check that the TCP channel is open. This check fires e.g. if the ethernet cable is pulled on my Windows machine, or if the Wifi is disabled on my Android device.
private async Task TestConnectionLoop()
{
byte[] buffer = new byte[1];
ArraySegment<byte> arraySegment = new ArraySegment<byte>(buffer, 0, 0);
SocketFlags flags = SocketFlags.None;
while (!_cancellationSource.Token.IsCancellationRequested)
{
try
{
await _soc.SendAsync(arraySegment, flags);
await Task.Delay(500);
}
catch (Exception e)
{
_cancellationSource.Cancel();
// Others can listen to the Cancellation Token or you
// can do other actions here
}
}
}
Please note that I have found GSF.Communication wrapper for System.Net.Sockets.TcpClient to be helpful because it has a CurrentState property that indicates whether the socket is open/connected or closed/disconnected. You can find details on the NuGet package here:
https://github.com/GridProtectionAlliance/gsf
Here is how you could setup a simple TCP socket and test whether it is connected:
GSF.Communication.TcpClient tcpClient;
void TestTcpConnectivity()
{
tcpClient = new GSF.Communication.TcpClient();
string myTCPServer = "localhost";
string myTCPport = "8080";
tcpClient.MaxConnectionAttempts = 5;
tcpClient.ConnectionAttempt += s_client_ConnectionAttempt;
tcpClient.ReceiveDataComplete += s_client_ReceiveDataComplete;
tcpClient.ConnectionException += s_client_ConnectionException;
tcpClient.ConnectionEstablished += s_client_ConnectionEstablished;
tcpClient.ConnectionTerminated += s_client_ConnectionTerminated;
tcpClient.ConnectionString = "Server=" + myTCPServer + ":" + myTCPport;
tcpClient.Initialize();
tcpClient.Connect();
Thread.Sleep(250);
if (tcpClient.CurrentState == ClientState.Connected)
{
Debug.WriteLine("Socket is connected");
// Do more stuff
}
else if (tcpClient.CurrentState == ClientState.Disconnected)
{
Debug.WriteLine(#"Socket didn't connect");
// Do other stuff or try again to connect
}
}
void s_client_ConnectionAttempt(object sender, EventArgs e)
{
Debug.WriteLine("Client is connecting to server.");
}
void s_client_ConnectionException(object sender, EventArgs e)
{
Debug.WriteLine("Client exception - {0}.", e.Argument.Message);
}
void s_client_ConnectionEstablished(object sender, EventArgs e)
{
Debug.WriteLine("Client connected to server.");
}
void s_client_ConnectionTerminated(object sender, EventArgs e)
{
Debug.WriteLine("Client disconnected from server.");
}
void s_client_ReceiveDataComplete(object sender, GSF.EventArgs<byte[], int> e)
{
Debug.WriteLine(string.Format("Received data - {0}.", tcpClient.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
}
I recommend the code from the answer of the user 'Uriel' above. His code in principle works great:
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
but it has a bug:
IPEndPoint.Equals() is used here to search the retrieved list of TCP connections for the one connection that has the same endpoints as the socket TcpClient.Client of the used TCP client.
The idea and concept are fine, but in real life may fail because of the coexistence of IPv4 and IPv6: Current operating systems like Windows 10 support IPv4 and IPv6, and sockets may be created with IPv6 addresses even if addresses like "100.111.1.251" in the IPv4 format were configured:
// Creation of TCP client:
m_tcpClient = new TcpClient ();
m_tcpClient.Connect ("100.111.1.251", 54321);
// Query of the local and remote IP endpoints in Visual Studio Immediate Window:
?m_tcpClient.Client.LocalEndPoint
{[::ffff:100.111.1.254]:55412}
Address: {::ffff:100.111.1.254}
AddressFamily: InterNetworkV6
Port: 55412
?m_tcpClient.Client.RemoteEndPoint
{[::ffff:100.111.1.251]:54321}
Address: {::ffff:100.111.1.251}
AddressFamily: InterNetworkV6
Port: 54321
// Query of the addresses of the local and remote IP endpoints in Visual Studio Immediate Window:
?((IPEndPoint)m_tcpClient.Client.LocalEndPoint).Address
{::ffff:100.111.1.254}
Address: '((IPEndPoint)m_tcpClient.Client.LocalEndPoint).Address.Address' threw an exception of type 'System.Net.Sockets.SocketException'
AddressFamily: InterNetworkV6
IsIPv4MappedToIPv6: true
IsIPv6LinkLocal: false
IsIPv6Multicast: false
IsIPv6SiteLocal: false
IsIPv6Teredo: false
ScopeId: 0
?((IPEndPoint)m_tcpClient.Client.RemoteEndPoint).Address
{::ffff:100.111.1.251}
Address: '((IPEndPoint)m_tcpClient.Client.RemoteEndPoint).Address.Address' threw an exception of type 'System.Net.Sockets.SocketException'
AddressFamily: InterNetworkV6
IsIPv4MappedToIPv6: true
IsIPv6LinkLocal: false
IsIPv6Multicast: false
IsIPv6SiteLocal: false
IsIPv6Teredo: false
ScopeId: 0
AddressFamily: InterNetworkV6 and IsIPv4MappedToIPv6: true indicate that the IP address in the local IP endpoint is an IPv6 address, although an IPv4 address was used to establish the connection. This obviously is because the socket is created in "dual-mode" or as "dual-stack":
https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
https://learn.microsoft.com/en-us/dotnet/api/system.net.ipaddress.isipv4mappedtoipv6
https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
https://www.ibm.com/docs/en/zos/2.2.0?topic=addresses-ipv4-mapped-ipv6
IPGlobalProperties.GetActiveTcpConnections() on the other side seems to always return IPEndPoint objects with IPv4 addresses:
?IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()[48].LocalEndPoint
{100.111.1.254:55412}
Address: {100.111.1.254}
AddressFamily: InterNetwork
Port: 55412
The consequence is that IPEndPoint.Equals() may return false even if two compared EndPoint objects refer to the same IP endpoint.
The solution for this problem is writing your own Equals() method, like:
public static class EndPointHelper
{
private static readonly AddressFamily[] addressFamilies =
{
AddressFamily.InterNetwork,
AddressFamily.InterNetworkV6
};
public static bool Equals (EndPoint? endPoint1, EndPoint? endPoint2)
{
if (endPoint1 is IPEndPoint ipEndPoint1 &&
endPoint2 is IPEndPoint ipEndPoint2)
{
if (ipEndPoint1.AddressFamily != ipEndPoint2.AddressFamily &&
addressFamilies.Contains(ipEndPoint1.AddressFamily) &&
addressFamilies.Contains(ipEndPoint2.AddressFamily))
{
var ipAddress1AsV6 = ipEndPoint1.Address.MapToIPv6();
var ipAddress2AsV6 = ipEndPoint2.Address.MapToIPv6();
return ipAddress1AsV6.Equals(ipAddress2AsV6)
&& ipEndPoint1.Port.Equals(ipEndPoint2.Port);
}
}
return object.Equals (i_endPoint1, i_endPoint2);
}
}
Furthermore, there is a bug in .NET 5, which makes the whole solution above unusable: IPGlobalProperties.GetActiveTcpConnections() has a memory leak (see https://github.com/dotnet/runtime/issues/64735), which will not be fixed in .NET 5 any more, because it has run out of support. The bug is not present in .NET 6. If you are tied to .NET 5, you will have to work around it by remembering the connection state yourself in a local variable (e.g. EnumState m_cachedState). Set this variable after each related operation, e.g. after a Connect() you would have to set it to EnumState.Connected.
This method of course will not detect when a connection was closed by the other side, so you have to cyclically check if the connection was closed, using this code:
var socket = m_tcpClient.Client;
bool state = socket.Poll (100, SelectMode.SelectRead);
int available = socket.Available;
return state && available == 0 // Condition for externally closed connection. The external close will not be recognized until all received data has been read.
? EnumState.Idle
: m_cachedState;
Try this, it works for me
private void timer1_Tick(object sender, EventArgs e)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
if (!client.Connected) sConnected = false;
else
{
byte[] b = new byte[1];
try
{
if (client.Client.Receive(b, SocketFlags.Peek) == 0)
{
// Client disconnected
sConnected = false;
}
}
catch { sConnected = false; }
}
}
if (!sConnected)
{
//--Basically what you want to do afterwards
timer1.Stop();
client.Close();
ReConnect();
}
}
i used Timer because, I wanted to check connection state at regular interval
and not in a LOOP with Listening code [I felt it was slowing the sending-recieving process]
In my case, I was sending some command to a server (running in a virtual machine on the same computer) and waiting for the response. However, if the server stopped unexpectedly while waiting, I did not get any notification. I tried the possibilities proposed by the other posters, but neither did work (it always said that the server is still connected). For me, the only thing that is working is to write 0 bytes to the stream:
var client = new TcpClient();
//... open the client
var stream = client.GetStream();
//... send something to the client
byte[] empty = { 0 };
//wait for response from server
while (client.Available == 0)
{
//throws a SocketException if the connection is closed by the server
stream.Write(empty, 0, 0);
Thread.Sleep(10);
}

Testing of socket communication program

Am starting with socket programming with a simple UDPClient program to send some data. The large code snippet is below:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
class ShowIP
{
public static void Main(string[] args)
{
string name = Dns.GetHostName();
//name = "GSL1460";
name = "GSL1296";
try
{
IPAddress[] addrs = Dns.GetHostEntry(name).AddressList;
foreach (IPAddress addr in addrs)
Console.WriteLine("{0}/{1}", name, addr);
Console.WriteLine("Started listening");
Thread listenerThread = new Thread(new ThreadStart(StartListeningUDP));
listenerThread.Start();
Console.WriteLine("Started sending");
for (int counter = 0; counter <= 3; counter++)
{
Thread.Sleep(1000);
Console.WriteLine("Sending {0} time", counter.ToString());
StartSendingUDP(addrs[0]);
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static void StartListeningUDP()
{
UdpClient udpListener = null;
IPEndPoint nwPoint = new IPEndPoint(IPAddress.Any, 12345);
while (true)
{
try
{
udpListener = new UdpClient(12345);
Console.WriteLine("Waiting to receive");
Byte[] receivedBytes = udpListener.Receive(ref nwPoint);
string receivedData = Encoding.ASCII.GetString(receivedBytes);
Console.WriteLine("Data received : " + receivedData);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
udpListener.Close();
}
}
}
private static void StartSendingUDP(IPAddress clientAddress)
{
UdpClient udpSender = new UdpClient();
try
{
Byte[] sendBytes = Encoding.ASCII.GetBytes("Say HI to Papa...");
Console.WriteLine("Data Sent : Say HI to Papa...");
udpSender.Send(sendBytes, sendBytes.Length, new IPEndPoint(clientAddress, 12345));
}
finally
{
udpSender.Close();
}
}
}
The sample works fine on local machine, but am not able to send data to another machine on the intranet.
During testing
Am uncommenting the appropriate code to send data to his machine
Am running the Receiver bit on his machine
Have checked that the required port is open on his machine
Am I missing something? Please suggest.
udpSender.Flush?
I'm not a C# person, so I can't comment too much on your code, but it looks basically okay. Make sure that the IP address you're sending to is being resolved correctly to your receiving machine.
Also, see if Windows has firewalled your internet connection, and try disabling the firewall if so. And, I know that Microsoft has some ideas about "safe" code that have caused us some problems in the past. I don't have any specifics, but there might be settings in the project that keep it from being able to access the network.
The UDP-Listener might be listening on localhost only. You could try to replace
udpListener = new UdpClient(12345)
in StartListeningUDP() with
udpListener = new UdpClient(new IPEndPoint(IPAddress.Any,12345))
you can't really send UDP over the internet without doing few things before.
you will get too many udp filters on the way.
even if you will disable your firewall, your router/provider modem can be set to block it.
else - your provider servers will block it.
so in fact you will have to make sure that this port is open for UDP, just as on your localhost it won't work unless you will open this port in the firewall and/or install the loopback adapter

Categories