How to asynchronously receive complex object in C#? - c#

EDIT:
A more concise explanation of what I was trying to do here and with answers
I'm using c# asynchronous sockets to receive data from a source.
My problem is how and where to store received data if there are more to be received?
When a string is received, I can just use a string builder to receive and store just like this from msdn:
private void ReceiveCallback_onQuery(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
dataReceived += state.buffer; //Does not work (dataReceived is byte[] type)
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback_onQuery), state);
}
else
{
// All the data has arrived; put it in response.
if (dataReceived > 1)
{
response_onQueryHistory = ByteArrayToObject(dataReceived)
}
// Signal that all bytes have been received.
receiveDoneQuery.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
I dont want to convert my received data into a string as in my case, I am receiving a complex object.
The data sent was serialized and I'm also fine with deserializing.
My question how to 'continually' receive data from the socket without using the string builder to store it.
Thanks!

This depends on how the complex thing is serialized prior to being pushed down the wire in the broken down bytes, you will receive those bytes and use the same algorithm/technique used to serialize the thing to deserialize it back to its original state.
For a more specific answer I'd ask that you be more specific yourself.

My problem is how and where to store received data if there are more to be received?
Can use Buffer.BlockCopy and queue it up, for eg,
int rbytes = client.EndReceive(ar);
if (rbytes > state.buffer)
{
byte[] bytesReceived = new byte[rbytes];
Buffer.BlockCopy(state.buffer, 0, bytesReceived, 0, rbytes);
state.myQueue.Enqueue(bytesReceived);
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback_onQuery), state)
}

Related

c# TCP socket how to validate incoming string and process it

I have a C# TPC Socket with the following code:
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// HERE WE NEED TO MAKE SURE THAT THE MESSAGE IS COMPLETE, IF NOT THEN READ MORE DATA
if (bytesRead == ***something***)
{
//Do something HERE...
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
The data that will be sent to the TCP Socket will be formed like this:
So the size can be different for every message. The first 10 bytes and the last 4 bytes are always fixed, but the payload is dynamic.
So I have to implement a way of validating the size of the message by intercepting the 4 bytes of the PAYLOAD SIZE position, that way is just matter of doing a sum of 2 + 4 + 4 + payload size + 4 so i can enter the if statement where I will do some other stuff there.
Any advice or clue on the best way to do this?
You can do it like this:
static void ReadCallback(IAsyncResult ar)
{
//put received bytes into queue for further processing
//initiate BeginReceive
}
On another thread:
static void ProcessMessages()
{
while(true)
{
//read messages from queue
//check message frames
}
}
You can check how it can be implemented in simplsockets, method ProcessReceivedMessage.

SslStream EndRead gets first 1 Byte

I've written a TcpClient and Server which are communicating via an SslStream.
The communication works, but when i send a message from the Client to the Server, first the Server reads 1 Byte, and in the next step the rest. Example: I want to send "test" via Client, and the Server receives first "t", and then "est"
Here is the code for the Client to send
public void Send(string text) {
byte[] message = Encoding.UTF8.GetBytes(text);
SecureStream.BeginWrite(message, 0, message.Length, new AsyncCallback(WriteCallback), null);
}
private void WriteCallback(IAsyncResult AR) {
}
And here the code the Server uses to read
private SslStream CryptedStream = ...;
private byte[] buffer = new byte[1024];
public void BeginReadCallback(IAsyncResult AsyncCall) {
// initialize variables
int bytesRead = 0;
try {
// retrieve packet
bytesRead = CryptedStream.EndRead(AsyncCall);
// check if client has disconnected
if (bytesRead > 0) {
// copy buffer to a temporary one
var temporaryBuffer = buffer;
Array.Resize(ref temporaryBuffer, bytesRead);
string read = Encoding.ASCII.GetString(temporaryBuffer);
SetText(read);
// read more data
CryptedStream.BeginRead(buffer, 0, 1024, new AsyncCallback(BeginReadCallback), null);
// client is still connected, read data from buffer
//ProcessPacket(temporaryBuffer, temporaryBuffer.Length, helper);
} else {
// client disconnected, do everything to disconnect the client
//DisconnectClient(helper);
}
} catch (Exception e) {
// encountered an error, closing connection
// Program.log.Add(e.ToString(), Logger.LogLevel.Error);
// DisconnectClient(helper);
}
}
Did i miss something?
Thanks for your help
As Lasse explained streaming APIs do not promise you to return a specific number of bytes per read.
The best fix for this is to not use sockets. Use a higher level API such as WCF, SignalR, HTTP, ...
If you insist you probably should use BinaryReader/Writer to send your data. That makes it quite easy. For example, it has string sending built-in. You also can manually length-prefix easily with those classes.
Probably, you don't need async IO and should not use it. If you insist you can at least get rid of the callbacks by using await.

C# Socket Receive: Taking in data until buffer is full dilemma

Here is my code I'm having an issue with:
// read the file in chunks of 5KB
var buffer = new byte[1024*5];
int bytesRead = 0;
do
{
bytesRead = host.Receive(buffer, buffer.Length, 0);
output.Write(buffer, 0, bytesRead);
}
while (bytesRead == buffer.Length);
So here's my problem: I want to read in data as long as the buffer is full. However, even if there is more data, the buffer isn't guaranteed to be filled when sending. This causes the Receive to prematurely exit. If I change the while condition to bytesRead > 0 then it reaches the end of data and Receive blocks until more data is available (which it won't be). How do I solve this?
I think you need to think about how the protocol works and make a more robust solution. Either you go with blocking and then you could either wait until you have no more data to read and then do something else and read again when you know you have more data to read.
Or you add threading and have a seperate thread that just reads and then it doesn't matter if it blocks since it will be on a seperate thread.
Another solution that might be simpler is to use asynchronous reads.
You can read more about it here: https://msdn.microsoft.com/en-us/library/bbx2eya8(v=vs.110).aspx
A simple example from the site above is to start with the read:
private static void Receive(Socket client) {
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Then you will get a callback to ReceiveCallback when there is data to be handled:
private static void ReceiveCallback( IAsyncResult ar ) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
I hope this works if not we need to know more about what protocol you use and if there is some reason you need blocking reads so we can give more specific information and help.

Ensure all data was received

I am following this example but I am running into a problem. The sequence of my client/server connection goes like this:
Server is always listening for new clients
Client initiates connection
Server sends a small packet with connection information (9 bytes), client receives
Client sends response connection information, server receives
More back and forth with packets
My code is getting stuck during step 3 because the first call to BeginReceive is actually receiving all the data at once, but it goes into another call to BeginReceive even though there is no more data to get. I am using a custom server implementation that already functions communicating in this same fashion with an iOS application.
Is there a simple check I can perform to see if there is indeed more data to be received on the socket?
Here is my implementation of the receive callback function, the start receive function is identical:
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
Console.WriteLine ( "Received data" );
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
//First pass reads all 9 bytes the server sent
Console.WriteLine ( "Received bytes: {0}", bytesRead );
// There might be more data, so store the data received so far.
state.ms.Write(state.buffer, 0, bytesRead);
// Get the rest of the data.
//Gets stuck in this call because there is no more data to receive
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
Console.WriteLine ( "Received all data? {0}", state.ms.Length );
// All the data has arrived; put it in response.
if ( state.ms.Length > 0 )
{
Console.WriteLine ( "Received whole packet" );
MyPacketInfo.MyPacket receivedPacket = MyPacketInfo.MyPacket.packetFromMemoryStream ( state.ms );
if ( receivedPacket != null )
{
state.caller.ReceivedPacket ( receivedPacket, client );
}
else
{
Console.WriteLine ( "Received bad packet!" );
}
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

Passing and Receiving complex objects in C# Sockets [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to asynchronously receive complex object in C#?
My complex object is of type IOrderedQueryable
It has 4 attributes, all of type List
I'm sending my object using asynchronous socket through this:
private void SendDatabaseObj(Socket handler, IOrderedQueryable<BuildHistory1> buildHistoryQueryResult)
{
byte[] byteData = ObjectToByteArray(buildHistoryQueryResult);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
ObjectToByteArray() function (Serializing the object before sending):
private byte[] ObjectToByteArray(Object obj)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
I'm receiving the object I sent through this:
private void ReceiveCallback_onQuery(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far. But how to store?
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback_onQuery), state);
}
else
{
// All the data has arrived; put it in response.
if (dataReceived > 1)
{
//Use the deserializing function here to retrieve the object to its normal form
}
// Signal that all bytes have been received.
receiveDoneQuery.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
My de-serializing function:
private Object ByteArrayToObject(byte[] arrayBytes)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
ms.Write(arrayBytes, 0, arrayBytes.Length);
ms.Seek(0, SeekOrigin.Begin);
Object obj = (Object)bf.Deserialize(ms);
return obj;
}
Now my question is in the receiving function "ReceiveCallback_onQuery()". If there are more data to be received, how to store previously received data?
EDIT:
I am aware of doing the code below but is there other ways of just storing the data received in a byte[] variable so I could convert them back to IOrderedQueryable
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
When streaming data, such as with sockets, you have to have, built-in to your protocol, some way to know where the beginning and ending of each message is in the data. This is handled automatically by tools for standard protocols (such as WCF for SOAP), but if you are going to design your own protocol, you'll need to implement it yourself. The simplest method would be to add a known delimiter character or string between each message, but then you need to be careful that the delimiter never appears within a message's data. Another alternative would be to send the length of the message in a message header, or to simply use fixed-length messages.
Don't pass a IQueryable over the network. You can't use the querying features on the other side. Instead create a class which represents a request (i.e. contains information about which items to receive) and a response (an object which contains an array of matching objects).
If you do not want to take care of the networking layer by yourself and want's a more lightweight approach than WCF you can use my Griffin.Networking library.
I've just uploaded a video that demonstrates how to create a simple chat client/server in 20 minutes: https://www.youtube.com/watch?v=DjJOaV2y1Lo
Sample code: https://github.com/jgauffin/Samples/tree/master/Griffin.Networking/ChatServerClient

Categories

Resources