.net socket problem: client disconnect from server - c#

On server side I have this code which running in new thread
static void ListenForConsultant()
{
while (true)
{
var serverSocket = new TcpListener(IPAddress.Any, 2111);
serverSocket.Start();
var clientSocket = serverSocket.AcceptTcpClient();
consultantConnected = true;
Console.WriteLine(" >> Consultant Connected");
byte[] bytesFrom = new byte[10025];
while (true)
{
if (!clientSocket.Connected)
{
break;
}
NetworkStream networkStream = clientSocket.GetStream();
bytesFrom = new byte[10025];
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
var dataFromConsultant = System.Text.Encoding.ASCII.GetString(bytesFrom);
if (dataFromConsultant.IndexOf("~") != -1 && dataFromConsultant.IndexOf("^") != -1 && dataFromConsultant.IndexOf("^") > dataFromConsultant.IndexOf("~"))
{
var lengthOfMessage = dataFromConsultant.IndexOf("^") - dataFromConsultant.IndexOf("~") - 1;
dataFromConsultant = dataFromConsultant.Substring(dataFromConsultant.IndexOf("~") + 1, lengthOfMessage);
Console.WriteLine(" >> From consultant:" + dataFromConsultant);
}
}
consultantConnected = false;
Console.WriteLine(" >> Consultant Disconnected");
serverSocket.Stop();
}
}
I connect using putty to port 2111. All works ok, but when I close putty socket doesn't close, however I have condition
if (!clientSocket.Connected)
{
break;
}
Debug shows me that clientSocket.Connected is true even after I disconnected from server.
Why does this happen?

The tcpClient.Connected property value is not reliable, it's value depending on the last communication; so if the last communication were succeed then it's value is true otherwise it is false. for more information on that check this.
Use this IsConnected property to check if the tcpClient is connected:
public static bool IsConnected
{
get
{
try
{
//return _tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected;
if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
{
/* As the documentation:
* When passing SelectMode.SelectRead as a parameter to the Poll method it will return
* -either- true if Socket.Listen(Int32) has been called and a connection is pending;
* -or- true if data is available for reading;
* -or- true if the connection has been closed, reset, or terminated;
* otherwise, returns false
*/
// Detect if client disconnected
if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Edit: Note that checking IsConnected will not prevents it from disconnect after the check. i.e its possible to happen that the socket is disconnected just after it was connected "after the IsConnected check evaluated and return true", So you should wrap all communication in try/catch or try/catch/finally block expecting the socket to be disconnected at any time.

Related

C# Server using TcpListener connects the first Client, but second Client timeouts and fails to connect, why?

I tried switching code to following async calls for Server.
I still am getting same issue. First client connects and interacts fine.
The Second client get stuck with SocketException 10060 (Timeout)
Despite switching over to Async calls, second client is still timing out
Any hints or suggestions are welcomed.
Server code
Console.WriteLine("Initialized Server Data");
Console.WriteLine("Starting Server......");
tcpListener = new TcpListener(IPAddress.Any, Port);
tcpListener.Start();
Console.WriteLine($"Server Started on Port: {Port}.");
tcpListener.BeginAcceptTcpClient(ConnectionCallback, null);
ConnectionCallback
TcpClient _client = tcpListener.EndAcceptTcpClient(_result);
//add client to server list
int i = 0;
for (i = 1; i <= MaxPlayers; i++)
{
if (Clients[i].tcp.socket == null)
{
//found empty client slot on server add client
Clients[i].Connect(_client, this);
i = MaxPlayers * 2;
}
}
if (i < MaxPlayers + 2)
{
Program.WriteConsoleLine($"{_client.Client.RemoteEndPoint} failed to connect: Server is full.", ConsoleColor.Red, ConsoleColor.Black);
}
tcpListener.BeginAcceptTcpClient(ConnectionCallback, null);
Clients[i].Connect(...)
public void Connect(TcpClient _socket, Server _actveServer)
{
ActiveServer = _actveServer;
socket = _socket;
socket.ReceiveBufferSize = dataBufferSize;
socket.SendBufferSize = dataBufferSize;
netStream = socket.GetStream();
ReceivedData = new Packet();
receiveBuffer = new byte[dataBufferSize];
Program.WriteConsoleLine($"{_socket.Client.RemoteEndPoint} connect success!!", ConsoleColor.Cyan, ConsoleColor.Black);
Program.WriteConsoleLine($"Sending IntialWelcome as a hello to {_socket.Client.RemoteEndPoint}", ConsoleColor.DarkCyan, ConsoleColor.Black);
ActiveServer.Packer.Send_InitialWelcome(id);
netStream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveDataCallback, null);
}
ReceiveDataCallback
private void ReceiveDataCallback(IAsyncResult _result)
{
try
{
int _byteLen = netStream.EndRead(_result);
if (_byteLen <= 0)
{
ActiveServer.Clients[id].Disconnect();
return;
}
byte[] _data = new byte[_byteLen];
Array.Copy(receiveBuffer, _data, _byteLen);
ReceivedData.Reset(HandleData(_data));
}
catch (Exception _err)
{
Program.WriteConsoleLine($"Error receiving TCP data: {_err}", ConsoleColor.Red, ConsoleColor.Black);
ActiveServer.Clients[id].Disconnect();
}
netStream.BeginRead(receiveBuffer, 0, dataBufferSize, ReceiveDataCallback, null);
}
Send Packet function and callback
public void SendPacket(Packet _packet)
{
try
{
if(socket != null)
{
netStream.BeginWrite(_packet.ToArray(), 0, _packet.Length(), EndPackagingData, netStream);
}
}
catch(Exception err)
{
Program.WriteConsoleLine($"Error sending data to Player {id}, via TCP: {err}", ConsoleColor.Red, ConsoleColor.Black);
}
}
private void EndPackagingData(IAsyncResult _result)
{
netStream.EndWrite(_result);
}

Socket.ReceiveAsync not calling e.Completed event

I'm changing some old sync/threaded code to async,
basically im implementing a 'server emulator' for an old flash game,
and trying to make the packet reading async to hopefully speed things up a little.
private List<byte> currentPacket = new List<byte>();
private byte[] workBuffer = new byte[1028];
private bool dcLock = false;
public GameClient(Socket clientSocket)
{
ClientSocket = clientSocket;
RemoteIp = clientSocket.RemoteEndPoint.ToString();
if (RemoteIp.Contains(":"))
RemoteIp = RemoteIp.Substring(0, RemoteIp.IndexOf(":"));
Logger.DebugPrint("Client connected # " + RemoteIp);
kickTimer = new Timer(new TimerCallback(kickTimerTick), null, kickInterval, kickInterval);
warnTimer = new Timer(new TimerCallback(warnTimerTick), null, warnInterval, warnInterval);
minuteTimer = new Timer(new TimerCallback(minuteTimerTick), null, oneMinute, oneMinute);
connectedClients.Add(this);
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += receivePackets;
e.SetBuffer(workBuffer, 0, workBuffer.Length);
ClientSocket.ReceiveAsync(e);
}
public static void CreateClient(object sender, SocketAsyncEventArgs e)
{
Socket eSocket = e.AcceptSocket;
e.AcceptSocket = null;
socket.AcceptAsync(e);
GameClient client = new GameClient(eSocket);
}
private void receivePackets(object sender, SocketAsyncEventArgs e)
{
// HI1 Packets are terminates by 0x00 so we have to read until we receive that terminator
if (e.SocketError == SocketError.Success && !isDisconnecting)
{
int availble = e.BytesTransferred;
if (availble >= 1)
{
for (int i = 0; i < availble; i++)
{
currentPacket.Add(e.Buffer[i]);
if (e.Buffer[i] == PacketBuilder.PACKET_TERMINATOR)
{
parsePackets(currentPacket.ToArray());
currentPacket.Clear();
}
}
}
Array.Clear(e.Buffer);
ClientSocket.ReceiveAsync(e);
return;
}
else
{
Disconnect();
}
while (dcLock) { }; // Refuse to shut down until dcLock is cleared. (prevents TOCTOU issues.)
// Stop Timers
if (inactivityTimer != null)
inactivityTimer.Dispose();
if (warnTimer != null)
warnTimer.Dispose();
if (kickTimer != null)
kickTimer.Dispose();
// Call OnDisconnect
connectedClients.Remove(this);
GameServer.OnDisconnect(this);
LoggedIn = false;
// Close Socket
ClientSocket.Close();
ClientSocket.Dispose();
return;
}
now you see
e.Completed += receivePackets;
e.SetBuffer(workBuffer, 0, workBuffer.Length);
ClientSocket.ReceiveAsync(e);
for some reason receivePackets is never being called,
if i open up NetCat and connect to 127.0.0.1:12321 and send stuff there it works,
but from in the original flash-based client of the game? nothing happens.
no call to receivePackets ever happens, even if the buffer is only 1 byte.
even after disconnecting the client...
but it worked in my sync implementation where i just called clientSocket.Receive() in a loop.. so why doesnt it work now in async verison?

TCP connections stuck with CLOSE_WAIT state

I need your help guys in order to fix issue with non closing TCP connection.
Basically it works fine but after a few minutes it stucks with connection in CLOSE_WAIT state.
The logic of the code:
The code accepts first packet and parse it and after that it sends the CRC back to the client. If the CRC valid then client sends the main packet to the server and repeat it. Once if there are no packets then client close connection but sometimes it does not. In this case server (code below) close connection after 1 minute of the communications.
I assume it should correspond to https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/TCP-states-explained/ta-p/78462 as well
Here is C# code
void TCPListenerServer(object obj) {
CancellationToken token = (CancellationToken) obj;
if (token.IsCancellationRequested) {
isDisposed = true;
if (listener != null) {
listener.Stop();
}
return;
} else {
try {
isDisposed = false;
try {
var validIP = IPAddress.Parse(Properties.Settings.Default.ServerIP);
listener = new TcpListener(validIP, Properties.Settings.Default.ServerPort);
listener.Start();
while (isDisposed == false || token.IsCancellationRequested == false) {
if (token.IsCancellationRequested || isDisposed) {
break;
} else {
if (!listener.Pending()) {
Thread.Sleep(50);
continue;
}
listener.Server.ReceiveTimeout = 10000;
listener.Server.LingerState = new LingerOption(true, 0);
var client = listener.AcceptTcpClient();
var arrThread = new ThreadParams() {
Client = client, Token = m_Cts.Token
};
var t = new Thread(ProcessClientRequests) {
IsBackground = true,
Name = "ClientConnectionThread",
};
clientThreads.Add(t);
t.Start((object) arrThread);
}
}
} catch (SocketException ex) {
if (ex.SocketErrorCode == SocketError.Interrupted) {}
} catch (Exception ex) {} finally {
if (listener != null) {
listener.Server.Close();
listener.Stop();
}
}
} catch (Exception ex) {
}
}
}
private void ProcessClientRequests(object argument) {
TcpClient client = ((ThreadParams) argument).Client;
CancellationToken token = ((ThreadParams) argument).Token;
client.SendTimeout = 10000;
client.ReceiveTimeout = 10000;
client.LingerState = new LingerOption(true, 0);
var bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
var isFirstPacket = true;
var startTime = DateTime.Now;
DateTime endTime = DateTime.Now;
try {
using(NetworkStream stream = client.GetStream()) {
do {
Thread.Sleep(20);
} while (!stream.DataAvailable);
while ((client != null && client.Connected) && stream != null && stream.CanRead && (endTime - startTime).TotalMinutes < 1) {
if (client == null) {
break;
}
do {
if (token.IsCancellationRequested) {
return;
}
if (client == null) {
break;
}
endTime = DateTime.Now;
int streamReadBytes = 0;
streamReadBytes = stream.Read(buffer, 0, buffer.Length);
if (streamReadBytes == 0) {
if (client != null) {
client.Close();
}
break;
}
if (buffer[0] == (byte) GalileoskyPacketHeaderEnums.FirstPacket || buffer[0] == (byte) GalileoskyPacketHeaderEnums.MainPacket) {
var parserGalileosky = new Galileosky();
var packetResult = parserGalileosky.ParsePacket(buffer, isFirstPacket);
if (packetResult == null) {
if (client != null) {
client.Close();
client = null;
}
break;
}
if (packetResult.Errors.Any()) {
if (client != null) {
client.Close();
client = null;
}
} else {
var imei = packetResult.Packet.IMEI;
if (isFirstPacket) {
isFirstPacket = false;
if (stream.CanWrite == true && packetResult.Packet.IsCrc) {
var answerPacket = packetResult.Packet.GetConfirmPacket();
stream.Write(answerPacket.Ready);
} else {
if (client != null) {
client.Close();
client = null;
}
}
} else // The Main Packet processing
{
// ... Some code to send the main packet to the app queue
if (stream.CanWrite == true && !packetResult.Errors.Any() && packetResult.Packet.IsCrc) {
var answerPacket = packetResult.Packet.GetConfirmPacket();
stream.Write(answerPacket.Ready);
}
if (packetResult.Packet.IsExtraData == false) {
if (client != null) {
client.Close();
client = null;
break;
}
}
}
}
} else {
if (client != null) {
client.Close();
client = null;
}
}
if ((endTime - startTime).TotalMinutes > 1) {
if (client != null) {
client.Close();
client = null;
break;
}
}
}
while ((client != null && client.Connected) && stream != null && stream.CanRead && stream.DataAvailable && (endTime - startTime).TotalMinutes < 1);
}
}
if (client != null) {
client.Close();
client = null;
}
} catch (Exception ex) {} finally {
if (client != null) {
client.Close();
client = null;
}
}
}
Even if your code is correct, you can hit the "2 generals" problem. There is no perfect algorithm for two parties to agree that a connection is closed, when there may be packet loss.
Gracefully closing a TCP stream requires both parties to shutdown sending, read data up to EOF, then close. I believe that you'll see a socket in CLOSE_WAIT, if one party has shutdown the connection, but the other has not. It looks like you have set your sockets to linger. In which case the operating system will take over the lifetime of the socket for you.
If your socket is not set to linger, then closing the socket early will cause data that you think you've sent to be lost.
I should also point out that your code is drastically over complicated, with error handling repeated everywhere. It looks like it was probably written before C# introduced async / await.
It also seems that you are assuming that one read operation equates to one data packet. But this is a TCP stream. The OS is allowed to fragment and recombine data that was written from one end, into any number of data reads at the other.
I'd suggest you search the internet for an example that uses .AcceptTcpClientAsync, .ReadAsync, .WriteAsync etc.
Finally I could'n find a way to solve problem to close connection within my code.
But I added a timer that close connection and it works just awesome!
private void ProcessClientRequests(object argument)
// ... The same code of my quetion
Timer timerCloseConn = new(new TimerCallback((e) =>
{
if (stream != null)
{
stream.Close();
stream.Dispose();
}
if (client != null)
{
if (client.Client.Connected)
{
client.Client.Shutdown(SocketShutdown.Both);
}
client.Close();
client.Dispose();
client = null;
}
Logger.Info("The connection has been closed by
timer's rule!");
}), null, 60000, Timeout.Infinite);
// ... The same code of my quetion
}

Check connection to local Server:Port with waiting time

I'm trying to make a function to test the connection to server:port, it will try connecting to that server:port each 100ms and return TRUE if connection is success either return FALSE until the time limit is reached.
I have a function as bellow:
private static bool _TryPing(string strIpAddress, int intPort, int nTimeoutMsec)
{
Socket socket = null;
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
IAsyncResult result = socket.BeginConnect(strIpAddress, intPort, null, null);
bool success = result.AsyncWaitHandle.WaitOne(nTimeoutMsec, true);
return socket.Connected;
}
catch
{
return false;
}
finally
{
if (null != socket)
socket.Close();
}
}
The problem is i'm using another software to make that local Server:Port, and it will take random 10-20sec to complete setting a new port. But the function above will check at the app starts so it will return false immediately without waiting time because the port is not opened yet.
_TryPing("127.0.0.1", 1080, 20000) //Return FALSE
Could anyone help me to fix this code. Thanks
If the port is not open then the connect will fail immediately. If you want to wait for a period of time to see if the port ever opens, you need to retry the connect in a loop until it is successful or the timeout has elapsed, eg:
private static bool hasElapsed(ref Stopwatch sw, int total)
{
return (sw.ElapsedMilliseconds > (long) total);
}
private static bool hasElapsed(ref Stopwatch sw, int total, out int remaining)
{
remaining = (int) (((long) total) - sw.ElapsedMilliseconds);
return (remaining < 0);
}
private static bool _TryPing(string strIpAddress, int intPort, int nTimeoutMsec)
{
Stopwatch sw = Stopwatch.StartNew();
do
{
try
{
using (TcpClient tcp = new TcpClient())
{
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
IAsyncResult ar = tcp.BeginConnect(strIpAddress, intPort, null, null);
WaitHandle wh = ar.AsyncWaitHandle;
try
{
int remaining;
if (!hasElapsed(ref sw, nTimeoutMsec, out remaining))
{
if (wh.WaitOne(remaining))
{
tcp.EndConnect(ar);
return true;
}
}
tcp.Close();
}
finally
{
wh.Close();
}
}
}
catch
{
}
}
while (!hasElapsed(sw, nTimeoutMsec));
return false;
}
Try this one, i don't know why but i need to devide WaitTime by 3 to make this function run properly...
private static bool TryConnect(string IP, int Port, int WaitTime)
{
int RunEvery = 500;
for (int i = 0; i <= WaitTime/3; i += RunEvery)
{
TcpClient client = new TcpClient();
try
{
client.Connect(IP, Port);
Console.WriteLine(IP + ":" + Port + " is active");
return true;
}
catch(SocketException e)
{
Console.WriteLine("Connection could not be established due to: \n" + e.Message);
Thread.Sleep(RunEvery);
}
finally
{
client.Close();
}
}
return false;
}

No connection could be made because the target machine actively refused it 74.125.25.109:993

Hi I am trying to connect to gmail using ImapX library in C#.
But getting error like this No connection could be made because the target machine actively refused it 74.125.25.109:993 while creating TcpClient (inside ImapX).
I browse few same questions on stackoverflow but none was helpful to me.
Here is my code.
static void Main(string[] args)
{
var client = new ImapClient("imap.gmail.com",993, true);
client.SslProtocol = System.Security.Authentication.SslProtocols.Ssl2;
client.UseSsl = true;
if (client.Connect()) // This method creates new instance of TcpClient which throws error so returning false from catch block method has been described below.
{
if (client.Login("example#gmail.com", "example123"))
{
// login successful
}
Console.WriteLine("Connection Successful...!");
Console.ReadLine();
}
else
{
Console.WriteLine("Connection UnSuccessful...!");
Console.ReadLine();
// connection not successful
}
}
Here is Client method in ImapX library.
public bool Connect(string host, int port, SslProtocols sslProtocol = SslProtocols.None,
bool validateServerCertificate = true)
{
_host = host;
_port = port;
_sslProtocol = sslProtocol;
_validateServerCertificate = validateServerCertificate;
if (IsConnected)
throw new InvalidStateException("The client is already connected. Please disconnect first.");
try
{
_client = new TcpClient(_host, _port);
if (_sslProtocol == SslProtocols.None)
{
_ioStream = _client.GetStream();
_streamReader = new StreamReader(_ioStream);
}
else
{
_ioStream = new SslStream(_client.GetStream(), false, CertificateValidationCallback, null);
(_ioStream as SslStream).AuthenticateAsClient(_host, null, _sslProtocol, false);
_streamReader = new StreamReader(_ioStream);
}
string result = _streamReader.ReadLine();
_lastActivity = DateTime.Now;
if (result != null && result.StartsWith(ResponseType.ServerOk))
{
Capability();
return true;
}
else if (result != null && result.StartsWith(ResponseType.ServerPreAuth))
{
IsAuthenticated = true;
Capability();
return true;
}
else
return false;
}
catch (Exception)
{
return false;
}
finally
{
if (!IsConnected)
CleanUp();
}
}
Thanks in advance.
When you're using ImapX with GMail, the following code is enough to establish the connection:
var client = new ImapClient("imap.gmail.com", true);
if (client.Connect()) {
// ...
}
It will use SSL with the standard 993 port. If you want to specify the SSL version manually, for GMail you need to use SslProtocols.Default, which is equivalent to SslProtocols.Ssl3 | SslProtocols.Tls

Categories