I have a Visual Studio 2008 C# .NET 3.5 application running on Windows XP SP3 x86. In my application, I have an event handler OnSendTask that can be called by multiple threads simultaneously. It opens a TCP connection to a remote host and sends/receives data.
For example:
/// <summary>
/// prevent us from exceeding the maximum number of half-open TCP
/// connections in Windows XP.
/// </summary>
private System.Threading.Semaphore tcp_connection_lock_ =
new System.Threading.Semaphore(10, 10);
public event EventHandler<SendTaskEventArgs> SendTask;
private void OnSendTask(object sender, SendTaskEventArgs args)
{
try
{
tcp_connection_lock_.WaitOne();
using (TcpClient recipient = new TcpClient())
{
// error here!
recipient.Connect(args.IPAddress, args.Port);
using (NetworkStream stream = recipient.GetStream())
{
// read/write data
}
}
catch
{
// write exceptions to the logfile
}
finally
{
tcp_connection_lock_.Release();
}
}
void SendTasks(int tasks_to_send)
{
using (ManualResetEvent done_event = new ManualResetEvent(false))
{
int countdown = tasks_to_send;
for (int i = 0; i < tasks_to_send; ++i)
{
ThreadPool.QueueUserWorkItem((o) =>
{
SendTaskEventArgs args = new SendTaskEventArgs(/*...*/);
EventHandler<SendTaskEventArgs> evt = SendTask;
if (evt != null)
evt(this, e);
if (Interlocked.Decrement(ref countdown) == 0)
done_event.Set();
}, i);
}
done_event.WaitOne();
}
}
Unfortunately, I occasionally see this error:
System.Net.Sockets.SocketException: 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 192.168.0.16:59596
at System.Net.Sockets.TcpClient.Connect(String hostname, Int32 port)
Some points of information:
If I send a task to 40 remotes, I will see this response from around 6.
A Wireshark trace shows no attempt to even initiate a TCP connection from the PC to the remote.
I can ping the remote from the PC and get consistent good responses.
The remotes are all on the same switch and subnet as the PC running this application. There is no fancy networking in the way.
Can anybody suggest what may be causing this error or how I can fix it?
Thanks
I am not sure of all of the details behind the max half-open TCP connections but, I believe it is NOT specific to application connections, but rather system wide. Are you sure that when this error occurs there are no other applications on the system that are creating TCP connections?
I'd setup a retry whenever an error occurs. Something like:
private const int MaxRetries = 10;
private void OnSendTask(object sender, SendTaskEventArgs args)
{
bool retry = false;
try
{
tcp_connection_lock_.WaitOne();
using (TcpClient recipient = new TcpClient())
{
// error here!
recipient.Connect(args.IPAddress, args.Port);
using (NetworkStream stream = recipient.GetStream())
{
// read/write data
}
}
}
catch (SocketException ex)
{
if(args.RetryCount < MaxRetries)
{
retry = true;
args.RetryCount++;
}
else
{
// write exceptions to the logfile
}
}
finally
{
tcp_connection_lock_.Release();
}
if(retry)
{
Thread.Sleep(1);
OnSendTask(sender, args);
}
}
Related
I am working on HoloLens (Unity-UWP) and trying to make a connection with PC (UWP) or Android phone work (Xamarin). So far I tried client and host with both Bluetooth and TCP (even two versions with different libraries) on Android and UWP. I kept the code entirely separated from user interface, so that it is easier to use, to understand and modular. An Action<string> is used to output results (error logs and sent messages).
Everything that is not on the HoloLens works fine (even though it's exactly the same code). It worked from PC (UWP) to Android with client and host switched. But it doesn't even work between HoloLens and PC (UWP). The behavior ranged from crashes (mostly for Bluetooth) to instant disconnection. The last tests resulted in disconnection once bytes are about to be received. It could even read the first 4 bytes (uint for the length of the following UTF-8 message), but then it was disconnected. The other devices seemed to work fine.
What I know: Capabilities are set, the code works, the issue is likely something that is common for everything that has to do with networking and HoloLens.
So the question is, is Unity or HoloLens incompatible with something I am using? What I used which is worth mentioning: StreamSocket, BinaryWriter, BinaryReader, Task (async, await). Or is HoloLens actively blocking communication with applications on other devices? I know it can connect to devices with Bluetooth and that it can connect via TCP, and it looks like people succeed to get it to work. Are there known issues? Or is there something with Unity that causes this - a bad setting maybe? Do I have to use async methods or only sync? Are there incompatibility issues with Tasks/Threads and Unity? Is this possibly the issue (inability to consent to permissions)?
Another thing to note is that I cannot ping HoloLens via its IP by using the cmd, even though the IP is correct.
I'd appreciate any advice, answer or guess. I can provide more information if requested (see also the comments below). I would suggest to focus on the TCP connection as it seemed to be working better and appears to be more "basic." Here is the code:
using System;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Windows.Networking;
using Windows.Networking.Sockets;
#region Common
public abstract class TcpCore
{
protected StreamSocket Socket;
protected BinaryWriter BWriter;
protected BinaryReader BReader;
protected Task ReadingTask;
public bool DetailedInfos { get; set; } = false;
public bool Listening { get; protected set; }
public ActionSingle<string> MessageOutput { get; protected set; } = new ActionSingle<string> (); // Used for message and debug output. They wrap an Action and allow safer use.
public ActionSingle<string> LogOutput { get; protected set; } = new ActionSingle<string> ();
protected const string USED_PORT = "1337";
protected readonly Encoding USED_ENCODING = Encoding.UTF8;
public abstract void Disconnect ();
protected void StartCommunication ()
{
Stream streamOut = Socket.OutputStream.AsStreamForWrite ();
Stream streamIn = Socket.InputStream.AsStreamForRead ();
BWriter = new BinaryWriter (streamOut); //{ AutoFlush = true };
BReader = new BinaryReader (streamIn);
LogOutput.Trigger ("Connection established.");
ReadingTask = new Task (() => StartReading ());
ReadingTask.Start ();
}
public void SendMessage (string message)
{
// There's no need to send a zero length message.
if (string.IsNullOrEmpty (message)) return;
// Make sure that the connection is still up and there is a message to send.
if (Socket == null || BWriter == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; }
uint length = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (length);
byte[] buffer = USED_ENCODING.GetBytes (message);
if (DetailedInfos) LogOutput.Trigger ("Sending: " + message);
BWriter.Write (countBuffer);
BWriter.Write (buffer);
BWriter.Flush ();
}
protected void StartReading ()
{
if (DetailedInfos) LogOutput.Trigger ("Starting to listen for input.");
Listening = true;
while (Listening)
{
try
{
if (DetailedInfos) LogOutput.Trigger ("Starting a listen iteration.");
// Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message (=n).
uint length = BReader.ReadUInt32 ();
if (DetailedInfos) LogOutput.Trigger ("ReadLength: " + length.ToString ());
MessageOutput.Trigger ("A");
byte[] messageBuffer = BReader.ReadBytes ((int) length);
MessageOutput.Trigger ("B");
string message = USED_ENCODING.GetString (messageBuffer);
MessageOutput.Trigger ("Received Message: " + message);
}
catch (Exception e)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
{
// Seems to occur on disconnects. Let's not throw().
Listening = false;
Disconnect ();
LogOutput.Trigger ("Unknown error occurred: " + e.Message);
break;
}
else
{
Listening = false;
Disconnect ();
break;
}
}
}
LogOutput.Trigger ("Stopped to listen for input.");
}
}
#endregion
#region Client
public class GTcpClient : TcpCore
{
public async void Connect (string target, string port = USED_PORT) // Target is IP address.
{
try
{
Socket = new StreamSocket ();
HostName serverHost = new HostName (target);
await Socket.ConnectAsync (serverHost, port);
LogOutput.Trigger ("Connection successful to: " + target + ":" + port);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
#region Server
public class GTcpServer : TcpCore
{
private StreamSocketListener socketListener;
public bool AutoResponse { get; set; } = false;
public async void StartServer ()
{
try
{
//Create a StreamSocketListener to start listening for TCP connections.
socketListener = new StreamSocketListener ();
//Hook up an event handler to call when connections are received.
socketListener.ConnectionReceived += ConnectionReceived;
//Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await socketListener.BindServiceNameAsync (USED_PORT);
}
catch (Exception e)
{
LogOutput.Trigger ("Connection error: " + e.Message);
}
}
private void ConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
listener.Dispose ();
Socket = args.Socket;
if (DetailedInfos) LogOutput.Trigger ("Connection received from: " + Socket.Information.RemoteAddress + ":" + Socket.Information.RemotePort);
StartCommunication ();
}
catch (Exception e)
{
LogOutput.Trigger ("Connection Received error: " + e.Message);
}
}
public override void Disconnect ()
{
Listening = false;
if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
if (BWriter != null) { BWriter.Dispose (); BWriter.Dispose (); BWriter = null; }
if (BReader != null) { BReader.Dispose (); BReader.Dispose (); BReader = null; }
if (Socket != null) { Socket.Dispose (); Socket = null; }
if (ReadingTask != null) { ReadingTask = null; }
}
}
#endregion
Coincidentially, I just implemented a BT connection between HoloLens and an UWP app. I followed the sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BluetoothRfcommChat.
As capabilities, I set "Bluetooth" (of course), "Internet (Client & Server)" and "Private Networks (Client & Server)". The steps on the server side then are:
Create an RfcommServiceProvider for your own or an existing (eg OBEX object push) service ID.
Create a StreamSocketListener and wire its ConnectionReceived Event.
Bind the service Name on the listener: listener.BindServiceNameAsync(provider.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
If you have a custom service ID, set its name along with other attributes you may want to configure. See the sample linked above for this. I think, this is mostly optional.
Start advertising the BT service: provider.StartAdvertising(listener, true);
Once a client connects, there is a StreamSocket in the StreamSocketListenerConnectionReceivedEventArgs that you can use to create a DataReader and DataWriter on like on any other stream. If you only want to allow one client, you can also stop advertising now.
On the client side, you would:
Show the DevicePicker and let the user select the peer device. Do not forget setting a filter like picker.Filter.SupportedDeviceSelectors.Add(BluetoothDevice.GetDeviceSelectorFromPairingState(true)); You can also allow unpaired devices, but you need to call PairAsync before you can continue in step 2. Also, I think there is no way to circumvent the user consent dialogue in this case, so I would recommend pairing before. To be honest, I did not check whether the unpaired stuff works on HoloLens.
You get a DeviceInformation instance from the picker, which you can use to obtain a BT device like await BluetoothDevice.FromIdAsync(info.Id);
Get the services from the device like device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); and select the one you are interested in. Note that I found that the built-in filtering did not behave as expected, so I just enumerated the result and compared the UUIDs manually. I believe that the UWP implementation performs a case-sensitive string comparison at some point, which might lead to the requested service not showing up although it is there.
Once you found your service, which I will call s from now on, create a StreamSocket and connect using socket.ConnectAsync(s.ConnectionHostName, s.ConnectionServiceName, SocketProtectionLevel.PlainSocket);
Again, you can not create the stream readers and writers like on the server side.
The answer is Threading.
For whoever may have similar issues, I found the solution. It was due to Unity itself, not HoloLens specifically. My issue was that I wrote my code separately in an own class instead of commingle it with UI code, which would have made it 1. unreadable and 2. not modular to use. So I tried a better coding approach (in my opinion). Everybody could download it and easily integrate it and have basic code for text messaging. While this was no issue for Xamarin and UWP, it was an issue for Unity itself (and there the Unity-UWP solution as well).
The receiving end of Bluetooth and TCP seemed to create an own thread (maybe even something else, but I didn't do it actively), which was unable to write on the main thread in Unity, which solely handles GameObjects (like the output log). Thus I got weird log outputs when I tested it on HoloLens.
I created a new TCP code which works for Unity but not the Unity-UWP solution, using TcpClient/TcpListener, in order to try another version with TCP connection. Luckily when I ran that in the editor it finally pointed on the issue itself: The main thread could not be accessed, which would have written into the UI - the text box for the log output. In order to solve that, I just had to use Unity's Update() method in order to set the text to the output. The variables themselves still could be accessed, but not the GameObjects.
I have a long running TCP connection. A machine (IoT device) establishes a connection with the server, connection is setup (encryption and stuff) and data stored, connection is kept open a while.
Everything kind of works but sometimes the server 'drops' the connection with an error:
An established connection was aborted by the software in your host machine (code: 10053 - ConnectionAborted). But the connection isn't dropped, cause server can read data from the device after the error and could start sending again. If the connection drops in real, both the server and client need to reinitialize connection (security and stuff).
There is nothing really that indicates why network stream cannot be written to.
And polling the socket says, that it's writetable, and in next point it throws an exception. Seems to happen randomly.
public class ClientIdentifier
{
...
public TcpClient Connection { get; set; }
public BlockDecoder ConnectionDecoder { get; set; }
}
private void ReplyToClient(ClientIdentifier client, byte[] data)
{
byte[] encrypted = client.ConnectionDecoder.Encrypt(data);
var stream = client.Connection.GetStream();
int dataIndex = 0;
while (dataIndex != encrypted.Length)
{
if (CanWriteClient(client))
{
byte[] block = encrypted.GetChunk(dataIndex, Frame.BLOCK_LENGTH);
stream.Write(block, 0, Frame.BLOCK_LENGTH);
dataIndex += Frame.BLOCK_LENGTH;
}
}
}
private bool CanWriteClient(ClientIdentifier client)
{
try
{
return client.Connection.Client.Poll(1000, SelectMode.SelectWrite);
}
catch (Exception ex)
{
Logger.Warn(ex, $"[{client.HexIdentifier}]: Polling client write failed");
return false;
}
}
EDIT (12.11.2017)
Server: 192.168.1.150
Device: 192.168.1.201
I can see that the device sends RST when the server resets Seq and Ack in some weird way.
Managed to find the mistake. Timeout was set to way too low.
...
TcpClient client = server.EndAcceptTcpClient(ar);
int timeout = (int)TimeSpan.FromSeconds(3).TotalMilliseconds;
client.ReceiveTimeout = timeout;
client.SendTimeout = timeout;
...
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);
}
I have around 5000 modem (thin clients), and I want to communicate with them, one of a my method is like this : string GetModemData(modemID), now I have an open port in server that listens to modem and I'm using socket programming to send data to modems (calling related function), but when i want send data to multiple modem in a same time and get response from them, I don't know what should i do? I can send data to one modem and waiting for its response and then send another data to other modems (sequential), but the problem is client should be wait long time to get answer(may be some different client want to get some information from modems so they all will be wait into the Q or something like this), I think one way to solving this problem is to use multiple port and listen for each modem to related port, but it takes too many ports and also may be memory usage going up and exceed my available memory space, so some lost may be occurred (is this true?). what should to do ? I'd thinking about Parallelism, but i think its not related i should to wait for one port, because i don't know should to pass current received data to which client. I'm using asp.net.
currently I'm doing like this:
private void StartListener()
{
ModemTcpListener = new TcpListener(ModemPort);
//ClientTcpListener = new TcpListener(ClientPort);
ModemTcpListener.Start();
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
}
and in return
private void DoReadModemCallback(IAsyncResult ar)
{
try
{
bool bRet = ar.AsyncWaitHandle.WaitOne(420000);
Modem modem = ar.AsyncState as Modem;
if (!bRet || modem == null)
{
return;
}
}
catch{}
// now send data to which client?????? if i'm going to use async????
}
and :
private void DoAcceptModemCallback(IAsyncResult ar)
{
try
{
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
TcpClient tcpClient = ModemTcpListener.EndAcceptTcpClient(ar);
Modem modem= new Modem(tcpClient, "");
tcpClient.GetStream().BeginRead(modem.Buffer, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(DoReadModemCallback), modem);
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
Log.Write("a Modem connect ...");
}
catch (Exception ex)
{
}
}
Heres an example keeping track of all your clients. I've compacted it for readability. You should really split it up into multiple classes.
I'm using Pool (which I just created and commited) and SimpleServer. Both classes are part of a library that I'm currently building (but far from done).
Don't be afraid of having 5000 sockets open, they do not consume much resources when you are using asynchronous operations.
public class SuperServer
{
private List<ClientContext> _clients = new List<ClientContext>();
private SimpleServer _server;
private Pool<byte[]> _bufferPool;
public SuperServer()
{
// Create a buffer pool to be able to reuse buffers
// since your clients will most likely connect and disconnect
// often.
//
// The pool takes a anonymous function which should return a new buffer.
_bufferPool = new Pool<byte[]>(() => new byte[65535]);
}
public void Start(IPEndPoint listenAddress)
{
_server = new SimpleServer(listenAddress, OnAcceptedSocket);
// Allow five connections to be queued (to be accepted)
_server.Start(5);
}
// you should handle exceptions for the BeginSend
// and remove the client accordingly.
public void SendToAll(byte[] info)
{
lock (_clients)
{
foreach (var client in _clients)
client.Socket.BeginSend(info, 0, info.Length, SocketFlags.None, null, null);
}
}
// Server have accepted a new client.
private void OnAcceptedSocket(Socket socket)
{
var context = new ClientContext();
context.Inbuffer = _bufferPool.Dequeue();
context.Socket = socket;
lock (_clients)
_clients.Add(context);
// this method will eat very few resources and
// there should be no problem having 5000 waiting sockets.
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
//Woho! You have received data from one of the clients.
private void OnRead(IAsyncResult ar)
{
var context = (ClientContext) ar.AsyncState;
try
{
var bytesRead = context.Socket.EndReceive(ar);
if (bytesRead == 0)
{
HandleClientDisconnection(context);
return;
}
// process context.Inbuffer here.
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
return;
}
// use a new try/catch to make sure that we start
// read again event if processing of last bytes failed.
try
{
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
}
}
// A client have disconnected.
private void HandleClientDisconnection(ClientContext context)
{
_bufferPool.Enqueue(context.Inbuffer);
try
{
context.Socket.Close();
lock (_clients)
_clients.Remove(context);
}
catch(Exception err)
{
//log exception
}
}
// One of your modems
// add your own state info.
private class ClientContext
{
public byte[] Inbuffer;
public Socket Socket;
}
}
Used classes:
Pool: http://fadd.codeplex.com/SourceControl/changeset/view/58858#1054902
SimpleServer: http://fadd.codeplex.com/SourceControl/changeset/view/58859#1054893
You need to use the asynchronous tcp/ip methods. This article shows how:
http://www.codeproject.com/KB/IP/asyncsockets.aspx
The critical piece is the BeginReceive() and related callback functions. Any more q's, please leave comments to this answer ;) BEST OF LUCK!
You need multi threading, whenever a client establishes a connection to the server start a new thread for it and start communication send/receive.
Here are some articles explaining multithreading in c#,
c-sharpcorner
codeproject
And here's a sample server application with multithreading,
http://www.dotnetspider.com/resources/2829-A-multi-readed-server-C-which-finds-prime-num.aspx
I need to poll a server, which is running some propriatary software, to determine if this service is running. Using wireshark, I've been able to narrow down the TCP port its using, but it appears that the traffic is encrypted.
In my case, its a safe bet that if the server is accepting connections (i.e. telnet serverName 1234) the service is up and all is OK. In other words, I don't need do any actual data exchange, just open a connection and then safely close it.
I'm wondering how I can emulate this with C# and Sockets. My network programming basically ends with WebClient, so any help here is really appreciated.
The process is actually very simple.
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
try
{
socket.Connect(host, port);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.ConnectionRefused)
{
// ...
}
}
}
Just use TcpClient try to connect to the server, TcpClient.Connect will throw an exception if the connection fails.
bool IsListening(string server, int port)
{
using(TcpClient client = new TcpClient())
{
try
{
client.Connect(server, port);
}
catch(SocketException)
{
return false;
}
client.Close();
return true;
}
}
I've used the following code. There is one caveat ... in a high transaction environment, the client's available ports may run out as the sockets are not released by the OS at the same rate they are released by the .NET code.
If anyone's got a better idea, please post. I've seen snowball issues arise where the server can no longer make outgoing connections. I'm working on a better solution ...
public static bool IsServerUp(string server, int port, int timeout)
{
bool isUp;
try
{
using (TcpClient tcp = new TcpClient())
{
IAsyncResult ar = tcp.BeginConnect(server, port, null, null);
WaitHandle wh = ar.AsyncWaitHandle;
try
{
if (!wh.WaitOne(TimeSpan.FromMilliseconds(timeout), false))
{
tcp.EndConnect(ar);
tcp.Close();
throw new SocketException();
}
isUp = true;
tcp.EndConnect(ar);
}
finally
{
wh.Close();
}
}
}
catch (SocketException e)
{
LOGGER.Warn(string.Format("TCP connection to server {0} failed.", server), e);
isUp = false;
}
return isUp;
Use the TcpClient class to connect the server.