I'm used to using synchronous sockets. In order to deal with messages that have not completely arrived yet, I'd set the first 4 bytes to be the expected length of the message. Then I'd use Socket.Receive(tcpRecv, 1024, SocketFlags.Peek); to take a look at the message without pulling it off the buffer. If all of it was there, I'd pull the data. If it wasn't, I'd leave it there. I had designed my protocol so that no message would ever be greater than 1024 bytes.
In asynchronous sockets, I don't see a way to peek at the data. Is there a way to do this? Is there a better approach to this than peeking at the data?
Thanks.
-Nick
You don't need to peek: .NET asynchronous sockets allow you to achieve the same type of functionality without peeking. I think you might be looking for something like this:
private void BeginReceive()
{
if ( _clientState == EClientState.Receiving)
{
if (_asyncTask.BytesReceived != 0 && _asyncTask.TotalBytesReceived <= _maxPageSize)
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.SetBuffer(_asyncTask.ReceiveBuffer, 0, _asyncTask.ReceiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(ReceiveCallback);
e.UserToken = _asyncTask.Host;
bool comletedAsync = false;
try
{
comletedAsync = _socket.ReceiveAsync(e);
}
catch (SocketException se)
{
Console.WriteLine("Error receiving data from: " + _asyncTask.Host);
Console.WriteLine("SocketException: {0} Error Code: {1}", se.Message, se.NativeErrorCode);
ChangeState(EClientState.Failed);
}
if (!comletedAsync)
{
// The call completed synchronously so invoke the callback ourselves
ReceiveCallback(this, e);
}
}
else
{
//Console.WriteLine("Num bytes received: " + _asyncTask.TotalBytesReceived);
ChangeState(EClientState.ReceiveDone);
}
}
}
When you get the callback you can schedule another receive:
private void ReceiveCallback(object sender, SocketAsyncEventArgs args)
{
lock (_sync) // re-entrant lock
{
// Fast fail: should not be receiving data if the client
// is not in a receiving state.
if (_clientState == EClientState.Receiving)
{
String host = (String)args.UserToken;
if (_asyncTask.Host == host && args.SocketError == SocketError.Success)
{
try
{
Encoding encoding = Encoding.ASCII;
_asyncTask.BytesReceived = args.BytesTransferred;
_asyncTask.TotalBytesReceived += _asyncTask.BytesReceived;
_asyncTask.DocSource += encoding.GetString(_asyncTask.ReceiveBuffer, 0, _asyncTask.BytesReceived);
BeginReceive();
}
catch (SocketException e)
{
Console.WriteLine("Error receiving data from: " + host);
Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
ChangeState(EClientState.Failed);
}
}
else if (_asyncTask.Host != host)
{
Console.WriteLine("Warning: received a callback for {0}, but the client is currently working on {1}.",
host, _asyncTask.Host);
}
else
{
Console.WriteLine("Socket Error: {0} when receiving from {1}",
args.SocketError,
_asyncTask.Host);
ChangeState(EClientState.Failed);
}
}
}
}
You can see the entire asynchronous client on my blog: http://codesprout.blogspot.com/2011/04/asynchronous-http-client.html
Your same data flow works without peeking:
schedule a four byte read
when it completes, save it in the buffer and decode it into length "n"
schedule a read of length "n" - 4
when it completes, append it to the four bytes already there
decode your message
The only difference from peeking is that you have to save the four bytes when you initially read them.
Related
I have some code that used to work for many years and even now in specific cases it works but in other cases I just cannot understand why it fails.
The following code is part of a Client class that uses a System.Net.Sockets.Socket for communication:
protected ConcurrentQueue<byte[]> ReadQueue { get; } = new ConcurrentQueue<byte[]>();
private void ReadTimer_Tick(object sender, EventArgs e) {
ReadTimer.Stop();
try
{
while (ReadQueue.Count > 0 && !IsDisposing)
{
try
{
if (this.ReadQueue.TryDequeue(out var data))
{
[...]
}
}
catch (Exception ex)
{
[...]
}
}
}
catch (Exception)
{
[...]
}
finally
{
if (IsConnected && !IsDisposing) ReadTimer.Start();
}
}
protected void EnqueueData(IEnumerable<byte> data)
{
ReadQueue.Enqueue(data.ToArray());
}
The ReadTimer ticks every millisecond if it is not stopped in order to process data from the ConcurrentQueue.
There are two uses of the code:
First case
I open a connection to a Socket port. After the connection is established I call the Socket.BeginReceive method of the Socket.
Second case
I listen to a Socket port and call the Socket.BeginAccept method. Within the ´callback´ method of BeginAccept I also call the BeginReceive method of the Socket.
In both cases the same method is called:
private void StartReceiving(SocketAnswerBuffer state)
{
try
{
Status = ClientStatus.Receiving;
_ = state.Socket.BeginReceive(
state.Buffer, 0,
state.Buffer.Length,
SocketFlags.None,
ReceiveCallback,
state
);
}
catch (Exception ex)
{
[...]
}
}
So in both cases the ReceiveCallback is used to handle incoming data:
private void OnReceive(IAsyncResult result)
{
if (result.AsyncState is SocketAnswerBuffer state)
{
try
{
var size = state.Socket.EndReceive(result);
if (size > 0)
{
var data = state.Buffer.Take(size).ToArray();
EnqueueData(data);
}
}
catch (Exception ex)
{
[...]
}
finally
{
Status = ClientStatus.Connected;
if (state != null && state.Socket.Connected)
StartReceiving(state);
}
}
}
In both cases the EnqueueData method is called.
In the first case everything works. When the ReadTimer ticks ReadQueue.Count is more than 0 and the loop handles all data collected so far and processes it.
In the second case EnqueueData is also called and enqueues data to the ReadQueue. But when the ReadTimer ticks ReadQueue.Count is 0 and nothing works.
What I really cannot understand is that debugging the code shows that ReadQueue.Count is larger than 0 on EnqueueData and the ReadQueue even grows but in ReadTimer_Tick the ReadQueue remains empty ... I neither clear nor redeclare ReadQueue and ReadTimer_Tick is the only method in code that tries to dequeue the data from ReadQueue.
Somehow creating a new class that includes the Timer, the ConcurrentQueue and the method that proceeds the data and using this class inside the class with the Socket forced the ConcurrentQueue to be in sync with the Timer and the method.
I have a TCP/IP server that is supposed to allow a connection to remain open as messages are sent across it. However, it seems that some clients open a new connection for each message, which causes the CPU usage to max out. I tried to fix this by adding a time-out but still seem to have the problem occasionally. I suspect that my solution was not the best choice, but I'm not sure what would be.
Below is my basic code with logging, error handling and processing removed.
private void StartListening()
{
try
{
_tcpListener = new TcpListener( IPAddress.Any, _settings.Port );
_tcpListener.Start();
while (DeviceState == State.Running)
{
var incomingConnection = _tcpListener.AcceptTcpClient();
var processThread = new Thread( ReceiveMessage );
processThread.Start( incomingConnection );
}
}
catch (Exception e)
{
// Unfortunately, a SocketException is expected when stopping AcceptTcpClient
if (DeviceState == State.Running) { throw; }
}
finally { _tcpListener?.Stop(); }
}
I believe the actual issue is that multiple process threads are being created, but are not being closed. Below is the code for ReceiveMessage.
private void ReceiveMessage( object IncomingConnection )
{
var buffer = new byte[_settings.BufferSize];
int bytesReceived = 0;
var messageData = String.Empty;
bool isConnected = true;
using (TcpClient connection = (TcpClient)IncomingConnection)
using (NetworkStream netStream = connection.GetStream())
{
netStream.ReadTimeout = 1000;
try
{
while (DeviceState == State.Running && isConnected)
{
// An IOException will be thrown and captured if no message comes in each second. This is the
// only way to send a signal to close the connection when shutting down. The exception is caught,
// and the connection is checked to confirm that it is still open. If it is, and the Router has
// not been shut down, the server will continue listening.
try { bytesReceived = netStream.Read( buffer, 0, buffer.Length ); }
catch (IOException e)
{
if (e.InnerException is SocketException se && se.SocketErrorCode == SocketError.TimedOut)
{
bytesReceived = 0;
if(GlobalSettings.IsLeaveConnectionOpen)
isConnected = GetConnectionState(connection);
else
isConnected = false;
}
else
throw;
}
if (bytesReceived > 0)
{
messageData += Encoding.UTF8.GetString( buffer, 0, bytesReceived );
string ack = ProcessMessage( messageData );
var writeBuffer = Encoding.UTF8.GetBytes( ack );
if (netStream.CanWrite) { netStream.Write( writeBuffer, 0, writeBuffer.Length ); }
messageData = String.Empty;
}
}
}
catch (Exception e) { ... }
finally { FileLogger.Log( "Closing the message stream.", Verbose.Debug, DeviceName ); }
}
}
For most clients the code is running correctly, but there are a few that seem to create a new connection for each message. I suspect that the issue lies around how I handle the IOException. For the systems that fail, the code does not seem to reach the finally statement until 30 seconds after the first message comes in, and each message creates a new ReceiveMessage thread. So the logs will show messages coming in, and 30 seconds in it will start to show multiple messages about the message stream being closed.
Below is how I check the connection, in case this is important.
public static bool GetConnectionState( TcpClient tcpClient )
{
var state = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.FirstOrDefault( x => x.LocalEndPoint.Equals( tcpClient.Client.LocalEndPoint )
&& x.RemoteEndPoint.Equals( tcpClient.Client.RemoteEndPoint ) );
return state != null ? state.State == TcpState.Established : false;
}
You're reinventing the wheel (in a worse way) at quite a few levels:
You're doing pseudo-blocking sockets. That combined with creating a whole new thread for every connection in an OS like Linux which doesn't have real threads can get expensive fast. Instead you should create a pure blocking socket with no read timeout (-1) and just listen on it. Unlike UDP, TCP will catch the connection being terminated by the client without you needing to poll for it.
And the reason why you seem to be doing the above is that you reinvent the standard Keep-Alive TCP mechanism. It's already written and works efficiently, simply use it. And as a bonus, the standard Keep-Alive mechanism is on the client side, not the server side, so even less processing for you.
Edit: And 3. You really need to cache the threads you so painstakingly created. The system thread pool won't suffice if you have that many long-term connections with a single socket communication per thread, but you can build your own expandable thread pool. You can also share multiple sockets on one thread using select, but that's going to change your logic quite a bit.
I'm currently using the ReadAsync method in NetworkStream, and thus far its been pretty stable. The problem occurs when the data received from the server is larger than the buffer itself.
ReadCallBack assumes that the buffer contains a complete and valid message, as a new buffer is created on every call to BeginAsyncRead. Thus, when the response is too large for one buffer, it gets split between multiple reads and thus multiple buffers.
My ReadCallBack then assumes each buffer is an independent message (instead of adding them together) and ends up failing the deserialization on both.
My question is, what is the correct way to handle messages that are bigger than the size of the buffer, specially if you want to do the reads asynchronously?
A character itself could be split between two buffers so this gets quite tricky.
Below is my code for ReadAsync and ReadCallBack.
public async Task ReadForever() {
while(this.IsConnected) {
await BeginAsyncRead();
}
}
public async Task BeginAsyncRead() {
if (this.Stream.CanRead) {
try {
ReceiverState readState = new ReceiverState(this.Stream);
var task = this.Stream.ReadAsync(readState.buffer, 0, ReceiverState.bufferSize);
await task.ContinueWith(bytesRead => ReadCallBack(bytesRead.Result, readState));
}
catch (Exception e) {
Console.WriteLine("Error");
}
} else {
Console.WriteLine("Unreadable stream");
}
}
private void ReadCallBack(int numBytesRead, ReceiverState state) {
if (numBytesRead > 0) {
string payload = Encoding.ASCII.GetString(state.buffer, 0, numBytesRead);
string[] responses = payload.Split(new string[] {"\n"}, StringSplitOptions.RemoveEmptyEntries);
foreach (string res in responses){
var t = Task.Run(() => this.OnRead(res));
}
} else {
Console.WriteLine("Corrupted number of bytes received");
}
}
Note : All responses sent by the server contain a newline at the end.
Note : ReadForever is called inside Task.Run, my application can receive messages from the server as notifications, thus I must always be reading for incoming messages (I don't know when a notification may arrive).
I followed this link http://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx to write this program.
public string ReceiveResponse(Socket client, int bufferSize)
{
// Receive the response from the remote device.
Receive(client, bufferSize);
// Wait until we receive entire response.
receiveDone.WaitOne();
return response;
}
And my receive callback is
public static void ReceiveCallback(IAsyncResult ar)
{
StateObject so = (StateObject)ar.AsyncState;
Socket s = so.WorkSocket;
int read = s.EndReceive(ar);
if (read > 0)
{
so.Sb.Append(Encoding.ASCII.GetString(so.Buffer, 0, read));
s.BeginReceive(so.Buffer, 0, 1024, 0,
new AsyncCallback(ReceiveCallback), so);
}
else
{
if (so.Sb.Length > 1) // Code never reaching this block??
{
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.Sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
receiveDone.Set();
}
}
As commented in my code else block never reaching so my receiveDone manual reset event blocking my unit test forever.
When Microsoft says this is the way to go then why it is not working for me???
I really need it for real time and multi threaded environment. I wasted a lot time on this but couldn't find a proper reason why this is happening? and how I can fix this?
I have been trying to create a metro application but there is a problem: StreamSocket doesn't really do what I want to do (I think)
Here is an excerpt my code from .Net that works:
try
{
TCP = new TcpClient(server, port);
Stream = TCP.GetStream();
Read = new StreamReader(Stream);
Write = new StreamWriter(Stream);
}
catch (Exception e)
{
Console.WriteLine("Error connecting to " + server + ": " + e);
return;
}
// Identify
Write.WriteLine("LOGIN " + Username);
Write.Flush();
while (Connected)
{
try
{
if ((line = Read.ReadLine()) != null && Connected)
I can't get StreamSocket to work... it requires you to know the length of the string that's coming in and I don't know what it will be - it varies. Is there any way to do this that will work?
This is what I have but it doesn't work:
try
{
// Connect to the server (in our case the listener we created in previous step).
await Socket.ConnectAsync(new HostName("example.com"), "1111");
}
catch (Exception exception)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
}
// Create a DataWriter if we did not create one yet. Otherwise use one that is already cached.
Writer = new DataWriter(Socket.OutputStream);
Listener = new DataReader(Socket.InputStream);
Debug.WriteLine(Socket.Information.RemoteAddress.CanonicalName); //Check if IP is correct
SendRaw("LOGIN " + Nickname);
string line = "";
Connected = true;
while (Connected)
{
if (Listener.UnconsumedBufferLength != 0)
{
line = Listener.ReadString(Listener.UnconsumedBufferLength);
Debug.WriteLine(line);
}
}
}
async public void SendRaw(string str)
{
Writer.WriteString(str);
// Write the locally buffered data to the network.
try
{
await Writer.StoreAsync();
}
catch (Exception exception)
{
// If this is an unknown status it means that the error if fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
}
}
Any help would be appreciated!
First things first: your original code is a DOS attack waiting to happen. If possible, I would recommend changing the protocol to include a length prefix before every string so you can tell how big it will be before allocating memory for it.
Second things second: the DataReader class must read a number of bytes into its internal buffer before it can interpret them. You read into this buffer by calling LoadAsync.
However, if you want to read a string of arbitrary length, you'll have to read into a buffer and scan for your newline yourself, resizing the buffer (or adding new buffers) as necessary if the newline isn't found, up to some maximum size.
Update:
Set InputStreamOptions to Partial; the you can call LoadAsync with an arbitrary large buffer size (e.g. 1024). After getting data, call ReadString(UnconsumedBufferLength). Each time you do this, you may get part of a line, a line, or more than a line. So you'll have to build up a string and then Split by \n, keeping any partial line at the end for the next time through the loop.