Listen.Update
It turns out that the function Read
that reads in streams does not guarantee the reading of all data sent at once. This happens to improve performance in streams. As data packets reach the customer, they are made available for reading.
When you call the function Read
it only returns immediately if some data packet has already arrived to the client through the connection, otherwise it goes into standby mode and waits for the arrival of some package. This routine can take several rounds.
The function Read
will read the available data up to a certain length (buffer length, 1024 in this case) and return the amount of bytes read. If no data is available for reading, the function Read
will go into standby mode until it reaches the time set in ReadTimout
or a package arrives. If ReadTimout
if reached, an exception will be thrown. And if the stream or connection is terminated, the function Read
return 0 (zero), indicating that no more data is to be read.
Therefore, you will need to close the stream or connection to indicate that the data has been fully uploaded.
public static void UpdateClient(UserConnection client) {
System.IO.MemoryStream mStream = new System.IO.MemoryStream(client.TCPClient.Available);
System.Net.Sockets.NetworkStream nStream = client.TCPClient.GetStream();
byte[] buffer = new byte[1024];
try {
while(true) {
int nBytes = nStream.Read(buffer, 0, 1024);
if(nBytes <= 0)
break;
mStream.Write(buffer, 0, nBytes);
}
} catch {
int code = System.Runtime.InteropServices.Marshal.GetExceptionCode();
Console.WriteLine("Erro Num: " + code);
return;
}
buffer = mStream.ToArray();
string data = null;
data = Encoding.UTF8.GetString(buffer);
Console.WriteLine("Data is: " + data);
Console.WriteLine("Size is: " + data.Length);
Server.Network.ReceiveData.SelectPacket(client.Index, data);
client.TCPClient.GetStream().Flush();
}
A good practice is to define a protocol for flow control, however simple. As an example, we can agree that before sending any information, the server will send 4 bytes (32 bits) indicating the amount of bytes that will compose the next information.
public static byte[] ReadBytes(System.IO.Stream stream, int size) {
if (size <= 0)
throw new ArgumentOutOfRangeException();
byte[] buffer = new byte[size];
int i = 0;
while (size > 0) {
int nBytes = stream.Read(buffer, i, size);
if (nBytes <= 0)
break;
i += nBytes;
size -= nBytes;
}
if(size > 0) {
byte[] nBuffer = new byte[buffer.Length - size];
Array.Copy(buffer, 0, nBuffer, 0, nBuffer.Length);
buffer = nBuffer;
}
return buffer;
}
public static void UpdateClient(UserConnection client) {
System.Net.Sockets.NetworkStream nStream = client.TCPClient.GetStream();
byte[] buffer = null;
try {
int size = BitConverter.ToInt32(ReadBytes(nStream, 4), 0);
if (size <= 0)
throw new InvalidOperationException();
buffer = ReadBytes(nStream, size);
if(buffer.Length != size)
throw new InvalidOperationException();
} catch {
int code = System.Runtime.InteropServices.Marshal.GetExceptionCode();
Console.WriteLine("Erro Num: " + code);
return;
}
string data = null;
data = Encoding.UTF8.GetString(buffer);
Console.WriteLine("Data is: " + data);
Console.WriteLine("Size is: " + data.Length);
Server.Network.ReceiveData.SelectPacket(client.Index, data);
client.TCPClient.GetStream().Flush();
}
Would be problem with Toint32? I believe that Tcpclient.Available already returns Int32
– Guilherme Nascimento