Actual way of receiving data in system.net.socket - c#

I use multi threaded async socket for receiving data from XDomainRequest. I didn't have a problem about receiving data till now but I don't like my current way of doing that. So I wonder if there is a better idea.
Here is my StateObject:
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 256;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
// stores total byte length which comes from XDomainRequest
public int totalReceivedBuffer = 0;
// stores byte lenght comes when each call of ReceiveCallBack
public int receivedBuffer = 0;
}
In AcceptCallBack I create StateObject:
StateObject state = new StateObject();
state.workSocket = handler;
Then I have to do a weird thing to learn total received bytes:
System.Threading.Thread.Sleep(200);
state.totalReceivedBuffer = handler.Available;
If I don't put sleep, I get available value less than the actual value. In ReceiveCallBack method, I get received byte lenght for small part of data:
int numBytesReceived = handler.EndReceive(result);
I get StateObject which comes from AcceptCallBack and add that received bytes to it:
state = (StateObject)result.AsyncState;
handler = state.workSocket;
state.receivedBuffer += numBytesReceived;
then I control if it got equal to total bytes:
if(state.receivedBuffer == state.totalReceivedBuffer)
{
// do the job
}
else
{
// Call ReceiveCallBack again
}
Is there a better way of doing that?
EDIT:
I'm calling ReceiveCallBack at the end of AcceptCallBack like that:
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
My ReceiveCallBack function is here. I cut some of it:
public static void ReceiveCallback(IAsyncResult result)
{
StateObject state = null;
Socket handler = null;
try
{
state = (StateObject)result.AsyncState;
handler = state.workSocket;
string clientIP = ((IPEndPoint)handler.RemoteEndPoint).Address.ToString();
// See how many bytes received
int numBytesReceived = handler.EndReceive(result);
if (!handler.Connected)
{
handler.Close();
return;
}
if (numBytesReceived > 0)
{
state.receivedBuffer += numBytesReceived;
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, numBytesReceived));
// Read all data line by line
string[] lines = state.sb.ToString().Split('\n');
if (state.receivedBuffer == state.totalReceivedBuffer)
{
// All data came
// do the job according to lines array
}
else
{
// continue receiving
handler.BeginReceive(state.buffer, 0, state.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
}
}
}
catch (ObjectDisposedException ex)
{
// Don't care
}
catch (SocketException ex)
{
#region SocketException
if (ex.ErrorCode == 10054)
{
// HATA: "An existing connection was forcibly closed by the remote host"
Globals.ErrorLogYaz(_log, Globals.GetCurrentMethod(), "Karşı taraf işlem tamamlanmadan bağlantıyı kapattı!", ex.StackTrace);
handler.Close();
}
#endregion
}
catch (Exception ex)
{
Globals.ErrorLogYaz(_log, Globals.GetCurrentMethod(), ex.Message, ex.StackTrace);
handler.Close();
}
}

The way to read data from a connection is to actually call Receive and examine how many bytes you got. There is no way to predict how many will come. Available is the amount of data available right now. How could the receiver possibly know how much more the sender will send in the future? It can't know.
Call Receive in a loop until it returns 0 (the connection is closed) or until you know by some other means that all bytes are received.
Since you seem to be a beginner with sockets and there does not seem to be a reason to use async you probably should use synchronous IO. It is much simpler.

Related

TCPIP Asynchronous Server connection dropout

i have written(Copied) code for a tcpip server.
We have software that sends a message via TCPIP and expects a return message back to say Receive is Acknowledged. This works fine. But after sending the acknowledgment the connection drops out. I am not sure where in the code this dropout is occurring. So when the next message is sent, there is no connection so it does a rollback and reconnects again and then send the message. So every time i send multiple messages i will keep getting rollback alerts. We will be constantly sending messages all day long. Below is the code :
class Program
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener()
{
}
public static void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
ipAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 8080);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
ar.AsyncWaitHandle.WaitOne(100);
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
String s1 = "MSH|^~\\&|BB|NSCCH|MILLENNIUM|BB|20150522080258137||ACK";
String s2 = "|20150522080258137883|P|2.4";//\r\nMSA|AA|Q245548634T287475114";
//String s3 = String.Empty;
String s4 = "MSA|AA|Q245548634T287475114";
//string s5 = "0x02";
int value = Convert.ToInt32("0x1C", 16);
char cc = (char)value;
int value_eob = Convert.ToInt32("0x0b", 16);
char eob = (char)value_eob;
int value_eod = Convert.ToInt32("0x0D", 16);
char eod = (char)value_eod;
int value_eoa = Convert.ToInt32("0x0A", 16);
char eoa = (char)value_eoa;
/*StringBuilder sb1 = new StringBuilder(s5);
sb1.Append(s1);
sb1.Append(s2);
s3 = sb1.ToString();*/
//byte[] s4 = Encoding.ASCII.GetBytes(s3);
// 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));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
//s3 = state.sb.ToString();
// s3 = s1.ToString() + s2.ToString();
if (bytesRead < 1024) //content.IndexOf("<EOF>") > -1)
{
string s6 = eob.ToString() + s1 + s2 + eod.ToString() + s4 + cc.ToString() + eod.ToString() + eoa.ToString();// "\r\n";// +cc.ToString() + "\r\n";
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
Console.WriteLine("Send {0} bytes from socket. \n Data : {1}",
s6.Length, s6);
// Echo the data back to the client.
Send(handler, s6);
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
else
{
if (content.Length > 0)
{
string s6 = eob.ToString() + s1 + s2 + eod.ToString() + s4 + cc.ToString() + eod.ToString() + eoa.ToString();// "\r\n";// +cc.ToString() + "\r\n";
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
Console.WriteLine("Send {0} bytes from socket. \n Data : {1}",
s6.Length, s6);
// Echo the data back to the client.
Send(handler, s6);
}
}
// Added code
handler.BeginReceive(state.buffer,0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
private static void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
// handler.Shutdown(SocketShutdown.Both);
// handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(string[] args)
{
StartListening();
return 0;
}
}
}
You have at least two major bugs in your code:
You are assuming that a receive operation will always complete having filled the provided buffer, unless the client has stopped sending data (i.e. there's no more data to be received at the moment). This is often going to be true, but it is not guaranteed and your code will fail spuriously if it makes that assumption.
Once you believe that you have received all of the current transmission from the client, you do not call BeginReceive() again. This renders the connection completely useless from that point forward.
Another possible bug is:
Your code does not correctly partition transmitted data. There does not seem to be any code here that will reliably detect the boundary between two different logical messages in the stream of bytes received. Depending on how quickly data is sent by a client, it is possible that some or all data intended to be part of a later logical message is consumed as part of an earlier logical message.
Unfortunately, there is not enough precise detail provided in your question. The phrase "connection drops out" is non-specific and has no well-defined technical meaning. It could mean you get an error on the socket, or it could simply mean that you never receive any more data, or it could mean something else entirely.
That said, if you mean "never receive any more data", then I would say it is most likely that the #2 bug above is the basic cause of your problem. Of course, the #1 bug can in conjunction with the #2 bug also lead to "never receive any more data".
Without a good, minimal, complete code example that reliably reproduces the problem (please note that for network-related issues, a complete example includes both endpoints of the connection), it's impossible to know for sure all of the bugs in the code example, never mind what the actual problem is. However, I would recommend fixing the above bugs and see if that improves things.
As a general rule, your network code should run like this:
Receive data
Interpret the data, identifying logical message boundary (if any)
If a complete logical message has been received, process it. Be sure to retain any excess data that might have been received, for processing as part of the next logical message.
Continue the above, always initiating a new receive operation after handling the results of the current completed operation, until a receive operation completes with a byte count of zero.
When a zero-byte receive operation completes, complete processing of the data received so far (if there is any not yet processed) and acknowledge the graceful closure of the connection by sending any remaining data (if any) and calling Socket.Shutdown(SocketShutdown.Both), and only then calling Socket.Close() to actually close the socket.

use asynchronus socket to receive data

I use 2 ways to receive data with a socket connected to an IP address.
Synchronous receive.
Asynchronous receive.
Using synchronous receive, I get all response from the server in less than 1 second. But by using asynchronous receive, it takes over 30 seconds to receive all the responses.
Here is the code:
class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
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());
}
}
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.ReceiveTimeout = 500;
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)
{
MessageBox.Show(e.ToString());
}
}
You should check for your value after you append the current string to your buffer, in order to process the message and remove the client.ReceiveTimeout = 500; which i think is taking time:
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
response = state.sb.ToString();
if (response.IndexOf("<EOF>") != -1)
{
state.sb.Clear();
receiveDone.Set();
}
else
{
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
}
This will only work for one message with , for real-time multiple messages, I think you should try different approach.

Reading information from a port waits even though data available

I am having a problem sending information down a socket and receiving a response. I have a demo program which is performing correctly so I know it is not an issue with the client on the other end.
The requestData is sent and the client acts correctly and responds, but my code never exits the loop in read response.
Could the client be responding before I am listening? How can I make sure I never miss an incoming message?
networkStream = tcpClient.GetStream();
StreamWriter clientStreamWriter = new StreamWriter();
clientStreamWriter.WriteLine(requestData);
clientStreamWriter.Flush();
// Continuously read data on the socket and if it is more than just a ping, read the response.
StringBuilder sbReadBuffer = new StringBuilder();
while (true)
{
String response = readresponse(timeoutOn30Seconds);
if (response.Length > 1 && (!response.Contains("\r\n") || response.Contains(",")))
{
break;
}
}
sbReadBuffer.Append(received);
return sbReadBuffer.ToString();
readResponse:
private string readresponse(Boolean timeoutOn30Seconds)
{
// Get network stream.
DateTime lastConTime = DateTime.Now;
Int32 i = 0;
// Start listening to stream.
while (!networkStream.DataAvailable)
{
Log.W(".");
// Only check every 10ms.
Thread.Sleep(10);
// If no response in 30mins stop listening and give an offline warning.
if ((DateTime.Now - lastConTime).TotalSeconds > tcpClient.ReceiveTimeout)
{
received = "CLIENT NOT FOUND";
return received;
}
// Only check if application online every 1s.
if (i > 100)
{
if (Process.GetProcessesByName(ConfigurationManager.AppSettings["ClientName"]).FirstOrDefault() == null && Convert.ToInt32(ConfigurationManager.AppSettings["Device"]) != 680)
{
received = "CLIENT NOT FOUND";
return received;
}
i = 0;
}
i++;
}
// If data has been writted to the buffer, read it out and assign output variable and return that string for length comparison.
byte[] receiveBuffer = new byte[tcpClient.ReceiveBufferSize];
Int32 receiveCount = networkStream.Read(receiveBuffer, 0, receiveBuffer.Length);
received = new ASCIIEncoding().GetString(receiveBuffer, 0, receiveCount);
return received;
}
DataAvailable isn't a good method to know if data are coming, especially in a while loop that is surely faster than network communications.
A better way could be to use the Read method (bytes read) to know where data are available, into a timed loop; so change your while condition in this manner (and then adjust the other parts)
while (networkStream.Read(receiveBuffer, 0, receiveBuffer.Length) > 0)
{
Log.W(".");
// Only check every 10ms.
Thread.Sleep(10);
but I prefer, if possible, an async approach, so your client will be notified when data are incoming.
See this answer that use this kind of approach.
Basically set an async callback that will be fired when data are coming
public void StartListening() {
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPEndPoint localEP = new IPEndPoint(ipHostInfo.AddressList[0],11000);
Console.WriteLine("Local address and port : {0}",localEP.ToString());
Socket listener = new Socket( localEP.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp );
try {
listener.Bind(localEP);
listener.Listen(10);
while (true) {
allDone.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(SocketListener.acceptCallback),
listener );
allDone.WaitOne();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine( "Closing the listener...");
}
and there you can read your data
public static void acceptCallback(IAsyncResult ar) {
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Signal the main thread to continue.
allDone.Set();
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(AsynchronousSocketListener.readCallback), state);
}
Here full MSDN documentation
You dont read in while loop. To clear DataAvailable flag, you have to read from networkStream.
Sample usage :
http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.dataavailable%28v=vs.100%29.aspx

c# asynchronous server with beginReceive

Hi stack overflow members.
I'm struggling with some simple code but I can't get it done.
I have this asynchronous server which waits for connections.
while (clientSocket.Connected)
{
try
{
clientSocket.BeginReceive(so.buffer, 0, 200, SocketFlags.None
, new AsyncCallback(wait),so);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
where so(shorted StateObject) it's my class:
internal class StateObject
{
public TcpClient client;
public byte[] buffer;
public StateObject()
{
buffer = new byte[200];
client = new TcpClient();
}
}
I use this class to put out the information on the callback function. However I get the system lacked sufficient buffer space or because a queue was full.
I posted a short piece from the actual program.
One interesting issue, is that if I write:
while (clientSocket.Connected)
{
try
{
byte[] buffer = new byte[200];
clientSocket.BeginReceive(buffer, 0, 200, SocketFlags.None
, new AsyncCallback(wait),so);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
it will work, but I will not be able to pull out the buffer from the asynchronous function(wait).
I'm struggling with this and I can't find answers.
The while loop shouldn't be there, just call beginreceive after the endreceive.
This is a poor example, but may give you some ideas:
public class AsyncTCP
{
public void StartReceive()
{
byte[] buffer = new byte[200];
clientSocket.BeginReceive(buffer, 0, 200, SocketFlags.None, (state) =>
{
int bytesReceived = clientSocket.EndReceive(state);
// handle buffer.
if(bytesReceived != 0)
StartReceive();
} ,so);
}
}
If it's about getting the state within the EndReceive handler:
private void StartReceive()
{
StateObject myState = new StateObject();
myState.buffer = new byte[200];
myState.client = _client; // or whatever
myState.client.BeginReceive(so.buffer, 0, 200, SocketFlags.None, new AsyncCallback(wait),myState);
}
private void EndReceive(IAsyncResult result)
{
StateObject myState = (StateObject)result.State;
int bytesReceived = myState.client.EndReceive(result);
// handle myState.buffer
StartReceive();
}
I think there are better ways to do this, like:
- only constructing a receive buffer ones.
- put some packet header/data with lengths in it.
Good luck
How about using some TPL functions. So your code can be simplified a lot
int readBytes = await s.ReceiveTaskAsync(buffer, 0, buffer.Length);
This is the extension method ReceiveTaskAsync
public static class SocketExtensions
{
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
}
The problem is here
while (clientSocket.Connected)//checks connected
{
try
{
clientSocket.BeginReceive(so.buffer, 0, 200, SocketFlags.None, new AsyncCallback(wait),so);//says begin receive and continues to do endlessly
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
You've to call BeginReceive again only after you received the data.
Here's and example from msdn how to do that.
I resolved my previous problem(I just needed to remove the while loop, however I messed it up with some code from a synchronous server).
Now I have another problem, so I will use this same thread.
From what I've searched, and understood, you can open an asynchronous server with BecinAccept like this(main method):
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 8082);
TcpListener tcpListener = new TcpListener(IPAddress.Any, 8082);
server = tcpListener.Server;
server.Bind(ipEndPoint);
server.Listen(4);
server.BeginAccept(new AsyncCallback(beginConnection), server);
Then:
static void beginConnection(IAsyncResult iar)
{
Console.WriteLine("Client connected");
Socket s = (Socket)iar.AsyncState;
server = s.EndAccept(iar);
server.Listen(4); // this line was initially absent
server.BeginAccept(beginConnection, s);
}
I want to be able to connect to this server multiple clients.
However, when I try to do this, only the first client connects itself.
The client it's a simple socket, which just echoes back from the server, what we read from the console.
I thought that since in the main method I've called server.Listen(4), I will be able to connect 4 clients.
Then I thought of calling recursively BeginAccept in the beginConnection method.
And last, I received the error, that I must call first the Listen method, so I added that too.
Still no luck.

Server/Client. The Client uses a lot of memory

This is my first question and I hope you can help me.
I have 2 programs, a Server and a Client and my problem is in the Client.
After 2 or 3 days of running, it uses more than 300MB of RAM (I can tell this by seeing the TaskManager) and never releases it! Also, I have to say that this Client receives data every second from GPS devices.
I have analyzed my Client with the ANTS Memory Profiler and I noticed that I create an object several times and they are never destroyed.
Here is my code:
private static TcpClient _client;
private readonly ManualResetEvent _receiveDone = new ManualResetEvent(false);
public void Receive()
{
if (_client.Connected)
{
_receiveDone.Reset();
ObjectState state = new ObjectState();
state.WorkSocket = _client.Client;
state.Data = new byte[_client.ReceiveBufferSize];
_client.Client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);
if (!_receiveDone.WaitOne(20000))
{
//after 20 seconds WITHOUT RECEIVING DATA, do some code to test if the connection is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
ObjectState state = (ObjectState)ar.AsyncState;
Socket client = state.WorkSocket;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(state.Data, 0, bytesRead);
doProcess(response);
client.BeginReceive(state.Data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, state);
_receiveDone.Set();
}
else
{
//Disconnection
}
}
}
public class ObjectState
{
public Socket WorkSocket;
public byte[] Data;
}
The ANTS Memory Profiler tells me I have thousands of live instances of byte[]. (because I always create new instances of ObjectState)
First thing I wanted to do: Dispose all the ObjectState that I create after I call BeginReceive, but I only get the first message.
Then I wanted to stop using the ObjectState... How?
This is my modified code:
private _data byte[];
public void Receive()
{
if (_client.Connected)
{
_receiveDone.Reset();
_data = new byte[_client.ReceiveBufferSize];
_client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
if (!_receiveDone.WaitOne(20000))
{
//after 20 seconds of inactivity do some code to test if the connectio is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
Socket client = _cliente.Client;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
doProcess(response);
client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
_receiveDone.Set();
}
else
{
//Disconnection
}
}
}
What's wrong with this? I only get the first message, so this is not good.
Then if I remove the _receiveDone.Set it gets all the messages, BUT the _receiveDone.WaitOne(2000) always is executed every 20 seconds, no matter if I actually am receiving data, and this is not good either.
So my question is, what can I do to reduce the using of so much RAM? I hope this explains well what my problem is.
EDIT:
I uploaded these images, I hope they can be helpful too.
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
// etc..
Calling EndReceive is not optional, you have to call it or you'll leak resources. Use try/catch to catch an ObjectDisposedException.
I think the primary problem is that you're depending on BeginReceive to return 0 bytes when there's nothing available. But that's not how it works. BeginReceive will block until there is data available, or until the client disconnects.
In the first case, the ReceiveCallback passes the state object to client.BeginReceive. Now you have an ObjectState instance out there waiting for data from the client. The _receiveDone event is set, so your Receive method exits but the client connection is still open and you have a pending asynchronous read.
The next time Receive is called, it will create another ObjectState instance and issue another asynchronous read request.
The second case is pretty scary. Your _data array is at class scope and gets reallocated every time. So it's possible that an asynchronous read can fill the buffer you pass it, but then the data is lost when you get to ReceiveCallback.
Your code only does the timeout check for the first receive. After that, things can hang indefinitely. I would suggest something more like:
private _data byte[];
private bool _receiving = false;
public void StartReceiving()
{
if (_receiving)
{
// ERROR: Already receiving
throw new InvalidOperationException();
}
_receiving = true;
Receive();
}
public void Receive()
{
if (_client.Connected)
{
_data = new byte[_client.ReceiveBufferSize];
var ir _client.Client.BeginReceive(_data, 0, Convert.ToInt32(_client.ReceiveBufferSize), 0, ReceiveCallback, null);
if (!_ir.WaitOne(20000))
{
//after 20 seconds of inactivity do some code to test if the connectio is alive
}
}
}
void ReceiveCallback(IAsyncResult ar)
{
Socket client = _cliente.Client;
int bytesRead;
try
{
bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.ASCII.GetString(_data, 0, bytesRead);
doProcess(response);
// Calling Receive here ensures that the timeout check happens
// with every read.
Receive();
}
else
{
_receiving = false;
}
}
catch
{
// Catch SocketException and others here
}
}
Note that you don't need the _receiveDone event. You can check it with the IAsyncResult.WaitHandle.

Categories