I'm writing a multiple clients per server application in C# using async sockets. Each client on connection sends 10 items. The problem is, when starting lots of clients quickly, it appears that each client sent less than the 10 items and sometimes it sends nothing, the server just logs them connecting only.
The packet structure is the first 4 bytes are an int with size of data in them. Here's a part of server code. Each connected client has its own receive buffer which BeginReceive should write to.
private void Recieve(IAsyncResult iar) //Called when socket receives something.
{
Socket server_conn = (Socket)iar.AsyncState;
if (!SocketConnected(server_conn))
{
server_conn.Close();
logthis("Client Disconnected");
return;
}
int n = server_conn.EndReceive(iar); //Stop Receiving and parse data, n is number of bytes received
ClientData asdf = null;
foreach (ClientData cls in clientlist)
{
if (server_conn.RemoteEndPoint == cls.clientsock.RemoteEndPoint) //Who sent this data
{
asdf = cls; //cls is who sent this data
//Start a new thread and pass received bytes to it in order to be parsed
var t = new Thread(() => parse(cls, n,cls.recvbuffer));
t.Start();
Thread.Sleep(100);
break;
}
}
asdf.recvbuffer = new byte[1024]; //Clear buffer of client
server_conn.BeginReceive(asdf.recvbuffer, 0, asdf.recvbuffer.Length, SocketFlags.None, new AsyncCallback(Recieve), server_conn); //Start receiving again
}
private void parse(ClientData theclient, int nobytesreceived, byte[] bytesreceived)
{
ClientData cls = theclient;
int n = nobytesreceived;
byte[] receivedbytes = bytesreceived;
lock(s)
{
if (!cls.dataphase) //If there's no fragmented packets still waiting to be read
{
cls.dataphase = true;
byte[] sizeinbytes = new byte[4];
for (int i = 0; i < 4; i++)
{
sizeinbytes[i] = receivedbytes[i];
}
int size = BitConverter.ToInt32(sizeinbytes, 0); //Read first four bytes of packet to get size of data
if ((n - 4) == size) //If received number of bytes - 4 is equals to datasize
{
byte[] payload = new byte[size];
Array.Copy(receivedbytes, 4, payload, 0, (n - 4)); //Copy data to separately to payload array to be displayed to user
logthis(cls.clientsock.RemoteEndPoint.ToString());
logthis(Encoding.ASCII.GetString(payload));
cls.dataphase = false; //packet read successfully
}
else if ((n - 4) < size) //If received amount of bytes is less than data size (fragmented data)
{
cls.data = new byte[size];
for (int i = 4; i <= n - 4; i++)
{
cls.data[i - 4] += receivedbytes[i];
}
cls.datasize = size; //And cls.dataphase will remain true so it can be handled correctly the next time we receive something from same client
}
else if((n-4) > size) //If received amount of bytes is bigger than data (lumped packets)
{
byte[] payload = new byte[size];
byte[] otherpacket = new byte[(n - 4) - size];
for(int i = 0; i < size; i++)
{
payload[i] += receivedbytes[i + 4];
}
logthis(cls.clientsock.RemoteEndPoint.ToString());
logthis(Encoding.ASCII.GetString(payload));
Array.Copy(receivedbytes, (size + 4), otherpacket, 0, ((n - 4) - size));
receivedbytes = new byte[(n - 4) - size];
receivedbytes = otherpacket;
cls.dataphase = false;
parse(cls, ((n - 4) - size), receivedbytes); //Send rest of packet to read again
}
}
else
{
//not implemented, supposed to handle fragmented packets
if (n >= cls.datasize)
{
}
else if (n < cls.datasize)
{
}
}
}
}
Your problem comes from
else
{
//not implemented, supposed to handle fragmented packets
I would put money on the fact that you are hitting that else statement and loosing data. As soon as you don't have a complete read of a packet or two packets lumped together returned from your read function (which is a lot more common than you think) your client is now stuck in cls.dataphase = true; and will never get out of it.
Related
For the sake of this question, let's assume nothing is wrong with the server and let's just focus on the client and how it receives the data.
So I have this client / server setup going and this is how it currently flows.
The server sends a packet that's 5000 bytes.
All the packets are structured like this.. The first 4 bytes are reserved for the length of the entire packet, the next byte is reserved for the OpCode.. The remaining bytes represents the actual payload.
Length (4 bytes) | OpCode (1 byte) | Payload (x bytes)
On the client I'm currently doing this
byte[] RawBuffer = new byte[Constants.BufferSize]; //Constants.BufferSize = 1024
int packLen = 0;
int totalReceived = 0;
private byte[] allData;
private void ReceiveCallback(IAsyncResult ar)
{
var stream = (NetworkStream)ar.AsyncState;
var received = stream.EndRead(ar);
Debug.Print($"Data received: {received} bytes");
//If we haven't assigned a length yet
if (packLen <= 0)
{
//Use "allData" as the final buffer that we use to process later down the line.
using (var ms = new MemoryStream(RawBuffer))
using (var reader = new BinaryReader(ms))
{
packLen = reader.ReadInt32();
allData = new byte[packLen];
}
}
Buffer.BlockCopy(RawBuffer, 0, allData, 0, received);
totalReceived += received;
if (totalReceived == allData.Length)
{
Debug.Print($"We've successfully appended {allData.Length} bytes out of {packLen}");
}
stream.BeginRead(RawBuffer, 0, Constants.BufferSize, ReceiveCallback, _stream);
}
That only works if I send 1 packet from the server to the client, but if I send 5 packets for instance it never actually splits them up.
How do I properly split them up so that once it's received the full packet based on the Length header, it prints out "Received packet successfully"
In order to clean some messy code and get a better understanding of the SocketAsyncEventArgs class, I'd to know what's the most efficient technique to reassemble partially received messages from SocketAsyncEventArgs buffers.
To give you the big picture, I'm connected to a TCP server using a C# Socket client that will essentially receive data. The data received is message-based delimited by a \n character.
As you're probably already aware of, when using the ReceiveAsync method, this is almost a certitude that the last received message will be uncompleted such as you'll have to locate the index of the last complete message, copy the incomplete buffer section and keep it as start for the next received buffer and so on.
The thing is, I wish to abstract this operation from the upper layer and call the ProcessReceiveDataImpl as soon I get completed messages in the _tmpBuffer. I found that my Buffer.BlockCopy is not much readable (very old code also (-:) but anyway I wish to know what are you doing in this typical use case?
Code to reassemble messages:
public class SocketClient
{
private const int _receiveBufferSize = 8192;
private byte[] _remBuffer = new byte[2 * _receiveBufferSize];
private byte[] _tmpBuffer = new byte[2 * _receiveBufferSize];
private int _remBufferSize = 0;
private int _tmpBufferSize = 0;
private void ProcessReceiveData(SocketAsyncEventArgs e)
{
// the buffer to process
byte[] curBuffer = e.Buffer;
int curBufferSize = e.BytesTransferred;
int curBufferOffset = e.Offset;
int curBufferLastIndex = e.BytesTransferred - 1;
int curBufferLastSplitIndex = int.MinValue;
if (_remBufferSize > 0)
{
curBufferLastSplitIndex = GetLastSplitIndex(curBuffer, curBufferOffset, curBufferSize);
if (curBufferLastSplitIndex != curBufferLastIndex)
{
// copy the remain + part of the current into tmp
Buffer.BlockCopy(_remBuffer, 0, _tmpBuffer, 0, _remBufferSize);
Buffer.BlockCopy(curBuffer, curBufferOffset, _tmpBuffer, _remBufferSize, curBufferLastSplitIndex + 1);
_tmpBufferSize = _remBufferSize + curBufferLastSplitIndex + 1;
ProcessReceiveDataImpl(_tmpBuffer, _tmpBufferSize);
Buffer.BlockCopy(curBuffer, curBufferLastSplitIndex + 1, _remBuffer, 0, curBufferLastIndex - curBufferLastSplitIndex);
_remBufferSize = curBufferLastIndex - curBufferLastSplitIndex;
}
else
{
// copy the remain + entire current into tmp
Buffer.BlockCopy(_remBuffer, 0, _tmpBuffer, 0, _remBufferSize);
Buffer.BlockCopy(curBuffer, curBufferOffset, _tmpBuffer, _remBufferSize, curBufferSize);
ProcessReceiveDataImpl(_tmpBuffer, _remBufferSize + curBufferSize);
_remBufferSize = 0;
}
}
else
{
curBufferLastSplitIndex = GetLastSplitIndex(curBuffer, curBufferOffset, curBufferSize);
if (curBufferLastSplitIndex != curBufferLastIndex)
{
// we must copy the unused byte into remaining buffer
_remBufferSize = curBufferLastIndex - curBufferLastSplitIndex;
Buffer.BlockCopy(curBuffer, curBufferLastSplitIndex + 1, _remBuffer, 0, _remBufferSize);
// process the msg
ProcessReceiveDataImpl(curBuffer, curBufferLastSplitIndex + 1);
}
else
{
// we can process the entire msg
ProcessReceiveDataImpl(curBuffer, curBufferSize);
}
}
}
protected virtual void ProcessReceiveDataImpl(byte[] buffer, int bufferSize)
{
}
private int GetLastSplitIndex(byte[] buffer, int offset, int bufferSize)
{
for (int i = offset + bufferSize - 1; i >= offset; i--)
{
if (buffer[i] == '\n')
{
return i;
}
}
return -1;
}
}
Your input is very important and appreciated!
Thank you!
Updated:
Also, rather then calling the ProcessReceiveDataImpl and block further receive operations, will it be useful to queue completed messages and make them available to the consumer?
What I'm trying to do is to received a large number of bytes (about 5MB data) sent from the client side
Below is the code where data(byte[]) is received
byte[] receivedBytesRaw = new byte[4000];
//first, initialize network stream
NetworkStream stream = client.GetStream();
//The bytesNeeded is the size of bytes which is a protocol sent by the client side indicating the size of byte which will be sent
int bytesNeeded = 4000;
int bytesReceived = 0;
do
{
int bytesRead = stream.Read(receivedBytesRaw, bytesReceived, bytesNeeded - bytesReceived);
networkValidation.addBytesToList(receivedBytesRaw, ref receivedBytes);
bytesReceived += bytesRead;
} while (bytesReceived < bytesNeeded);
But now I'm stuck on a problem:
Everytime when data arrives, the do while loop loops for the first time, and the return value (i) is 26, then it loops again, this time, when it goes to " i = stream.Read(receivedBytesRaw, 0, receivedBytesRaw.Length);", the program seems waiting for the client side to send data and have no response, also, when I check "receivedBytesRaw", the data was incomplete, only the first 13 bytes was received, the remaining space in the byte array remains null, and the stream.DataAvailable is false
Why the server side received incomplete data?
Note: when I try to send small data (a string), it's ok
=====================================================================
Edited
Below is the client side code which sends data:
private int sendData(byte[] dataSend, string IP, int portNumber)
{
TcpClient clientSide = new TcpClient();
int result = -1;
try
{
clientSide.Connect(IP, portNumber);
}
catch (Exception ex)
{
return 2;
}
NetworkStream netStream = clientSide.GetStream();
if (netStream.CanWrite)
{
byte[] replyMsg = new byte[1024];
netStream.Write(dataSend, 0, dataSend.Length);
netStream.Flush();
result = 0;
}
else
{
result = 1;
}
return result;
}
Because it's a stream, and can be partial received. Are you sure you are always receiving packages with te size of 2048 bytes?
int i = 0;
int bytesNeeded = 200;
int bytesReceived = 0;
do
{
//read byte from client
int bytesRead = stream.Read(receivedBytesRaw, bytesReceived, bytesNeeded-bytesReceived);
bytesReceived += bytesRead;
// merge byte array to another byte array
} while (bytesReceived < bytesNeeded);
I think you need a frame protocol, try create a protocol like, writing the size of the data that follows.
example: (psuedo)
void SendData(byte[] data)
{
// get the 4 bytes of a int value.
byte[] dataLength = BitConverter.GetBytes(data.Lenght);
// write the length to the stream.
stream.Write(dataLength, 0, dataLength.Length);
// write the data bytes.
stream.Write(data, 0, data.Length);
}
void Receive()
{
// read 4 bytes from the stream.
ReadBuffer(buffer, 4);
// convert those 4 bytes to an int.
int dataLength = BitConverter.ToInt32(buffer, 0);
// read bytes with dataLength as count.
ReadBuffer(buffer, dataLength);
}
// read until the right amount of bytes are read.
void ReadBuffer(byte[] buffer, int length)
{
int i = 0;
int bytesNeeded = length;
int bytesReceived = 0;
do
{
//read byte from client
int bytesRead = stream.Read(buffer, bytesReceived, bytesNeeded-bytesReceived);
bytesReceived += bytesRead;
// merge byte array to another byte array
} while (bytesReceived < bytesNeeded); // <- you should do this async.
}
This is just an example..
Another solution you could try is using async reads.
I made a class that reads until all bytes are read. If it isn't a problem that the complete file is read, you could try this:
Example:
This example show that you can read a simple protocol. ReadPacket handles a length + data message. So the sender will first send an int value containing the length of data that follows.
The StartReading method reads a filename and the filedata. It will store up to 10mb max filesize. But this isn't originally designed for receiving files.
const int MaxFileSize = 10 * 1024 * 1024;
private void Example()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("localhost", 12345);
StartReading(socket);
}
private void StartReading(Socket socket)
{
ReadPacket(socket, (filenameData) =>
{
if (filenameData.Count == 0)
{
// disconnected
return;
}
// parse the filename
string filename = Encoding.UTF8.GetString(filenameData.Array, filenameData.Offset, filenameData.Count);
Trace.WriteLine("Receiving file :" + filename);
ReadPacket(socket, (fileData) =>
{
if (fileData.Count == 0)
{
// disconnected
return;
}
Trace.WriteLine("Writing file :" + filename);
// write to the file
using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write))
stream.Write(fileData.Array, fileData.Offset, fileData.Count);
// start waiting for another packet.
StartReading(socket);
});
});
}
private void ReadPacket(Socket socket, Action<ArraySegment<byte>> endRead)
{
// read header. (length of data) (4 bytes)
EasySocketReader.ReadFromSocket(socket, 4, (headerBufferSegment) =>
{
// if the ReadFromSocket returns 0, the socket is closed.
if (headerBufferSegment.Count == 0)
{
// disconnected;
endRead(new ArraySegment<byte>());
return;
}
// Get the length of the data that follows
int length = BitConverter.ToInt32(headerBufferSegment.Array, headerBufferSegment.Offset);
// Check the length
if (length > MaxFileSize)
{
// disconnect
endRead(new ArraySegment<byte>());
return;
}
// Read bytes specified in length.
EasySocketReader.ReadFromSocket(socket, length, (dataBufferSegment) =>
{
// if the ReadFromSocket returns 0, the socket is closed.
if (dataBufferSegment.Count == 0)
{
endRead(new ArraySegment<byte>());
return;
}
endRead(dataBufferSegment);
});
});
}
The EasySocketReader class can be found on my blog: http://csharp.vanlangen.biz/network-programming/async-sockets/asyncsocketreader/
The original EasyPacketReader can be found here: http://csharp.vanlangen.biz/network-programming/async-sockets/easypacketreader/
For the sending part, you could use something like this:
private void SendFile(Socket socket, string filename)
{
byte[] filenameData = Encoding.UTF8.GetBytes(filename);
socket.Send(BitConverter.GetBytes(filenameData.Length));
socket.Send(filenameData);
int fileSize;
byte[] fileData;
using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
fileSize = (int)stream.Length;
if (fileSize > MaxFileSize)
throw new ArgumentOutOfRangeException("File too big");
fileData = new byte[fileSize];
stream.Read(fileData, 0, fileSize);
}
socket.Send(BitConverter.GetBytes(fileSize));
socket.Send(fileData);
}
I've written an application which listens to a port and receives some packets,according to my customized protocol, the packets are either 49 byte to 1500 byte, which i can tell from data length in the packet. the way i should interpret and deal with data in 49 byte packets and bigger packets are different.
The problem is that when i receive packets less than 1374 byte everything is ok, but when the packet length gets more, i receive the following exception and i also lose 4 last bytes of my data(i've tested with a 1384byte packet and i lost the last 4 bytes)
Exception which is raised:
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: startIndex
each 49 byte packet has 35 byte of data, and the data length of bigger packets are non-deterministic(because of compression).
i found out sometimes the last 4 bytes are in a seperate "bytes" and "result" variables,meaning they are being treated like new packets and are not being attached to the packet they belong to.
here's the code for receiving data:
TcpClient Client = obj as TcpClient;
EndPoint ep = Client.Client.RemoteEndPoint;
List<Byte> result = new List<byte>();
result.Capacity = 2000;
try
{
NetworkStream stream = Client.GetStream();
int i = 49;
while ((i = stream.Read(bytes, 0,49)) != 0)
{
for (int id = 0; id < i; id++)
{
result.Add(bytes[id]);
}
//reading data length to determine packet length
byte[] tmp = new byte[2];
tmp = BitConverter.GetBytes(BitConverter.ToUInt16(result.ToArray(), 9));
if (BitConverter.IsLittleEndian)
{
Array.Reverse(tmp);
}
Int16 l = BitConverter.ToInt16(tmp, 0);
if (l>35)
{
stream.Read(bytes, result.Count, l - 35);
for (int id = 49; id <((l-35)+49); id++)
{
result.Add(bytes[id]);
}
if (this.TCPDataReceivedHandler != null)
{
this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
result.Clear();
Array.Clear(bytes, 0, 2000);
result.Capacity = 2000;
}
}
else
{
if (this.TCPDataReceivedHandler != null)
{
this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
result.Clear();
Array.Clear(bytes, 0, 2000);
result.Capacity = 2000;
}
}
}
System.Diagnostics.Debug.WriteLine("client Close");
Client.Close();
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
Client.Close();
this.clients.Remove(Client);
}
According to Greg Suggestion and my researches,i also tried using following method:
NetworkStream stream = Client.GetStream();
int bytesread = 0, OffsetTemp = 0;
while (stream.CanRead)
{
OffsetTemp = 0;
bytesread += stream.Read(bytess, OffsetTemp, 11);
OffsetTemp = OffsetTemp + 11;
byte[] tmp = new byte[2];
tmp = BitConverter.GetBytes(BitConverter.ToUInt16(bytess.ToArray(), 9));
if (BitConverter.IsLittleEndian)
{
Array.Reverse(tmp);
}
Int16 l = BitConverter.ToInt16(tmp, 0);
bytesread += stream.Read(bytess, OffsetTemp++, 11 + l + 3);
for (int id = 0; id < l + 14; id++)
{
result.Add(bytess[id]);
}
if (this.TCPDataReceivedHandler != null)
{
this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
result.Clear();
Array.Clear(bytess, 0, 2000);
}
}
When using TCP, you must keep in mind that the size of the data blocks you put in is not guaranteed to be the same size of data blocks you get out on the receiving side. TCP is a stream protocol, so it guarantees that the same bytes get to the other side in the same order as they were sent (if not, the socket connection will be reset). TCP does not maintain any kind of block boundaries between calls to send(), and blocks may be arbitrarily split or coalesced depending on network conditions.
Your receiver must be prepared to handle receiving any amount of data from the calls to stream.Read(), but your code does not appear to do this. For example, correctly written receiver code should continue to work correctly even if stream.Read() receives only one byte at a time.
I want to communicate between my PC and some controller boards.
The expectation is that the PC will send an identifier of the board on RS-485 and then it should receive the answer from the board.
When I try to receive the response, I receive the wrong data.
Here is my code:
public void set()
{
SerialPort sp = new SerialPort("COM1");
sp.Open();
if (sp.IsOpen)
{
byte[] id = new byte[]{0,0,0,0,0,0,0,0,0,0};
byte[] rec = new byte[540];
while (!end)
{
sp.Write(id,0,id.Length);
sp.Read(rec,0,rec.Length);
//do some with rec
//WORKING
//do soem with rec
}
}
sp.Close();
}
It works if I am using RS-232, but not when I am using RS-485.
UPDATE :
It is RS-485 2 wire.(http://en.wikipedia.org/wiki/RS-485)
I found the problem.
sp.Read(rec,0,rec.Length);
Read is a non-blocking method so it reads the buffer but does not wait for all of the bytes. So you need to use the return value of this function which returns an integer with a number of bytes that it could read.
I am using this:
int read = 0;
int shouldRead = readData1.Length;
int len;
while (read < shouldRead )
{
len = serialport.Read(buffer, 0, readData1.Length);
if (len == 0)
continue;
Array.Copy(buffer, 0, readData1, read, len);
read += len;
Thread.Sleep(20);
}