Unity C# tcpclient stream.Read StartCoroutine Problem - c#

I am developing TcpClient with Unity3D.
There's a problem.
if (length == Marshal.SizeOf(typeof(BATTLE_CHARACTER_DATA_GROUP_SEND))+1)
{
BATTLE_CHARACTER_DATA_GROUP_SEND bcdgs = new BATTLE_CHARACTER_DATA_GROUP_SEND();
bcdgs.Deserialize(ref bytes);
Debug.Log("bcdgs data Size : " + bcdgs.data_size);
StartCoroutine(PlayerObjectCreate(bcdgs));
}
If you try to create an object of mainThread in the code above, but you run StartCoroutine, it will send "0" client disconnect value to Server side.
What's wrong with my code?
Below are some full sources.
please.
private void ConnectToTcpServer()
{
try
{
clientReceiveThread = new Thread(new ThreadStart(ListenForData));
clientReceiveThread.IsBackground = true;
clientReceiveThread.Start();
}
catch (Exception e)
{
Debug.Log("On Client Connect Exception " + e);
}
}
private void ListenForData()
{
try
{
socketConnection = new TcpClient("192.168.235.128", 53390);
if (socketConnection.Connected)
{
ServerConnected = true;
Debug.Log("Connected!!!");
}
else
{
ServerConnected = false;
Debug.Log("Not Connected!!!");
}
while (true)
{
using (NetworkStream stream = socketConnection.GetStream())
{
int length;
byte[] bytes = new byte[Marshal.SizeOf(typeof(BATTLE_CHARACTER_DATA_GROUP_SEND))+1];
while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
Debug.Log("length : " + length);
if (length == Marshal.SizeOf(typeof(BATTLE_CHARACTER_DATA_GROUP_SEND))+1)
{
BATTLE_CHARACTER_DATA_GROUP_SEND bcdgs = new BATTLE_CHARACTER_DATA_GROUP_SEND();
bcdgs.Deserialize(ref bytes);
Debug.Log("bcdgs data Size : " + bcdgs.data_size);
StartCoroutine(PlayerObjectCreate(bcdgs));
}
}
}
}
}
catch (SocketException socketException)
{
Debug.Log("Socket Exception : " + socketException.ToString());
}
}

Related

.netcore sockets break while classic .net still works

I have client and server written in classic .net communicating through TCP sockets. I also have load tests with many parallel connections and it all works.
However, same code using .netcore breaks. On linux it breaks all the time with client getting exceptions while trying to read from stream:
Client socket error: Unable to read data from the transport connection: Connection timed out.
Or server might return 0 as bytes to read as well.
On Windows .netcore client breaks less often, but still breaks sometimes with error like:
socket error: Unable to read data from the transport connection: 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
.netcore 3.0 by the way.
Any ideas why that happens?
Client:
public class TcpConnection
{
object _lock = new object();
bool _is_busy = false;
public bool TakeLock()
{
lock (_lock)
{
if (_is_busy)
{
return false;
}
else
{
_is_busy = true;
return true;
}
}
}
public void ReleaseLock()
{
_is_busy = false;
}
public bool Connected { get; set; }
public string ConnError { get; set; }
public Socket client { get; set; }
public Stream stream { get; set; }
public BinaryWriter bw { get; set; }
public DateTime LastUsed { get; set; }
public int Index { get; set; }
public TcpConnection(string hostname, int port)
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();
connectEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(ConnectedEvent);
connectEventArg.UserToken = this;
connectEventArg.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(hostname), port);
var connected = client.ConnectAsync(connectEventArg);
if (!connected)
{
if (connectEventArg.SocketError != SocketError.Success)
{
#if (VERBOSE)
Console.WriteLine("Connection error (immediate)");
#endif
throw new LinqDbException("Linqdb: Connection error (immediate)");
}
#if (VERBOSE)
Console.WriteLine("Connected immediately");
#endif
//client.NoDelay = true;
client.ReceiveTimeout = 60000;
client.SendTimeout = 60000;
this.stream = new NetworkStream(client);
this.bw = new BinaryWriter(stream);
}
else
{
int total_wait_ms = 0;
while (!this.Connected)
{
Thread.Sleep(100);
total_wait_ms += 100;
#if (VERBOSE)
if (total_wait_ms % 2000 == 0)
{
Console.WriteLine("Can't connect in {0} ms", total_wait_ms);
}
#endif
}
if (!string.IsNullOrEmpty(this.ConnError))
{
throw new LinqDbException(this.ConnError + " after " + total_wait_ms + " ms wait time");
}
else
{
#if (VERBOSE)
Console.WriteLine("Connected {0} ms", total_wait_ms);
#endif
}
}
_is_busy = true;
LastUsed = DateTime.Now;
}
private void ConnectedEvent(object sender, SocketAsyncEventArgs e)
{
TcpConnection conn = e.UserToken as TcpConnection;
if (e.SocketError != SocketError.Success)
{
#if (VERBOSE)
Console.WriteLine("Connection error");
#endif
conn.ConnError = "Connection error";
conn.Connected = true;
return;
}
//e.ConnectSocket.NoDelay = true;
e.ConnectSocket.ReceiveTimeout = 60000;
e.ConnectSocket.SendTimeout = 60000;
conn.stream = new NetworkStream(conn.client);
conn.bw = new BinaryWriter(conn.stream);
conn.ConnError = null;
conn.Connected = true;
}
}
public class ClientSockets
{
const int _limit = 100;
TcpConnection[] cons = new TcpConnection[_limit];
object _lock = new object();
object[] _locks = null;
public byte[] CallServer(byte[] input, string hostname, int port, out string error_msg)
{
error_msg = null;
if (_locks == null)
{
lock (_lock)
{
if (_locks == null)
{
_locks = new object[_limit];
for (int i = 0; i < _limit; i++)
{
_locks[i] = new object();
}
}
}
}
TcpConnection conn = null;
while (true)
{
int last_index = 0;
for (int i = _limit - 1; i >= 0; i--)
{
if (cons[i] != null)
{
last_index = i;
break;
}
}
for (int i = 0; i < _limit; i++)
{
var tmp = cons[i];
if (tmp != null)
{
var available = tmp.TakeLock();
if (!available)
{
continue;
}
else
{
if ((DateTime.Now - tmp.LastUsed).TotalSeconds > 30)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
else
{
//ping
tmp.bw.Write(BitConverter.GetBytes(-3));
tmp.bw.Flush();
int numBytesRead = 0;
var data = new byte[1024];
var bad = false;
while (numBytesRead < 4)
{
int read = 0;
try
{
read = tmp.stream.Read(data, numBytesRead, data.Length - numBytesRead);
}
catch (Exception ex)
{
//server closed connection
bad = true;
break;
}
numBytesRead += read;
if (read <= 0)
{
//server closed connection
bad = true;
break;
}
}
if (bad)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
var pong = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (pong != -3)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
//socket is ok
conn = tmp;
break;
}
}
}
else
{
if (i < last_index)
{
continue;
}
if (Monitor.TryEnter(_locks[i]))
{
try
{
if (cons[i] != null)
{
continue;
}
conn = new TcpConnection(hostname, port);
cons[i] = conn;
conn.Index = i;
break;
}
catch (Exception ex)
{
conn = null;
cons[i] = null;
#if (VERBOSE)
Console.WriteLine("Client socket creation error: " + ex.Message);
#endif
error_msg = ex.Message;
return BitConverter.GetBytes(-1);
}
finally
{
Monitor.Exit(_locks[i]);
}
}
else
{
continue;
}
}
}
if (conn == null)
{
Thread.Sleep(150);
continue;
}
else
{
break;
}
}
bool error = false;
try
{
var length = BitConverter.GetBytes(input.Length);
var data = new byte[1024];
conn.bw.Write(input);
conn.bw.Flush();
using (MemoryStream ms = new MemoryStream())
{
int numBytesRead;
int total;
while (true)
{
numBytesRead = 0;
while (numBytesRead < 4)
{
int read = conn.stream.Read(data, numBytesRead, data.Length - numBytesRead);
numBytesRead += read;
if (read <= 0)
{
throw new LinqDbException("Read <= 0: " + read);
}
}
numBytesRead -= 4;
total = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (total == -2)
{
#if (VERBOSE)
Console.WriteLine("PINGER!!!");
#endif
continue;
}
break;
}
if (numBytesRead > 0)
{
var finput = new byte[numBytesRead];
for (int i = 0; i < numBytesRead; i++)
{
finput[i] = data[4 + i];
}
ms.Write(finput, 0, numBytesRead);
}
total -= numBytesRead;
while (total > 0)
{
numBytesRead = conn.stream.Read(data, 0, data.Length);
if (numBytesRead <= 0)
{
throw new LinqDbException("numBytesRead <= 0: " + numBytesRead);
}
ms.Write(data, 0, numBytesRead);
total -= numBytesRead;
}
conn.LastUsed = DateTime.Now;
return ms.ToArray();
}
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Client socket error: " + ex.Message);
#endif
error = true;
error_msg = ex.Message;
return BitConverter.GetBytes(-1);
}
finally
{
if (!error)
{
conn.ReleaseLock();
}
else
{
cons[conn.Index] = null;
try
{
conn.client.Dispose();
conn.stream.Dispose();
conn.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
}
}
}
}
Server:
class Pinger
{
public bool Done { get; set; }
public object _lock = new object();
public BinaryWriter bw { get; set; }
public void Do()
{
try
{
int total_wait = 0;
int sleep_ms = 2000;
while (!Done)
{
Thread.Sleep(sleep_ms);
total_wait += sleep_ms;
if (total_wait % 10000 == 0)
{
lock (_lock)
{
if (!Done)
{
bw.Write(BitConverter.GetBytes(-2));
bw.Flush();
}
}
}
}
}
catch { return; }
}
}
class ServerSockets
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static string db_path = null;
static int port = 0;
public static void Main()
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
CommandHelper.ReadConfig(out db_path, out port);
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("Building in-memory indexes...");
ServerLogic.Logic.ServerBuildIndexesOnStart(db_path);
sw.Stop();
Console.WriteLine("Done building in-memory indexes. It took: " + Math.Round(sw.ElapsedMilliseconds / 60000.0, 0) + " min.");
Console.WriteLine("Listening on " + port);
listener.Bind(new IPEndPoint(IPAddress.Any, port));
listener.Listen((int)SocketOptionName.MaxConnections);
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Service);
bool willRaiseEvent = listener.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
Service(null, acceptEventArg);
}
while (true)
{
try
{
Thread.Sleep(60000);
#if (VERBOSE)
Console.WriteLine("Still kicking...");
#endif
}
catch (Exception ex)
{
Console.WriteLine("BAD ERROR... " + ex.Message);
}
}
}
static void OnProcessExit(object sender, EventArgs e)
{
ServerLogic.Logic.Dispose();
}
private static void LoopToStartAccept()
{
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Service);
bool willRaiseEvent = listener.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
Service(null, acceptEventArg);
}
}
private static void HandleBadAccept(SocketAsyncEventArgs acceptEventArgs)
{
#if (VERBOSE)
Console.WriteLine("bad accept");
#endif
acceptEventArgs.AcceptSocket.Dispose();
}
private static void Service(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
LoopToStartAccept();
HandleBadAccept(e);
return;
}
LoopToStartAccept();
try
{
using (Socket soc = e.AcceptSocket)
{
var rg = new Random();
#if (VERBOSE)
Console.WriteLine("New socket: " + rg.Next(0, 1000000));
#endif
//soc.NoDelay = true;
soc.ReceiveTimeout = 60000;
soc.SendTimeout = 60000;
using (Stream stream = new NetworkStream(soc))
using (BinaryWriter bw = new BinaryWriter(stream))
{
while (true) //reuse same connection for many commands
{
byte[] data = new byte[1024];
using (MemoryStream ms = new MemoryStream())
{
int numBytesRead = 0;
while (numBytesRead < 4)
{
int read = 0;
try
{
read = stream.Read(data, numBytesRead, data.Length - numBytesRead);
}
catch (Exception ex)
{
//client closed connection
return;
}
numBytesRead += read;
if (read <= 0)
{
//throw new Exception("Read <= 0: " + read);
//client closed connection
return;
}
}
numBytesRead -= 4;
var total = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (total == -3) //ping
{
//pong
bw.Write(BitConverter.GetBytes(-3));
bw.Flush();
continue;
}
if (numBytesRead > 0)
{
var finput = new byte[numBytesRead];
for (int i = 0; i < numBytesRead; i++)
{
finput[i] = data[4 + i];
}
ms.Write(finput, 0, numBytesRead);
}
total -= numBytesRead;
while (total > 0)
{
numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead <= 0)
{
throw new Exception("numBytesRead <= 0: " + numBytesRead);
}
ms.Write(data, 0, numBytesRead);
total -= numBytesRead;
}
var input = ms.ToArray();
var pinger = new Pinger()
{
bw = bw
};
ThreadPool.QueueUserWorkItem(f => { pinger.Do(); });
var output = ServerLogic.Logic.Execute(input, db_path);
pinger.Done = true;
lock (pinger._lock)
{
bw.Write(output);
bw.Flush();
}
}
}
}
}
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Socket error: " + ex.Message);
#endif
//try
//{
// var rg = new Random();
// File.WriteAllText("sock_error_" + rg.Next() + ".txt", ex.Message + " " + ex.StackTrace + (ex.InnerException != null ? (" " + ex.InnerException.Message + " " + ex.InnerException.StackTrace) : ""));
//}
//catch (Exception) { }
return;
}
finally
{
#if (VERBOSE)
Console.WriteLine("Listener finally ");
#endif
}
}
}
EDIT
Fully reproducible project: https://github.com/ren85/serverclientbug
EDIT
No solution still, 500 more points to whoever can fix this.
EDIT
Maybe related
https://github.com/dotnet/coreclr/issues/11979
https://github.com/dotnet/runtime/issues/364
I debugged your code and looks like the problem are bugs not issues with sockets in .NET core. From the code it looks you expect that the first four bytes of the data sent will contain the length of the data but you send only the data. This causes that you get a random data length since the first four bytes of the data are used as the length. This in some cases is bigger than the actual data and then the while loop that is reading the data timeouts on waiting for more data that never arrives.
Here is the problematic part of the code in the client:
var length = BitConverter.GetBytes(input.Length); // You prepare the length
var data = new byte[1024];
conn.bw.Write(length); // This is missing in your code so it never gets sent to the server
conn.bw.Write(input);
conn.bw.Flush();
Full client with the fix included:
public class ClientSockets
{
const int _limit = 100;
TcpConnection[] cons = new TcpConnection[_limit];
object _lock = new object();
object[] _locks = null;
public byte[] CallServer(byte[] input, string hostname, int port, out string error_msg)
{
error_msg = null;
if (_locks == null)
{
lock (_lock)
{
if (_locks == null)
{
_locks = new object[_limit];
for (int i = 0; i < _limit; i++)
{
_locks[i] = new object();
}
}
}
}
TcpConnection conn = null;
while (true)
{
int last_index = 0;
for (int i = _limit - 1; i >= 0; i--)
{
if (cons[i] != null)
{
last_index = i;
break;
}
}
for (int i = 0; i < _limit; i++)
{
var tmp = cons[i];
if (tmp != null)
{
var available = tmp.TakeLock();
if (!available)
{
continue;
}
else
{
if ((DateTime.Now - tmp.LastUsed).TotalSeconds > 30)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
else
{
//ping
tmp.bw.Write(BitConverter.GetBytes(-3));
tmp.bw.Flush();
int numBytesRead = 0;
var data = new byte[1024];
var bad = false;
while (numBytesRead < 4)
{
int read = 0;
try
{
read = tmp.stream.Read(data, numBytesRead, data.Length - numBytesRead);
}
catch (Exception ex)
{
//server closed connection
bad = true;
break;
}
numBytesRead += read;
if (read <= 0)
{
//server closed connection
bad = true;
break;
}
}
if (bad)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
var pong = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (pong != -3)
{
cons[i] = null;
try
{
tmp.client.Dispose();
tmp.stream.Dispose();
tmp.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
continue;
}
//socket is ok
conn = tmp;
break;
}
}
}
else
{
if (i < last_index)
{
continue;
}
if (Monitor.TryEnter(_locks[i]))
{
try
{
if (cons[i] != null)
{
continue;
}
conn = new TcpConnection(hostname, port);
cons[i] = conn;
conn.Index = i;
break;
}
catch (Exception ex)
{
conn = null;
cons[i] = null;
#if (VERBOSE)
Console.WriteLine("Client socket creation error: " + ex.Message);
#endif
error_msg = ex.Message;
return BitConverter.GetBytes(-1);
}
finally
{
Monitor.Exit(_locks[i]);
}
}
else
{
continue;
}
}
}
if (conn == null)
{
Thread.Sleep(150);
continue;
}
else
{
break;
}
}
bool error = false;
try
{
var length = BitConverter.GetBytes(input.Length);
var data = new byte[1024];
conn.bw.Write(length); // Send the length first.
conn.bw.Write(input);
conn.bw.Flush();
using (MemoryStream ms = new MemoryStream())
{
int numBytesRead;
int total;
while (true)
{
numBytesRead = 0;
while (numBytesRead < 4)
{
int read = conn.stream.Read(data, numBytesRead, data.Length - numBytesRead);
numBytesRead += read;
if (read <= 0)
{
throw new LinqDbException("Read <= 0: " + read);
}
}
numBytesRead -= 4;
total = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (total == -2)
{
#if (VERBOSE)
Console.WriteLine("PINGER!!!");
#endif
continue;
}
break;
}
if (numBytesRead > 0)
{
var finput = new byte[numBytesRead];
for (int i = 0; i < numBytesRead; i++)
{
finput[i] = data[4 + i];
}
ms.Write(finput, 0, numBytesRead);
}
total -= numBytesRead;
while (total > 0)
{
numBytesRead = conn.stream.Read(data, 0, data.Length);
if (numBytesRead <= 0)
{
throw new LinqDbException("numBytesRead <= 0: " + numBytesRead);
}
ms.Write(data, 0, numBytesRead);
total -= numBytesRead;
}
conn.LastUsed = DateTime.Now;
return ms.ToArray();
}
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Client socket error: " + ex.Message);
#endif
error = true;
error_msg = ex.Message;
return BitConverter.GetBytes(-1);
}
finally
{
if (!error)
{
conn.ReleaseLock();
}
else
{
cons[conn.Index] = null;
try
{
conn.client.Dispose();
conn.stream.Dispose();
conn.bw.Dispose();
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Disposing error:" + ex.Message);
#endif
}
}
}
}
}
The same issue is also in the server code:
var output = ServerLogic.Logic.Execute(input, db_path);
var length = BitConverter.GetBytes(output.Length); // You again need to get the length
pinger.Done = true;
lock (pinger._lock)
{
bw.Write(length); // Send it before the data
bw.Write(output);
bw.Flush();
}
Full Server with the fix included:
class ServerSockets
{
static Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static string db_path = null;
static int port = 0;
public static void Main()
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
CommandHelper.ReadConfig(out db_path, out port);
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("Building in-memory indexes...");
ServerLogic.Logic.ServerBuildIndexesOnStart(db_path);
sw.Stop();
Console.WriteLine("Done building in-memory indexes. It took: " + Math.Round(sw.ElapsedMilliseconds / 60000.0, 0) + " min.");
Console.WriteLine("Listening on " + port);
listener.Bind(new IPEndPoint(IPAddress.Any, port));
listener.Listen((int)SocketOptionName.MaxConnections);
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Service);
bool willRaiseEvent = listener.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
Service(null, acceptEventArg);
}
while (true)
{
try
{
Thread.Sleep(60000);
#if (VERBOSE)
Console.WriteLine("Still kicking...");
#endif
}
catch (Exception ex)
{
Console.WriteLine("BAD ERROR... " + ex.Message);
}
}
}
static void OnProcessExit(object sender, EventArgs e)
{
ServerLogic.Logic.Dispose();
}
private static void LoopToStartAccept()
{
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Service);
bool willRaiseEvent = listener.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
Service(null, acceptEventArg);
}
}
private static void HandleBadAccept(SocketAsyncEventArgs acceptEventArgs)
{
#if (VERBOSE)
Console.WriteLine("bad accept");
#endif
acceptEventArgs.AcceptSocket.Dispose();
}
private static void Service(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
LoopToStartAccept();
HandleBadAccept(e);
return;
}
LoopToStartAccept();
try
{
using (Socket soc = e.AcceptSocket)
{
var rg = new Random();
#if (VERBOSE)
Console.WriteLine("New socket: " + rg.Next(0, 1000000));
#endif
//soc.NoDelay = true;
soc.ReceiveTimeout = 60000;
soc.SendTimeout = 60000;
using (Stream stream = new NetworkStream(soc))
using (BinaryWriter bw = new BinaryWriter(stream))
{
while (true) //reuse same connection for many commands
{
byte[] data = new byte[1024];
using (MemoryStream ms = new MemoryStream())
{
int numBytesRead = 0;
while (numBytesRead < 4)
{
int read = 0;
try
{
read = stream.Read(data, numBytesRead, data.Length - numBytesRead);
}
catch (Exception ex)
{
//client closed connection
return;
}
numBytesRead += read;
if (read <= 0)
{
//throw new Exception("Read <= 0: " + read);
//client closed connection
return;
}
}
numBytesRead -= 4;
var total = BitConverter.ToInt32(new byte[4] { data[0], data[1], data[2], data[3] }, 0);
if (total == -3) //ping
{
//pong
bw.Write(BitConverter.GetBytes(-3));
bw.Flush();
continue;
}
if (numBytesRead > 0)
{
var finput = new byte[numBytesRead];
for (int i = 0; i < numBytesRead; i++)
{
finput[i] = data[4 + i];
}
ms.Write(finput, 0, numBytesRead);
}
total -= numBytesRead;
while (total > 0)
{
numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead <= 0)
{
throw new Exception("numBytesRead <= 0: " + numBytesRead);
}
ms.Write(data, 0, numBytesRead);
total -= numBytesRead;
}
var input = ms.ToArray();
var pinger = new Pinger()
{
bw = bw
};
ThreadPool.QueueUserWorkItem(f => { pinger.Do(); });
var output = ServerLogic.Logic.Execute(input, db_path);
var length = BitConverter.GetBytes(output.Length);
pinger.Done = true;
lock (pinger._lock)
{
bw.Write(length);
bw.Write(output);
bw.Flush();
}
}
}
}
}
}
catch (Exception ex)
{
#if (VERBOSE)
Console.WriteLine("Socket error: " + ex.Message);
#endif
//try
//{
// var rg = new Random();
// File.WriteAllText("sock_error_" + rg.Next() + ".txt", ex.Message + " " + ex.StackTrace + (ex.InnerException != null ? (" " + ex.InnerException.Message + " " + ex.InnerException.StackTrace) : ""));
//}
//catch (Exception) { }
return;
}
finally
{
#if (VERBOSE)
Console.WriteLine("Listener finally ");
#endif
}
}
}
For me with this two changes the code works in Windows and Linux. If you still have an issue you will need to provide also more details what data you are sending.
I did some more research on Linux after you posted your GitHub sample. As I already suggested in the comment below you should use the Connect method instead of the ConnectAsync to get better behavior. In my test on Raspbian and Arch Linux once you open lots of threads in the client you probably occupy all the ThreadPool so the Connect events stop happening and everything timeouts. Since your code loops on threads and waits you don't gain much by using the Async method anyway.
Here is the changed TcpConnection class to do synchronous connections:
public class TcpConnection
{
object _lock = new object();
bool _is_busy = false;
public bool TakeLock()
{
lock (_lock)
{
if (_is_busy)
{
return false;
}
else
{
_is_busy = true;
return true;
}
}
}
public void ReleaseLock()
{
_is_busy = false;
}
public bool Connected { get; set; }
public string ConnError { get; set; }
public Socket client { get; set; }
public Stream stream { get; set; }
public BinaryWriter bw { get; set; }
public DateTime LastUsed { get; set; }
public int Index { get; set; }
public TcpConnection(string hostname, int port)
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(new IPEndPoint(IPAddress.Parse(hostname), port));
if (client.Connected)
{
#if (VERBOSE)
Console.WriteLine("Connected immediately");
#endif
//client.NoDelay = true;
client.ReceiveTimeout = 60000;
client.SendTimeout = 60000;
this.stream = new NetworkStream(client);
this.bw = new BinaryWriter(stream);
}
_is_busy = true;
LastUsed = DateTime.Now;
}
}
As a final comment I have to say your code is very complicated and does not follow best practices as already other people pointed out. I advise you to learn more about about multi threading and asynchronous programming and then improve the code which will also make it work better and more predictable.
Here are the links to the Microsoft samples on how to work with sockets asynchronously and the general documentation about how to do Async programming:
Asynchronous Client Socket Example
Asynchronous Server Socket Example
Parallel Processing, Concurrency, and Async Programming in .NET
Let me start off with saying that the code is incredibly hard to understand. Not only does a lot of things happen within a method, you're also dealing with threads and (a lot of) locks. This makes things incredibly complex and very hard to pinpoint whats going on.
Its like trying to find a needle in the haystack. Except a hundred times worse because of timeouts, threads and locking mechanisms that constantly shift the needle.
I've ran this project on a Windows 10 machine, with Visual Studio 2019 Preview (16.4.0 preview 2.0) and .NET Core runtime 3.1.0-preview1 (target framework being 3.0).
Let me state one thing though, you have set the receive/set timeout for both the client and server to 60000. I cannot reproduce your error using this. Because this timeout number is so big, everything eventually gets finished before a timeout can even occur.
In order to reproduce your errors I have downscaled the timeout considerably:
// File: ClientLib.cs
// Method: "ConnectedEvent(...)"
// Line 114-115
e.ConnectSocket.ReceiveTimeout = 1000;
// Lowering it even more multiplies the errors.
The above now gives me about ~5 errors, which then again simply happens because of this code (where the Pinger is instantiated):
// File: Server\Program.cs
// Method: "Service(...)"
Thread.Sleep(random.Next(100, 1000));
where also the following happens just above:
ThreadPool.QueueUserWorkItem(f => { pinger.Do(); });
The server uses the ThreadPool and it can handle 8 operations1 concurrently on it. Not only are you using the ThreadPool when a Socket connection is established, but also when you do a ThreadPool.QueueUserWorkItem(...).
This means big trouble when the client starts trying to establish a huge amount of connections. As this can cause timeouts too.
One way of increasing the threads in the ThreadPool:
public static void Main()
{
int w, c = 0;
ThreadPool.GetMinThreads(out w, out c);
Console.WriteLine($"ThreadPool max: {w} worker threads {c} completion threads");
ThreadPool.SetMinThreads(100, 100);
ThreadPool.GetMinThreads(out w, out c);
Console.WriteLine($"ThreadPool max: {w} worker threads {c} completion threads");
/// other code...
}
But really, just removing these two lines solves the problem:
var random = new Random();
Thread.Sleep(random.Next(100, 1000));
1 This can differ per machine, see the documentation. You can check it on your machine by using ThreadPool.GetMinThreads(...).
Luckily the problem reproduced with just 2 parallel threads. The problem was in server part:
private static void LoopToStartAccept()
{
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(Service);
bool willRaiseEvent = listener.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
Service(null, acceptEventArg);
}
}
Service(null, acceptEventArg); blocked responding socket. Changing it to
Task.Run(() => Service(null, acceptEventArg));
fixed the problem. No idea why it worked on Windows though.
Changing connection in client to synchronous as #AlesD suggested also solved some other problems.

How to transfer the data from Chrome Browser Console onto C# file

We are getting joint data in JSON Objects format. The data is streaming live on the Chrome Console as you can see in the picture.
How do we take this data (from the Chrome Console) and send it over to a C# file on the computer in real-time?
From Console: JSON Object data from kinectv2
Here is some basic c# websocket code, it doesnt have to convert the data it receives to a string. I was just testing some stuff
C# simple websocket connection
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
public partial class MainWindow : Window
{
private Websocket_listen _listener;
public MainWindow()
{
_listener = new Websocket_listen("127.0.0.1", 13000);
_listener.StringReceived += _listener_StringReceived;
_listener.Start();
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_running = false;
_listener.Stop();
}
private void _listener_StringReceived(string received)
{
}
}
public class Websocket_listen
{
public readonly int port = 13000;
public readonly IPAddress localAddr;
private TcpListener server = null;
private TcpClient client = null;
private NetworkStream stream = null;
private bool connected = false;
private byte[] bytes = new byte[2048];
private byte[] _last_key = new byte[4];
private int _last_message_length;
private bool read_more_message;
private Thread _current_thread;
private bool _running = true;
private int _received = 0;
private StringBuilder _results = new StringBuilder();
public event Action<string> StringReceived = null;
public Websocket_listen(string ipaddress, int port)
{
this.port = port;
localAddr = IPAddress.Parse(ipaddress);
server = new TcpListener(localAddr, port);
}
public void _running_loop()
{
while (_running)
{
try
{
server.Server.ReceiveTimeout = 5000;
server.Server.SendTimeout = 5000;
client = server.AcceptTcpClient();
// Get a stream object for reading and writing
stream = client.GetStream();
}
catch (Exception ex)
{
continue;
}
while (_running)
{
try
{
try
{
l.AcquireWriterLock(-1);
if (messsages.Count > 0)
{
byte[] msg = System.Text.Encoding.ASCII.GetBytes(messsages[0]);
//byte[] msg = System.Text.Encoding.ASCII.GetBytes("Connected to " + Environment.MachineName);
Array.Copy(msg, 0, bytes, 2, msg.Length);
bytes[0] = 0x81;
bytes[1] = (byte)msg.Length;
// Send back a response.
stream.Write(bytes, 0, msg.Length + 2);
messsages.RemoveAt(0);
}
}
finally
{
l.ReleaseWriterLock();
}
}
catch { }
try
{
_received = stream.Read(bytes, 0, bytes.Length);
}
catch
{
continue;
}
if (_received == 0)
continue;
if (!connected)
{
_is_connection();
continue;
}
if (!_parse_message())
break;
}
}
try
{
stream.Close();
client.Close();
}
catch (Exception ex)
{
}
}
private bool _parse_message()
{
int offset = 0;
int message_length = 0;
if (read_more_message)
{
_last_message_length -= bytes.Length;
message_length = _last_message_length;
if (message_length < bytes.Length)
message_length += 8;
}
else
{
_results.Clear();
var trigger = bytes[0];
var magic_byte = bytes[1];
bool is_text = (0x1 & trigger) != 0;
bool is_fin = (0x80 & trigger) != 0;
if (trigger == 0x88)
{
connected = false;
return false;
}
/*
text = 0x81
binary = 0x82
close 0x88
ping 0x89
pong -0x8A
*/
if (!is_fin)
{
return true;
}
if (!is_text)
{
return true;
}
//If the second byte minus 128 is between 0 and 125, this is the length of message.
//If it is 126, the following 2 bytes (16-bit unsigned integer), if 127, the following 8 bytes (64-bit unsigned integer) are the length.
var r = magic_byte - 128;
var key_starts_at = 0;
if (r <= 125)
{
key_starts_at = 2;
message_length = r;
}
else if (r == 126)
{
key_starts_at = 4;
message_length = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
}
else if (r == 127)
{
key_starts_at = 10;
for (var m = 7; m >= 0; --m)
{
message_length += bytes[m] << (8 * (7 - m));
}
}
else
{
// not documented
}
//// because its encoded
_last_message_length = message_length;
Array.Copy(bytes, key_starts_at, _last_key, 0, 4);
offset = key_starts_at + 4;
}
for (var mx = 0; mx < message_length && offset + mx < bytes.Length; ++mx)
{
bytes[offset + mx] = (byte)(bytes[offset + mx] ^ _last_key[mx % 4]);
}
var new_result = System.Text.Encoding.ASCII.GetString(bytes, offset, Math.Min(message_length, bytes.Length - offset));
_results.Append(new_result);
read_more_message = message_length > bytes.Length;
if (!read_more_message)
{
try
{
StringReceived?.Invoke(_results.ToString());
}
catch (Exception ex)
{
}
}
return true;
}
private void _is_connection()
{
try
{
string data = System.Text.Encoding.ASCII.GetString(bytes, 0, _received);
if (!new Regex("^GET").IsMatch(data))
return;
Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+ "Connection: Upgrade" + Environment.NewLine
+ "Upgrade: websocket" + Environment.NewLine
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
SHA1.Create().ComputeHash(
Encoding.UTF8.GetBytes(
new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
)
)
) + Environment.NewLine
+ Environment.NewLine);
stream.Write(response, 0, response.Length);
connected = true;
}
catch (Exception ex)
{
}
}
public void Stop()
{
_running = false;
}
public void Start()
{
// Start listening for client requests.
server.Start();
_current_thread = new Thread(new ThreadStart(_running_loop));
_current_thread.Start();
}
ReaderWriterLock l = new ReaderWriterLock();
List<string> messsages = new List<string>();
internal void Send(string msg)
{
try
{
l.AcquireWriterLock(-1);
messsages.Add(msg);
}
catch
{
}
finally
{
l.ReleaseWriterLock();
}
}
}
not a very robust javascript simple websocket to connect to c# with the same port
try {
var isopen = false;
connection = new WebSocket('ws://127.0.0.1:13000');
// When the connection is open, send some data to the server
connection.onopen = function () {
isopen = true;
connection.send('Ping'); // Send the message 'Ping' to the server
$('#open_messages').html("connection made\r\n<br>");
$('#socket_errors').html("");
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ', error);
$('#socket_errors').html(error.currentTarget.url + " failed\r\n<br><button id=\"reconnect\">Try to connect</button>");
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ', e);
$('#messages').append(e.data + "\r\n<br>");
};
connection.onclose = function(x) {
console.log('closed ', x);
$('#open_messages').html("Disconnected\r\n<br>");
$('#socket_errors').html("<button id=\"reconnect\">Connect</button>");
};
} catch (err) {
console.log(err);
}
That should get you started sending data back and forth, but will probably need extra exception handling.
Not sure if sending strings across is efficient, by you could probably change the data later.

C#, tcpClient to Multiple IP Address but on the same port

Scenario:
I have multiple machines, each wit hit's own IP, that run a tcpServer, all on the same port. During each transaction data is sent if the tcpServer port is opened. It, looks for a client before opening the port.
What I Need:
I'm writing an app, that needs to:
1. Check through the list of IP's if they are availible
2. Open a tcpClient port to each machine IP, (all on the same ports)
3. After every specified time make sure the connection is open, if not reopen connection.
Future:
I use an XMl file to give the SQL data which is used to get the list of machines and the port to listen on. In this xml is a udp setting as well, if this is yes, then the data being sent, must be received and redirected to a specified UDP port.
The Class:
public static void tcpListen(IPAddress server, int port)
{
try
{
TcpListener listener = new TcpListener(server, port);
listener.AcceptTcpClient();
listener.Start();
while (true)
{
Socket client = listener.AcceptSocket();
var childSocketThread = new Thread(() =>
{
byte[] data = new byte[100];
int size = client.Receive(data);
for (int i = 0; i < size; i++)
feedback = server + ":" + port + ": " + Convert.ToChar(data[i]);
using (StreamWriter w = File.AppendText("TCPLog.log"))
{
Log(feedback, w);
}
client.Close();
});
childSocketThread.Start();
}
}
catch (Exception err)
{
using (StreamWriter w = File.AppendText("error.log"))
{
Log("tcpControl.tcpListen: " + err.Message, w);
}
}
}
The part of the code that repeats every few seconds:
private void ptoCheck()
{
IPAddress sourceIP;
int sourcePort;
int destinationPort;
string tcpStatus;
int oldCOunt = dgvWorkstations.RowCount;
int newCount = sqlScripts.posCount(sqlServer, sqlUser, sqlPassword, sqlDatabase);
if (newCount != oldCOunt)
{
getWorkstations();
}
try
{
foreach (DataGridViewRow row in dgvWorkstations.Rows)
{
int i = row.Index;
bool tcpState;
dgvWorkstations["Status", i].Value = "Checking";
dgvWorkstations.Refresh();
name = row.Cells["POS_Name"].Value.ToString();
sourceIP = IPAddress.Parse(row.Cells["LastKnownIP"].Value.ToString());
sourcePort = Convert.ToInt32(row.Cells["Receive_Port"].Value.ToString());
destinationPort = Convert.ToInt32(row.Cells["Send_Port"].Value.ToString());
tcpState = tcpControl.tcpCheck(sourceIP, sourcePort, name);
if (tcpState == false)
{
dgvWorkstations["Status", i].Value = "Connecting";
dgvWorkstations.Refresh();
tcpStatus = tcpControl.tcpConnect(sourceIP, sourcePort, name);
tcpControl.tcpListen(sourceIP, sourcePort);
dgvWorkstations["Status", i].Value = tcpStatus;
}
if (tcpState == true)
{
dgvWorkstations["Status", i].Value = "Connected";
dgvWorkstations.Refresh();
}
i = i + 1;
}
}
catch (Exception err)
{
using (StreamWriter w = File.AppendText("AError.log"))
{
Log("frmMain.ptoCheck: (" + name + ") " + err.Message, w);
}
}//End Catch
}
I got the following code toe work, from my class I can now make multiple connections, however. The part where I call another method to listen and receive the data blocks the first method and holds it untill the connection drops without making further connections.
My Class:
public void tcpTest2(IPAddress server, Int32 port, int x)
{
TcpClient client;
sendData("Connecting to Host: " + server + " on port: " + port.ToString() + "...");
sendStatus("Connecting", x);
try
{
client = new TcpClient(server.ToString(), port);
if (false)
{
}
if (true)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..ESTABLISHED");
sendStatus("Connected", x);
receiveData(client, server, port);
}
}
catch (Exception)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..FAILED");
sendStatus("Failed", x);
}
}
public void receiveData(TcpClient client, IPAddress server, int port)
{
Byte[] data = System.Text.Encoding.Default.GetBytes("|");
data = new byte[1024];
string stringData;
bool connected;
connected = true;
while (connected == true)
{
string fromC = client.Client.RemoteEndPoint.ToString();
NetworkStream ns = client.GetStream();
int recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
sendUpdate("{" + fromC + "}" + stringData);
connected = IsConnected(client);
}
if (connected == false)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..LOST");
sendLost(server);
}
}
public bool IsConnected(TcpClient client)
{
try
{
if (client != null && client.Client != null && client.Client.Connected)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (client.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Here is the final code that worked for me:
My Class:
public void tcpConnect(object pos)
{
IPAddress hostIP = IPAddress.Parse(pos.ToString().Split(':')[0]);
int hostPort = Int32.Parse(pos.ToString().Split(':')[1]);
rowInd = Int32.Parse(pos.ToString().Split(':')[2]);
var client = new TcpClient();
if (!client.ConnectAsync(hostIP, hostPort).Wait(1000))
{
sendData("Connection to Host: " + hostIP + " on port: " + hostPort.ToString() + ".FAILED");
sendStatus("Failed", "", rowInd);
return;
}
if (true)
{
sendData("Connection to Host: " + hostIP.ToString() + " on port: " + hostPort.ToString() + "..ESTABLISHED");
Thread thread = new Thread(new ParameterizedThreadStart(ClientHandler));
thread.IsBackground = true;
Thread.FreeNamedDataSlot(hostIP.ToString() + rowInd.ToString());
thread.Name = hostIP.ToString() + rowInd.ToString();
thread.Start(client);
threadID = thread.ManagedThreadId.ToString();
sendStatus("Connected", threadID, rowInd);
}
}
public bool IsConnected(TcpClient client)
{
try
{
if (client != null && client.Client != null && client.Client.Connected)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (client.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
public void ClientHandler(object c)
{
TcpClient client = (TcpClient)c;
NetworkStream netstream = client.GetStream();
string fromC = client.Client.RemoteEndPoint.ToString();
string fromIP = fromC.Split(':')[0];
bool connected = true;
while (connected)
{
Thread.Sleep(10);
try
{
byte[] data = new byte[client.ReceiveBufferSize];
data = System.Text.Encoding.Default.GetBytes("|");
data = new byte[1024];
string stringData;
NetworkStream ns = client.GetStream();
int recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
sendUpdate("|" + fromC + "|" + stringData);
connected = IsConnected(client);
}
catch (Exception err)
{
connected = false;
sendLost(fromIP);
using (StreamWriter w = File.AppendText("Arch-PTO.log"))
{
Log("tcpServices.ClientHandler: " + err.Message, w);
}
}
}
sendLost(fromIP);
}

Mono HTTPListener throws exception when using self signed SSL certificate

I wrote a little web server on Linux using the Mono HTTPListener class. It works fine for http requests. However, if I use a self-signed SSL certificate (created with openssl and installed with httpcfg) it will throw an un-catchable exception as soon as the request forom a browser comes in.
The exception is:
Unhandled Exception:
System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: The client stopped the handshake.
at Mono.Security.Protocol.Tls.SslServerStream.EndNegotiateHandshake (IAsyncResult asyncResult) <0xb4b079c8 + 0x001cf> in <filename unknown>:0
at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) <0xb4b07428 + 0x0005f> in <filename unknown>:0
Here is the complete code:
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Threading;
namespace SSLTest
{
class MainClass
{
static void Main ()
{
try
{
HttpListener l = new HttpListener ();
l.Prefixes.Add ("https://*:8443/");
l.Start ();
Console.WriteLine("Server is running.");
while (l.IsListening)
{
//create the worker thread
HttpListenerContext ctx = l.GetContext(); //.GetContext() blocks until something comes in
if(ctx != null)
{
if(ctx.Request.RemoteEndPoint != null)
{
Thread workerThread = new Thread(() => RunWorker(ctx));
workerThread.Start();
}
}
}
Console.WriteLine("Server is stopped.");
}
catch(Exception ex)
{
Console.WriteLine ("Exception in Main: " + ex);
}
}
static void RunWorker(HttpListenerContext ctx)
{
try
{
if(ctx.Request != null)
{
if(ctx.Request.RemoteEndPoint != null)
{
Console.WriteLine ("Got request from " + ctx.Request.RemoteEndPoint.ToString());
string rstr = "Test Website!\n" + DateTime.Now.ToString();
byte[] buf = Encoding.UTF8.GetBytes(rstr);
if(buf!=null)
{
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
}
}
}
}
catch(Exception ex)
{
Console.WriteLine ("#Exception in RunWorker: " + ex.Message);
}
}
}
}
This is the case when I am using a browser for the first time. The browser will show something like "Unsafe certificate! Do you want to continue (not recommended)?". If I click on Yes and restart the crashed server app it will work from that moment on.
How can I fix this?
Also, I am not able to catch this exception with a try block. It will always terminate my application. How can I prevent that?
Should be fixed by an unreleased bug fix https://bugzilla.xamarin.com/show_bug.cgi?id=52675... although I have not had a chance to test.
I have seen this as well and am currently trying to find a way to handle the exception. This looks to be a bug in Mono.
It occurs when certificate verification fails in any way.
I obtained a CA signed cert which fixed the problem as long as the certificate common name (dns) is the same as the dns used in the URL I'm trying to send a get request to. If I instead specify the public IP address in the url (which the cert is not registered with) the mono app will crash with an unhandled exception.
One option we're considering is implementing a TCP based webserver which would use a TcpListener instead of the heavy HttpListener which would in turn fix other problems we're seeing with mono httplistener prefixes not working correctly when bound to the internal IP behind a NAT. This would mean the cert could be bound pro-grammatically as well, it just takes more work.
Below is a very rough version of what this might look like. By no means is this a finished product but it may serve to help others on this same path... I'll have to do this in two answers, this is the tcp web server.
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
namespace Example.Lib.Net
{
internal class TcpWebServer : IDisposable
{
private TcpListener m_Listener = null;
private bool m_IsSSL = false;
private X509Certificate2 m_ServerCertificate = null;
internal X509Certificate2 ServerCertificate
{
get { return m_ServerCertificate; }
set { m_ServerCertificate = value; }
}
internal void Start(string ip, int port, bool useSsl = false)
{
if (useSsl) // for player streams always use ssl to
{
m_IsSSL = true;
m_ServerCertificate = new X509Certificate2("./cert/cert.pfx", "pass");
X509Store store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(m_ServerCertificate);
store.Close();
}
IPAddress ipAddr = IPAddress.Any;
if (ip != "*") IPAddress.TryParse(ip, out ipAddr);
try
{
m_Listener = new TcpListener(ipAddr, port);
m_Listener.Start();
m_Listener.BeginAcceptTcpClient(OnClientAccepted, m_Listener);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private void OnClientAccepted(IAsyncResult ar)
{
TcpListener listener = ar.AsyncState as TcpListener;
if (listener == null)
return;
TcpClient client = listener.EndAcceptTcpClient(ar);
client.ReceiveBufferSize = 65535;
client.Client.ReceiveBufferSize = 65535;
TcpWebConnection con = new TcpWebConnection(client, this, m_IsSSL);
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
}
}
}
This is the tcp web connection code.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Web;
using Example.Lib.Common;
using Example.Lib.Models;
namespace Example.Lib.Net
{
internal enum RequestType
{
None = 0,
GET = 1,
POST = 2,
OPTIONS = 3
}
internal class TcpWebConnection : IDisposable
{
#region private members
private bool m_IsDisposed = false;
private bool m_IsSSL = false;
private bool m_HasHeaders = false;
private bool m_FileCreated = false;
private bool m_IsFileUpload = false;
private RequestType m_RequestType = RequestType.None;
private string m_ReadData = string.Empty;
private string m_Request = string.Empty;
private string m_RemoteIP = string.Empty;
private string m_AbsoluteURI = string.Empty;
private string m_ContentType = string.Empty;
private string m_TempFilename = string.Empty;
private byte[] m_EndBoundaryBytes = null;
private byte[] m_StartBoundaryBytes = null;
private int m_ContentLength = 0;
private long m_StartBoundaryIndex = -1;
private long m_EndBoundaryIndex = -1;
private long m_BytesRead = 0;
private NameValueCollection m_QueryString = null;
private string[] m_Segments = new string[1];
private string m_HttpVersion = "HTTP/1.1";
private byte[] m_PostData = null;
private byte[] m_Buffer = new byte[65535];
private ReadWriteBuffer m_TempBuffer;
private FileStream m_FileStream = null;
private MemoryStream m_FullBuffer = new MemoryStream();
private TcpClient m_Client = null;
private System.IO.Stream m_NetworkStream = null;
private TcpWebServer m_Parent = null;
private Thread m_Thread_Read = null;
private Timer m_Timer_Check = null;
private DateTime m_LastRead = DateTime.Now;
private AutoResetEvent m_WaitHandle_Write;
#endregion private members
#region constructors
internal TcpWebConnection(TcpClient client, TcpWebServer parent, bool ssl)
{
m_WaitHandle_Write = new AutoResetEvent(false);
m_TempBuffer = new ReadWriteBuffer(65535);
m_IsSSL = ssl;
m_Segments[0] = string.Empty;
m_Client = client;
m_Parent = parent;
m_RemoteIP = ((IPEndPoint)m_Client.Client.RemoteEndPoint).Address.ToString();
if (ssl)
{
m_NetworkStream = new SslStream(m_Client.GetStream(), false);
}
else
{
m_NetworkStream = m_Client.GetStream();
}
m_NetworkStream.ReadTimeout = 2000;
m_Timer_Check = new Timer(Timer_Check_Callback, this, 2000, 2000);
// start threads
m_Thread_Read = new Thread(DoRead);
m_Thread_Read.IsBackground = true;
m_Thread_Read.Start();
}
#endregion constructors
#region destructors
~TcpWebConnection()
{
try
{
if (m_Timer_Check != null) m_Timer_Check.Dispose();
m_Timer_Check = null;
}
catch { } // if the timer was
}
#endregion destructors
#region internal properties
internal bool IsLargeFileUpload { get; set; } = false;
internal string TempFilename
{
get { return m_TempFilename; }
set { m_TempFilename = value; }
}
/// <summary>
/// Remote IP
/// </summary>
internal string RemoteIP
{
get { return m_RemoteIP; }
}
internal string AbsoluteURI
{
get { return m_AbsoluteURI; }
}
internal string ContentType
{
get { return m_ContentType; }
}
internal string[] Segments
{
get { return m_Segments; }
}
internal NameValueCollection QueryString
{
get { return m_QueryString; }
}
internal Stream NetworkStream
{
get { return m_NetworkStream; }
}
internal int ContentLength
{
get { return m_ContentLength; }
}
#endregion internal properties
#region private methods
private void Timer_Check_Callback(object state)
{
if ((DateTime.Now - m_LastRead).TotalSeconds > 15)
{
try
{
Program.BlacklistIP(m_RemoteIP, "TcpWebConnection - Timer", "Connection Timed Out");
ProcessRequest(m_ReadData);
Dispose();
}
catch (Exception e) { }
}
}
private void DoRead()
{
if (m_IsSSL)
{
try
{
((SslStream)m_NetworkStream).AuthenticateAsServer(m_Parent.ServerCertificate, false, SslProtocols.Tls, false);
((SslStream)m_NetworkStream).BeginRead(m_Buffer, 0, m_Buffer.Length, new AsyncCallback(SslRead), m_NetworkStream);
m_NetworkStream.ReadTimeout = 5000;
m_NetworkStream.WriteTimeout = 5000;
}
catch (Exception e)
{
//Console.WriteLine("SSL Auth Error: " + e.Message);
}
}
else
{
NormalRead();
}
}
private void UpdatePostData()
{
m_FullBuffer.Position = 0;
byte[] fullBuffer = Common.Conversion.MemoryStreamToByteArray(m_FullBuffer);
m_FullBuffer.Dispose();
if (m_StartBoundaryIndex > -1 && m_EndBoundaryIndex > -1)
{
m_PostData = new byte[m_EndBoundaryIndex - m_StartBoundaryIndex];
Array.Copy(fullBuffer, m_StartBoundaryIndex, m_PostData, 0, m_EndBoundaryIndex - m_StartBoundaryIndex);
}
}
internal void SaveFile(string filepath)
{
try
{
UpdatePostData();
if (m_PostData == null) return;
if (!Directory.Exists(Path.GetDirectoryName(filepath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
}
if (File.Exists(filepath))
{
File.Delete(filepath);
}
using (FileStream output = new FileStream(filepath, FileMode.Create, FileAccess.Write))
{
output.Write(m_PostData, 0, m_PostData.Length);
}
}
catch (Exception e)
{
// report error
}
}
private void AppendBuffer(byte[] newBuffer, int length)
{
// we need to keep a running buffer here, the last 1024 bytes? how best to find the end boundary? need to determine when the stream is finished!
m_TempBuffer.Write(newBuffer, length);
if (m_IsFileUpload)
{
if (m_EndBoundaryIndex < 0)
{
m_EndBoundaryIndex = StreamHelper.LastIndexOf(m_TempBuffer.RawBytes, m_TempBuffer.Count, m_EndBoundaryBytes);
if (!IsLargeFileUpload && m_EndBoundaryIndex > -1)
{
m_EndBoundaryIndex = (m_FullBuffer.Length + length) - (m_TempBuffer.Count - m_EndBoundaryIndex);
}
}
if (m_StartBoundaryIndex < 0)
{
m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, m_StartBoundaryBytes);
if (m_StartBoundaryIndex > -1)
{
m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, Encoding.UTF8.GetBytes("\r\n\r\n"), m_StartBoundaryIndex + m_StartBoundaryBytes.Length) + 4;
}
}
}
if (m_StartBoundaryIndex == -1 || !IsLargeFileUpload) // if this is not a file upload because no start boundary has been found then write buffer to memory
{
m_FullBuffer.Write(newBuffer, 0, length);
}
else
{
if (!m_FileCreated) // we have never written to the file, dump the contents of the full buffer now
{
bool exists = true;
while (exists)
{
m_TempFilename = Config.StaticConfig.TempFolder + "/" + Path.GetRandomFileName();
exists = File.Exists(m_TempFilename);
}
m_FileStream = new FileStream(m_TempFilename, FileMode.Create, FileAccess.Write);
m_FullBuffer.Position = m_StartBoundaryIndex;
m_FullBuffer.CopyTo(m_FileStream);
m_FileStream.Write(newBuffer, 0, length);
m_FileCreated = true;
}
else // we have previously written to the file, append new bytes
{
if (m_EndBoundaryIndex == -1)
{
m_FileStream.Write(newBuffer, 0, length);
}
else
{
m_FileStream.Write(newBuffer, 0, length - m_EndBoundaryBytes.Length);
}
}
}
}
private void NormalRead()
{
try
{
int bufferSize = m_Buffer.Length;
int bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);
while (bytesRead > 0 && !m_IsDisposed)
{
m_LastRead = DateTime.Now;
m_BytesRead += bytesRead;
if (!m_HasHeaders || m_RequestType == RequestType.GET)
{
string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, bytesRead);
m_ReadData += sBuffer;
}
AppendBuffer(m_Buffer, bytesRead);
m_HasHeaders = UpdateUniqueHeaders();
if (!m_HasHeaders && m_BytesRead > 1024)
{
Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
return;
}
if (m_RequestType != RequestType.POST)
{
break; // process the request
}
else if (m_EndBoundaryIndex != -1)
{
break; // process the request, we found our end boundary for posted data
}
bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);
}
ProcessRequest(m_ReadData);
}
catch (Exception e)
{
// report error
}
}
private void SslRead(IAsyncResult ar)
{
if (m_IsDisposed) return;
try
{
int byteCount = -1;
int bufferSize = m_Buffer.Length;
m_LastRead = DateTime.Now;
byteCount = m_NetworkStream.EndRead(ar);
m_BytesRead += byteCount;
if (!m_HasHeaders || m_RequestType == RequestType.GET)
{
string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, byteCount);
m_ReadData += sBuffer;
}
AppendBuffer(m_Buffer, byteCount);
m_HasHeaders = UpdateUniqueHeaders();
if (!m_HasHeaders && m_BytesRead > 1024)
{
Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
return;
}
if (byteCount > 0)
{
if (m_RequestType != RequestType.POST && m_RequestType != RequestType.None)
{
m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
}
else if (m_EndBoundaryIndex == -1) // as long as we haven't found the end of the stream continue reading
{
m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
return;
}
}
}
catch (Exception e)
{
return;
}
ProcessRequest(m_ReadData);
}
private bool UpdateUniqueHeaders()
{
if (m_RequestType == RequestType.None && m_ReadData.Length > 8)
{
m_RequestType = (m_ReadData.StartsWith("GET ") ? RequestType.GET : m_RequestType);
m_RequestType = (m_ReadData.StartsWith("POST ") ? RequestType.POST : m_RequestType);
m_RequestType = (m_ReadData.StartsWith("OPTIONS ") ? RequestType.OPTIONS : m_RequestType);
}
if (m_RequestType == RequestType.GET || m_RequestType == RequestType.POST)
{
string request = m_ReadData;
if (string.IsNullOrEmpty(m_HttpVersion)) m_HttpVersion = m_ReadData.Substring(request.IndexOf("HTTP", 1), 8);
if (string.IsNullOrEmpty(m_ContentType)) m_ContentType = GetHeader(request, "Content-Type");
if (m_ContentLength == 0)
{
int cLength = 0;
int.TryParse(GetHeader(request, "Content-Length"), out cLength);
m_ContentLength = cLength;
if (m_ContentLength / 1024 / 1024 > 20)
{
IsLargeFileUpload = true; // data is sent directly to a file instead of saving in memory
}
}
}
if (m_RequestType != RequestType.None && !string.IsNullOrEmpty(m_HttpVersion) && (!string.IsNullOrEmpty(m_ContentType) || m_RequestType != RequestType.POST))
{
if (m_RequestType == RequestType.POST)
{
try
{
if (m_IsFileUpload == false)
{
m_IsFileUpload = Segments[1].Replace("/", "") == "upload";
}
}
catch { }
if (m_RequestType == RequestType.POST && m_StartBoundaryBytes == null)
{
m_StartBoundaryBytes = Encoding.ASCII.GetBytes(GetStartBoundary());
m_EndBoundaryBytes = Encoding.ASCII.GetBytes(GetEndBoundary());
}
}
if (string.IsNullOrEmpty(m_Request) && m_Segments.Length <= 1 && m_QueryString == null)
{
// Extract the Requested Type and Requested file/directory
string m_Request = m_ReadData.Substring(0, m_ReadData.IndexOf("HTTP", 1) - 1);
//Replace backslash with Forward Slash, if Any
m_Request = m_Request.Replace("\\", "/");
m_Request = m_Request.Replace("GET ", "");
m_Request = m_Request.Replace("POST ", "");
Uri uri = new Uri("http://localhost" + m_Request);
NameValueCollection query = HttpUtility.ParseQueryString(uri.Query);
//SendHeader(sHttpVersion, "image/jpeg", Program.BlankImageBuffer.Length, " 200 OK");
m_AbsoluteURI = m_Request;
m_Segments = uri.Segments;
m_QueryString = query;
}
if (m_RequestType != RequestType.POST)
{
return true;
}
else if (m_ContentLength > 0 && m_EndBoundaryBytes != null)
{
return true;
}
}
return false;
}
private string GetStartBoundary()
{
return "--" + m_ContentType.Split(';')[1].Split('=')[1];
}
private string GetEndBoundary()
{
return "--" + m_ContentType.Split(';')[1].Split('=')[1] + "--\r\n";
}
private string GetHeader(string request, string key)
{
string result = string.Empty;
int iStartPos = request.IndexOf(key + ":", 0) + key.Length + 1;
if (request.IndexOf(key + ":", 0) > -1)
{
// Get the HTTP text and version e.g. it will return "HTTP/1.1"
int iEndPos = request.IndexOf("\r\n", iStartPos);
result = request.Substring(iStartPos, iEndPos - iStartPos).Trim();
}
return result;
}
private void CleanFile()
{
try
{
if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
{
using (Stream stream = File.Open(m_TempFilename, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
//stream.Position = 0;
//stream.Write(data, 0, data.Length);
}
}
}
catch { }
}
private void ProcessRequest(string request)
{
try
{
if (request.Length < 5) return;
List<string> headers = null;
if (request.StartsWith("OPTIONS"))
{
headers = GetCommonHeader("", 0);
headers.Add("Access-Control-Allow-Credentials: true");
headers.Add("Access-Control-Allow-Headers: Authorization, X-Mashape-Authorization, Accept, Content-Type, X-Requested-With, X-PINGOTHER, X-File-Name, Cache-Control");
headers.Add("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS");
headers.Add("Keep-Alive: timeout=15,max=100");
headers.Add("Access-Control-Allow-Origin: *");
headers.Add("Connection: close");
SendHeader(headers);
return;
}
UpdateUniqueHeaders();
CleanFile();
CloseFile();
if (m_Timer_Check != null) m_Timer_Check.Dispose();
string responseText = Program.ProcessRequest(this);
if (string.IsNullOrEmpty(responseText)) responseText = "\r\n";
byte[] buf = Encoding.ASCII.GetBytes(responseText);
headers = GetCommonHeader("text/html", buf.Length, " 200 OK");
headers.Add("Access-Control-Allow-Origin: *");
SendHeaderAndData(headers, buf);
}
catch (Exception e) { }
finally
{
Dispose();
}
}
private void CloseFile()
{
try
{
if (m_FileStream != null)
{
m_FileStream.Dispose();
m_FileStream = null;
}
}
catch { }
}
/// <summary>
/// This function send the Header Information to the client (Browser)
/// </summary>
/// <param name="sHttpVersion">HTTP Version</param>
/// <param name="sMIMEHeader">Mime Type</param>
/// <param name="iTotBytes">Total Bytes to be sent in the body</param>
/// <param name="mySocket">Socket reference</param>
/// <returns></returns>
public List<string> GetCommonHeader(string mimeHeader = "text/html", int length = -1, string sStatusCode = " 200 OK", string filename = "", bool chunked = false)
{
// if Mime type is not provided set default to text/html
List<string> headers = new List<string>();
headers.Add(m_HttpVersion + sStatusCode);
headers.Add("Server: ExampleTcpWebServer");
if (!string.IsNullOrEmpty(mimeHeader))
{
headers.Add("Content-Type: " + mimeHeader);
}
if (length > -1)
{
headers.Add("Content-Length: " + length);
}
headers.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("ddd, d MMM yyyy HH:mm:ss") + " GMT");
if (!string.IsNullOrEmpty(filename))
{
headers.Add("Content-Disposition: attachment; filename=\"" + filename + "\"");
}
if (chunked)
{
headers.Add("Transfer-Encoding: chunked");
}
return headers;
}
public void SendHeader(List<string> headers)
{
string sHeader = string.Empty;
foreach (string header in headers)
{
sHeader += header + "\r\n";
}
sHeader += "\r\n";
byte[] bSendData = Encoding.ASCII.GetBytes(sHeader);
SendToBrowser(bSendData, bSendData.Length);
}
public void SendHeaderAndData(List<string> headers, byte[] data)
{
string sHeader = string.Empty;
foreach (string header in headers)
{
sHeader += header + "\r\n";
}
sHeader += "\r\n";
byte[] bHeader = Encoding.ASCII.GetBytes(sHeader);
byte[] combined = new byte[bHeader.Length + data.Length];
Array.Copy(bHeader, combined, bHeader.Length);
Array.Copy(data, 0, combined, bHeader.Length, data.Length);
SendToBrowser(combined, combined.Length);
}
/// <summary>
/// Sends data to the browser (client)
/// </summary>
/// <param name="bSendData">Byte Array</param>
/// <param name="mySocket">Socket reference</param>
public void SendToBrowser(byte[] bSendData, int length)
{
try
{
if (Common.TcpHelper.SocketConnected(m_Client.Client))
{
if (m_IsSSL)
{
m_NetworkStream.Write(bSendData, 0, length);
}
else
{
m_Client.Client.Send(bSendData, length, 0);
}
}
else
{
Dispose();
}
}
catch (Exception e)
{
//Console.WriteLine("Error Occurred : {0} ", e);
}
}
#endregion private methods
#region IDisposable
public void Dispose()
{
if (!m_IsDisposed)
{
m_IsDisposed = true;
try
{
if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
{
File.Delete(m_TempFilename);
}
}
catch { }
CloseFile();
try
{
m_Client.Client.Close(5);
m_Client.Close();
m_Client.Client.Dispose();
}
catch { }
try
{
m_NetworkStream.Dispose();
}
catch { }
try
{
if (Thread.CurrentThread != m_Thread_Read && m_Thread_Read.IsAlive)
{
m_Thread_Read.Join(1000);
if (m_Thread_Read.IsAlive) m_Thread_Read.Abort();
}
}
catch { }
try
{
m_ReadData = null;
m_PostData = null;
m_Buffer = null;
m_TempBuffer = null;
if (m_FullBuffer != null) m_FullBuffer.Dispose();
if (m_Timer_Check != null) m_Timer_Check.Dispose();
m_Timer_Check = null;
}
catch { }
}
}
#endregion IDisposable
}
}

C# Null Sockets

When the socket is null, it lags everyone which is connected and I'm wondering what could be the cause of this, the affected code is below.
public void SendData(byte[] Data)
{
try
{
if (Stopped)
{
return;
}
if (mSocket != null)
{
Output.WriteLine("[SND][" + mId + "]: " + Constants.DefaultEncoding.GetString(Data),
OutputLevel.DebugInformation);
mSocket.BeginSend(Data, 0, Data.Length, SocketFlags.None, new AsyncCallback(OnDataSent), null);
}
else
{
using (SqlDatabaseClient MySqlClient = SqlDatabaseManager.GetClient())
{
Stop(MySqlClient);
}
}
}
catch (Exception e)
{
//SOCKETISNULL
}
}

Categories