Reading/Writing TcpClient/SslStream in separate threads - c#

I have an SSL connection to a server and post requests to it. The act of posting a message should be instant with no delay to read the response, because there would be consequent posts that should come without the delay.
That's why i just do
this.sslStream.Write(byteArray, 0, byteArray.Length);
However, I need some responses to be actually received by my app, so I have a parallel thread:
this.threadReadStream = new Thread(new ThreadStart(this.ThreadReadStream));
this.threadReadStream.Start();
In that thread I have a loop which reads data from sslStream:
public void ThreadReadStream()
{
int bufferLen = 4096, byteCount, pos, pos2;
var buffer = new byte[bufferLen];
string responseBuffer = "", responsePart, response = "";
bool err = true;
while (true)
{
err = true;
byteCount = 0;
while (err)
{
err = false;
byteCount = 0;
try
{
byteCount = this.sslStream.Read(buffer, 0, bufferLen);
}
catch (Exception exception)
{
err = true;
this.TcpConnect();
}
}
if (byteCount > 0)
{
// do something
}
}
}
The problem is that sslStream.Read always returns 0 while being used in separate thread, but works fine if called in the same thread with sslStream.Write.
Few comments about the things that are not likely to influece the problem, however, it is better to clarify the stuff:
1) I use while(err) cycle to check that the connection was not broken. If so, I reconnect and read again.
2) For the ones who don't like while(true) thing. Yes, I have the exit condition from the loop in the parallel thread which shut downs all the tcp-related threads.

How does the server identify that the write process was ended?
Maybe there is actually a data on the way to the server so it don't starting to send the response.
In addition, sharing a TCP connection between different threads is not a good practice.

Related

C# NetworkStream detect all kind of disconnections and exceptions

I have this code to connect to streaming server. the server disconnect from time to time and I want to detect it and restart the connection when neede.
How can I detect in this code any kind of exceptions?
Because now I get disconnected and can't catch it.
this.ns = new NetworkStream(server);
while (true)
{
// Incoming message may be larger than the buffer size.
do
{
byte[] myReadBuffer = new byte[10240 * 5];
await this.ns.ReadAsync(myReadBuffer, 0, myReadBuffer.Length).ContinueWith((numberOfBytesRead) =>
{
string message = Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead.Result);
p.Invoke(message);
});
}
while (this.ns.DataAvailable);
}
Your variable numberOfBytesRead is actually the previous task that has finished from where you can check whether it is completed or failed.
if(numberOfBytesRead.IsFaulted)
{
var aggregatedEx = numberOfBytesRead.Exception;
//do something
}

Best Way to write large data over network stream of TcpClient

We have a requirement to upload large firmware files to printers to upgrade the firmware of the device. The printer device is in the same network as my server, and the size of firmware that we are trying to upload is approximately to 200 - 500 MB. The approach that we have chosen is to load the firmware (.bin file) into Memory stream and write it in chunks over the network using TcpClient.
Based on the response from the network stream, we are displaying the status of firmware upgrade to the client. Following is the code snippet that we have used for firmware upgrade. I want to know if it is the best approach, as wrong one may harm the device.
EDIT:
class MyClass
{
int port = 9100;
string _deviceip;
byte[] m_ReadBuffer = null;
TcpClient _tcpclient;
NetworkStream m_NetworkStream;
static string CRLF = "\r\n";
public event EventHandler<DeviceStatus> onReceiveUpdate;
public async Task<bool> UploadFirmware(Stream _stream)
{
bool success = false;
try
{
_tcpclient = new TcpClient();
_tcpclient.Connect(_deviceip, port);
_stream.Seek(0, SeekOrigin.Begin);
m_NetworkStream = _tcpclient.GetStream();
byte[] buffer = new byte[1024];
m_ReadBuffer = new byte[1024];
int readcount = 0;
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length,
new AsyncCallback(EndReceive), null);
await Task.Run(() =>
{
while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0)
{
m_NetworkStream.Write(buffer, 0, readcount);
m_NetworkStream.Flush();
}
});
success = true;
}
catch (Exception ex)
{
upgradeStatus = false;
}
return success;
}
private void EndReceive(IAsyncResult ar)
{
try
{
int nBytes;
nBytes = m_NetworkStream.EndRead(ar);
if (nBytes > 0)
{
string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes);
DeviceStatus status = new DeviceStatus();
string[] readlines = res.Split(new string[] { CRLF },
StringSplitOptions.RemoveEmptyEntries);
foreach (string readline in readlines)
{
if (readline.StartsWith("CODE"))
{
//read readline string here
break;
}
}
}
if (m_NetworkStream.CanRead)
{
do
{
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new
AsyncCallback(EndReceive), null);
} while (m_NetworkStream.DataAvailable);
}
}
catch (ObjectDisposedException ods)
{
return;
}
catch (System.IO.IOException ex)
{
}
}
}
Any help will be really appreciated.
Your code is basically fine with a few issues:
m_NetworkStream.Flush(); AFAIK this does nothing. If it did something it would harm throughput. So delete that.
_stream.Seek(0, SeekOrigin.Begin); seeking is the callers concern, remove that. This is a layering violation.
Use bigger bigger buffers. Determine the right size experimentally. I usually start at 64KB for bulk transfers. This makes the IOs less chatty.
Turn on nagling which helps with bulk transfers because it saves you from spurious small packets.
You can replace the entire read-write-loop with Stream.Copy.
The way you report exceptions to the callers hides a lot of information. Just let the exception bubble out. Don't return a bool.
Use using for all resource to ensure they are cleaned up in the error case.
nBytes = m_NetworkStream.EndRead(ar); here, you assume that a single read will return all data that will be coming. You might receive just the first byte, though. Probably, you should use StreamReader.ReadLine in a loop until you know you are done.
catch (System.IO.IOException ex) { } What is that about? If firmware updates are such a critical thing suppressing errors appears very dangerous. How else can you find out about bugs?
I would convert the reading code to use await.
As the maximum length for a TcpPacket is 65535 (2^16-1), if you send any packet breaching this lenght it will be truncated. If I were you, I think the best way of sending large packets, is setting a Header of every packet and enumerating them. For example:
C->S ; [P1] <content>
and then the same structure, just plus 1 [P2] <content>
To do so, just use few substrings to truncate the data and sending them.
Cheers!

NetworkStream Receive, how to processing data without using 100% CPU?

I have a small game server I'm making that will have dozens of connections sending player data constantly. While I've finally accomplished some basics and now have data sending/receiving, I now face a problem of flooding the server and the client with too much data. I've tried to throttle it back but even then I am hitting 90-100% cpu simply because of receiving and processing the data received running up the CPU.
The method below is a bare version of receiving data from the server. The server sends a List of data to be received by the player, then it goes through that list. I've thought perhaps instead just using a dictionary with a key based on type rather than for looping but I don't think that will significantly improve it, the problem is that it is processing data non-stop because player positions are constantly being updated, sent to the server, then send to other players.
The code below shows receive for the client, the server receive looks very similar. How might I begin to overcome this issue? Please be nice, I am still new to network programming.
private void Receive(System.Object client)
{
MemoryStream memStream = null;
TcpClient thisClient = (TcpClient)client;
List<System.Object> objects = new List<System.Object>();
while (thisClient.Connected && playerConnected == true)
{
try
{
do
{
//when receiving data, first comes length then comes the data
byte[] buffer = GetStreamByteBuffer(netStream, 4); //blocks while waiting for data
int msgLenth = BitConverter.ToInt32(buffer, 0);
if (msgLenth <= 0)
{
playerConnected = false;
thisClient.Close();
break;
}
if (msgLenth > 0)
{
buffer = GetStreamByteBuffer(netStream, msgLenth);
memStream = new MemoryStream(buffer);
}
} while (netStream.DataAvailable);
if (memStream != null)
{
BinaryFormatter formatter = new BinaryFormatter();
memStream.Position = 0;
objects = new List<System.Object>((List<System.Object>)formatter.Deserialize(memStream));
}
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.ToString());
if (thisClient.Connected == false)
{
playerConnected = false;
netStream.Close();
thisClient.Close();
break;
}
}
try
{
if (objects != null)
{
for (int i = 0; i < objects.Count; i++)
{
if(objects[i] != null)
{
if (objects[i].GetType() == typeof(GameObject))
{
GameObject p = (GameObject)objects[i];
GameObject item;
if (mapGameObjects.TryGetValue(p.objectID, out item))
{
mapGameObjects[p.objectID] = p;;
}
else
{
mapGameObjects.Add(p.objectID, p);
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception " + ex.ToString());
if (thisClient.Connected == false)
{
playerConnected = false;
netStream.Close();
break;
}
}
}
Console.WriteLine("Receive thread closed for client.");
}
public static byte[] GetStreamByteBuffer(NetworkStream stream, int n)
{
byte[] buffer = new byte[n];
int bytesRead = 0;
int chunk = 0;
while (bytesRead < n)
{
chunk = stream.Read(buffer, (int)bytesRead, buffer.Length - (int)bytesRead);
if (chunk == 0)
{
break;
}
bytesRead += chunk;
}
return buffer;
}
Based on the code shown, I can't say why the CPU utilization is high. The loop will wait for data, and the wait should not consume CPU. That said, it still polls the connection in checking the DataAvailable property, which is inefficient and can cause you to ignore received data (in the implementation shown...that's not an inherent problem with DataAvailable).
I'll go one further than the other answer and state that you should simply rewrite the code. Polling the socket is just no way to handle network I/O. This would be true in any scenario, but it is especially problematic if you are trying to write a game server, because you're going to use up a lot of your CPU bandwidth needlessly, taking it away from game logic.
The two biggest changes you should make here are:
Don't use the DataAvailable property. Ever. Instead, use one of the asynchronous APIs for dealing with network I/O. My favorite approach with the latest .NET is to wrap the Socket in a NetworkStream (or get the NetworkStream from a TcpClient as you do in your code) and then use the Stream.ReadAsync() along with async and await. But the older asynchronous APIs for Sockets work well also.
Separate your network I/O code from the game logic code. The Receive() method you show here has both the I/O and the actual processing of the data relative to the game state in the same method. This two pieces of functionality really belong in two separate classes. Keep both classes, and especially the interface between them, very simple and the code will be a lot easier to write and to maintain.
If you decide to ignore all of the above, you should at least be aware that your GetStreamByteBuffer() method has a bug in it: if you reach the end of the stream before reading as many bytes were requested, you still return a buffer as large as was requested, with no way for the caller to know the buffer is incomplete.
And finally, IMHO you should be more careful about how you shutdown and close the connection. Read about "graceful closure" for the TCP protocol. It's important that each end signal that they are done sending, and that each end receive the other end's signal, before either end actually closes the connection. This will allow the underlying networking protocol to release resources as efficiently and as quickly as possible. Note that TcpClient exposes the socket as the Client property, which you can use to call Shutdown().
Polling is rarely a good approach to communication, unless you're programming 16-bit microcontrollers (and even then, probably not the best solution).
What you need to do is to switch to a producer-consumer pattern, where your input port (a serial port, an input file, or a TCP socket) will act as a producer filling a FIFO buffer (a queue of bytes), and some other part of your program will be able to asynchronously consume the enqueued data.
In C#, there are several ways to do it: you can simply write a couple of methods using a ConcurrentQueue<byte>, or a BlockingCollection, or you can try a library like the TPL Dataflow Library which IMO doesn't add too much value over existing structures in .NET 4. Prior to .NET 4, you would simply use a Queue<byte>, a lock, and a AutoResetEvent to do the same job.
So the general idea is:
When your input port fires a "data received" event, enqueue all received data into the FIFO buffer and set a synchronization event to notify the consumer,
In your consumer thread, wait for the synchonization event. When the signal is received, check if there is enough data in the queue. If yes, process it, if not, continue waiting for the next signal.
For robustness, use an additional watchdog timer (or simply "time since last received data") to be able to fail on timeout.
You want to use the Task-based Asynchronous Pattern. Probably making liberal use of the async function modifier and the await keyword.
You'd be best replacing GetStreamByteBuffer with a direct call to ReadAsync.
For instance you could asynchronously read from a stream like this.
private static async Task<T> ReadAsync<T>(
Stream source,
CancellationToken token)
{
int requestLength;
{
var initialBuffer = new byte[sizeof(int)];
var readCount = await source.ReadAsync(
initialBuffer,
0,
sizeof(int),
token);
if (readCount != sizeof(int))
{
throw new InvalidOperationException(
"Not enough bytes in stream to read request length.");
}
requestLength = BitConvertor.ToInt32(initialBuffer, 0);
}
var requestBuffer = new byte[requestLength];
var bytesRead = await source.ReadAsync(
requestBuffer,
0,
requestLength,
token);
if (bytesRead != requestLength)
{
throw new InvalidDataException(
string.Format(
"Not enough bytes in stream to match request length." +
" Expected:{0}, Actual:{1}",
requestLength,
bytesRead));
}
var serializer = new BinaryFormatter();
using (var requestData = new MemoryStream(requestBuffer))
{
return (T)serializer.Deserialize(requestData);
}
}
Like your code this reads an int from the stream to get the length, then reads that number of bytes and uses the BinaryFormatter to deserialize the data to the specified generic type.
Using this generic function you can simplify your logic,
private Task Receive(
TcpClient thisClient,
CancellationToken token)
{
IList<object> objects;
while (thisClient.Connected && playerConnected == true)
{
try
{
objects = ReadAsync<List<object>>(netStream, token);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.ToString());
if (thisClient.Connected == false)
{
playerConnected = false;
netStream.Close();
thisClient.Close();
break;
}
}
try
{
foreach (var p in objects.OfType<GameObject>())
{
if (p != null)
{
mapGameObjects[p.objectID] = p;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception " + ex.ToString());
if (thisClient.Connected == false)
{
playerConnected = false;
netStream.Close();
break;
}
}
}
Console.WriteLine("Receive thread closed for client.");
}
You need to put a Thread.Sleep(10) in your while loop. This is also a very fragile way to receive tcp data because it assumes the other side has sent all data before you call this receive. If the other side has only sent half of the data this method fails. This can be countered by either sending fixed sized packages or sending the length of a package first.
Your player position update is similar to the framebuffer update in the VNC protocol where the client request a screen frame & server responds to it with the updated screen data. But there is one exception, VNC server doesn't blindly send the new screen it only sends the changes if there is one. So you need to change the logic from sending all the requested list of objects to only to the objects which are changed after the last sent. Also in addition to it, you should send entire object only once after that send only the changed properties, this will greatly reduce the size of data sent & processed both at clients & server.

Threaded Socket.Send()

I have 2 threads running simultaneously and each is writing to the socket.Send() stream,
While (soc.Connected)
{
byte[] byData = new byte[2];
byData = System.Text.Encoding.ASCII.GetBytes("A");
soc.Send(BitConverter.GetBytes(byData.Length));
soc.Send(byData);
}
The other thread uses the exact same code, except it's sending "1" instead of "A".
How will the data at the other end look like? Will it be either a stream of AAAAAAAA or 111111111s or randomly mixed like A1A1111AAAA1 ?
Should I avoid this way of sending entirely and block the sending until the other thread finishes?
Should I avoid this way of sending entirely and block the sending until the other thread finishes?
Yes and no, you should avoid this entirely but it is not necessary to block sending till the other thread finishes.
What you could do is have a 3rd thread who's responsibility is to send data and your two threads who need to send data put their data on to a thread safe queue. Then the sending thread would dequeue the work to be done and send it out on the wire.
const int MAX_QUEUE_LENGTH = 10;
private BlockingCollection<MyMessageContainer> messageQueue = new BlockingCollection<MyMessageContainer>(new ConcurrentQueue<MyMessageContainer>(), MAX_QUEUE_LENGTH);
void ProcessMessages()
{
foreach (var message in messageQueue.GetConsumingEnumerable())
{
if(soc.Connected == false)
break;
soc.Send(message.ToPaylod());
}
}
void GenerateMessageOne()
{
while(true)
{
messageQueue.Add(new MyMessageContainer("A"));
}
}
void GenerateMessageTwo()
{
while(true)
{
messageQueue.Add(new MyMessageContainer("1"));
}
}
class MyMessageContainer
{
public MyMessageContainer(string message)
{
_message = message;
}
private string _message;
public byte[] ToPayload()
{
var lengthBytes = BitConverter.GetBytes(byData.Length);
return lengthBytes.Concat(() => System.Text.Encoding.ASCII.GetBytes(_message)).ToArray();
}
}
The above code will let both threads queue work at the same time without blocking till the queue reaches a length of MAX_QUEUE_LENGTH, once there calling messageQueue.Add( will now start blocking till the sending thread has had a chance to clear up some room, once room has been made it will unblock one of the functions and let it continue.
If you want randomly-sequenced output, the easiest solution is to simply put a lock around the line that actually writes to the socket. I would also recommend adding a call to Thread.Sleep for fairness, though that is somewhat optional.
While (soc.Connected)
{
byte[] byData = new byte[2];
byData = System.Text.Encoding.ASCII.GetBytes("A");
lock(soc)
{
soc.Send(BitConverter.GetBytes(byData.Length));
soc.Send(byData);
}
Thread.Sleep(0);
}

C# udp socket not receiving entire message

I have the following method which sends an rcon command to a game server.
public string sendCommand(string command)
{
byte[] bufferTemp = Encoding.ASCII.GetBytes(command);
byte[] bufferSend = new byte[bufferTemp.Length + 4];
//big enough to receive response
byte[] bufferRec = new byte[65000];
//intial 4 characters as per standard
bufferSend[0] = byte.Parse("255");
bufferSend[1] = byte.Parse("255");
bufferSend[2] = byte.Parse("255");
bufferSend[3] = byte.Parse("255");
int j = 4;
for (int i = 0; i < bufferTemp.Length; i++)
{
bufferSend[j++] = bufferTemp[i];
}
//send rcon command and get response
try
{
this.server.Socket.Send(bufferSend, SocketFlags.None);
this.server.Socket.Receive(bufferRec);
}
catch (SocketException e)
{
MessageBox.Show(e.ToString(), "Error occured");
}
string response = Encoding.ASCII.GetString(bufferRec);
return response;
}
out of all the commands I can possibly send, 1 of them returns a lot more data than the others and it seems that the *buffer_rec* byte array only gets about 1/4 of the message, but that array has been declared big enough to contain all the data.
On the subsequent 3 requests, the rest of the data gets output, as if it was buffered in some way.
I don't know why this is taking place. If you do, could you please let me know how to remedy the problem?
Thank you
Crouz
Ok, it seems that after 6 hours of looking into this I have finally come up with a solution, so here is a small change made to the receiving of data, note the thread sleeping for 10ms, this seems to be needed to let the datagrams arrive in due time.
//send rcon command and get response
string response = "";
try
{
this.server.Socket.Send(bufferSend, SocketFlags.None);
do
{
int bytesReceived = this.server.Socket.Receive(bufferRec);
response += Encoding.ASCII.GetString(bufferRec, 0, bytesReceived);
System.Threading.Thread.Sleep(10);
} while (this.server.Socket.Available > 0);
}
catch (SocketException e)
{
MessageBox.Show(e.ToString(), "Error occured");
}
If you think there is a better or more elegant way of handling this, don't hesitate to slam my code, but do so by offering an alternative as I am always happy to learn new things. Regards, Crouz

Categories

Resources