How to handle Error in Async Network programming? - c#

I have the following method :
private void ClientReadCallback(IAsyncResult asyncResult)
{
try
{
var networkStream = _client.GetStream();
var read = networkStream.EndRead(asyncResult);
if (read == 0)
{
Disconnected?.Invoke(this, new EventArgs());
}
byte[] buffer = asyncResult.AsyncState as byte[];
if (buffer != null)
{
byte[] data = new byte[read];
Buffer.BlockCopy(buffer, 0, data, 0, read);
networkStream.BeginRead(buffer, 0, buffer.Length, ClientReadCallback, buffer);
DataRead?.Invoke(this, new DataReadEventArgs(data));
}
}
catch (Exception ex)
{
GetLog().Error(ex);
ClientReadException?.Invoke(this, new ExceptionEventArgs(ex));
}
}
That triggers a : System.ObjectDisposedException: Cannot access a disposed object at some points.
My question is : in which situation does this happens? network issue? How to handle this properly ?
thanks

Related

C#: How to Close a sslStream from other thread

I am trying to let sslStream read asynchronously for 5 seconds. If the stream received nothing after 5 seconds, the stream will be closed.
The problem of current implementation is that closing the stream using this trick will emit an error on console saying: Cannot access a disposed object.
Is there any other way to achieve my goal without this error?
Here's my code:
private static async Task<string> ReadMessageAsync(SslStream pSslStream)
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
try
{
do
{
bytes = await pSslStream.ReadAsync(buffer, 0, buffer.Length)
.TimeoutAfter(TimeSpan.FromSeconds(5));
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
}
catch (TimeoutException)
{
Console.WriteLine("Timeout");
pSslStream.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return messageData.ToString();
}
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
{
if (task != await Task.WhenAny(task, Task.Delay(timeout)))
{
throw new TimeoutException("Timeout");
}
return task.Result;
}
P/s: The extension method was from How do you catch CancellationToken.Register callback exceptions?

.net c# Socket.SendFile method, how to use pre- and postBuffer param and receive this information

I currently have an application that uses the .NET C# .SendFile() method.
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(endPoint);
client.SendFile(filePath);
And I have a very simple receive file solution that goes something like this. Takes the the bits read and append it to a file basically:
ReadCallback(IAsyncResult ar) {
StateObject tempState = (StateObject)ar.AsyncState;
Socket handler = tempState.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead <= 0)
{
return;
}
BinaryWriter writer = null;
try
{
writer = new BinaryWriter(File.Open(receivePath, FileMode.Append));
writer.Write(tempState.buffer, 0, bytesRead);
}
catch (Exception error)
{
Console.WriteLine(error.Message);
Thread.Sleep(30);
}
finally
{
if (writer != null)
{
writer.Close();
}
// this method starts a new AsyncCallback(ReadCallback)
// and this method is ReadCallback so it works as a recursive method
handler.BeginReceive(tempState.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), tempState);
}
Now I want to add additonal info like: filename, file size, md5 hash etc to the transfer.
So I wonder how if I send this for example:
byte[] preBuffer = { 1, 2, 3, 4 };
byte[] postBuffer ={ 5, 6, 7, 8 };
.SendFile(filePath, preBuffer, postBuffer, TransmitFileOptions.None)
How can I receive this information and separate it from the binary file that I write? How should I change my receive
Unless you want to append data to the file received on the other end, don't use postBuffer. i.e. if you want to send meta information about the file, only use preBuffer. In this case I recommend serializing the data to an in memory buffer then sending that buffer as the preBuffer and deserializing it on the other end. For example:
byte[] preBuffer;
using (var memoryStream = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(memoryStream))
{
writer.Write(filePath);
writer.Write(fileLength);
writer.Write(md5Hash);
}
preBuffer = memoryStream.ToArray();
}
mySocket.SendFile(filePath, preBuffer, null,
TransmitFileOptions.UseDefaultWorkerThread);
Then, when you want to read the meta information and file, read the pre-data first, and the remaining will be the file. For example:
private void ReadCallback(IAsyncResult ar)
{
StateObject tempState = (StateObject) ar.AsyncState;
Socket handler = tempState.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead <= 0)
{
return;
}
using (var memoryStream = new MemoryStream(tempState.buffer))
{
using (BinaryReader reader = new BinaryReader(memoryStream))
{
var filename = reader.ReadString();
var length = reader.ReadInt32();
var md5Hash = reader.ReadString();
var fileData = new byte[memoryStream.Length - memoryStream.Position];
reader.Read(fileData, 0, fileData.Length);
try
{
using (var writer = new BinaryWriter(
File.Open(receivePath, FileMode.Append)))
{
writer.Write(tempState.buffer, 0, bytesRead);
}
}
catch (Exception error)
{
Console.WriteLine(error.Message);
Thread.Sleep(30);
}
finally
{
// this method starts a new AsyncCallback(ReadCallback)
// and this method is ReadCallback so it works as a recursive method
handler.BeginReceive(tempState.buffer,
0,
StateObject.BufferSize,
0,
new AsyncCallback(ReadCallback),
tempState);
}
}
}
}

C# FTP Upload Server returned an error: 550 The process cannot access the file because it is being used by another process

Hi I'm trying to upload a file with a passed in byte[] and am getting a 550.
I'm using a Memory stream in a using statement so I thought it should close itself off nicely.
If I'm dealing with a byte array rather than a physical file what could be causing the lock as the byte array doesn't map to a file per-say it is generated by a csv helper.
Basically I build up a csv with a stringbuilder and the call Export which takes the string builder and puts it in to a byte[] which then gets passed in to this method..
Just wondering if anyone could see what I'm doing wrong.
Thanks!
bool PutFileToServer(string remoteFile, byte[] bytesToUpload, bool result)
{
try
{
LogStatus("Creating FTP Request");
var ftpRequest = CreateRequest(remoteFile, WebRequestMethods.Ftp.UploadFile);
using (var dataStream = new MemoryStream(bytesToUpload))
using (var requestStream = ftpRequest.GetRequestStream())
{
dataStream.Position = 0;
byte[] byteBuffer = new byte[bufferSize];
int bytesSent = dataStream.Read(byteBuffer, 0, bufferSize);
LogStatus("Reading File Stream");
try
{
while (bytesSent != 0)
{
requestStream.Write(byteBuffer, 0, bytesSent);
bytesSent = dataStream.Read(byteBuffer, 0, bufferSize);
}
}
catch (Exception ex)
{
LogStatus("Error Reading File Bytes");
LogStatus(ex.Message);
result = false;
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
if (dataStream != null)
{
dataStream.Close();
}
if (requestStream != null)
{
requestStream.Close();
}
if (ftpStream != null)
{
ftpStream.Close();
}
ftpRequest = null;
}
result = true;
}
}
catch (Exception ex)
{
LogStatus(ex.Message);
result = false;
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
if (ftpStream != null)
{
ftpStream.Close();
}
ftpRequest = null;
}
return result;
}

C# TcpClient losing packets

I'm having some issues when I really stress test my networking code. Essentially once the socket is set up it calls this:
NetworkStream networkStream = mClient.GetStream();
networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
private void ReadCallback(IAsyncResult result)
{
try
{
int read;
NetworkStream networkStream;
try
{
networkStream = mClient.GetStream();
read = networkStream.EndRead(result);
}
catch
{
return;
}
if (read == 0)
{
//The connection has been closed.
return;
}
var readBuffer = (byte[])result.AsyncState;
var readCount = readBuffer.Length;
while (readCount < 4)
{
readCount += networkStream.Read(readBuffer, 0, readBuffer.Length - readCount);
}
var length = BitConverter.ToInt32(readBuffer, 0);
var messageBuffer = new byte[length];
readCount = 0;
while (readCount < length)
{
readCount += networkStream.Read(messageBuffer, 0, messageBuffer.Length - readCount);
}
else
{
RaiseMessageReceived(this, messageBuffer);
}
//Then start reading from the network again.
readBuffer = new byte[4]; //may not need to reset, not sure
networkStream.BeginRead(readBuffer, 0, readBuffer.Length, ReadCallback, readBuffer);
}
catch(Exception)
{
//Connection is dead, stop trying to read and wait for a heal to retrigger the read queue
return;
}
}
Then the below is my send methods
private byte[] GetMessageWithLength(byte[] bytes)
{
//Combine the msg length to the msg
byte[] length = BitConverter.GetBytes(bytes.Length);
var msg = new byte[length.Length + bytes.Length];
Buffer.BlockCopy(length, 0, msg, 0, length.Length);
Buffer.BlockCopy(bytes, 0, msg, length.Length, bytes.Length);
return msg;
}
public override bool Send(byte[] bytes)
{
lock (sendQueue)
{
sendQueue.Enqueue(bytes);
Interlocked.Increment(ref sendQueueSize);
}
if (!mClient.Connected)
{
if (Connect())
{
RaiseConnectionChanged(this, true, Localisation.TCPConnectionEstablished);
}
else
{
RaiseConnectionChanged(this, false, (bytes.Length > 0 ? Localisation.TCPMessageFailed : Localisation.TCPMessageConnectionLost));
}
}
try
{
NetworkStream networkStream = mClient.GetStream();
lock (sendQueue)
{
if (sendQueue.Count == 0)
{
return true;
}
bytes = sendQueue.Dequeue();
}
var msg = GetMessageWithLength(bytes);
//Start async write operation
networkStream.BeginWrite(msg, 0, msg.Length, WriteCallback, null);
}
catch (Exception ex)
{
RaiseConnectionChanged(this, false, (bytes.Length > 0 ? Localisation.TCPMessageFailed : Localisation.TCPMessageConnectionLost));
}
return true;
}
/// <summary>
/// Callback for Write operation
/// </summary>
/// <param name="result">The AsyncResult object</param>
private void WriteCallback(IAsyncResult result)
{
try
{
NetworkStream networkStream = mClient.GetStream();
while (sendQueue.Count > 0)
{
byte[] bytes;
lock (sendQueue)
{
if (sendQueue.Count == 0)
{
break;
}
bytes = sendQueue.Dequeue();
}
var msg = GetMessageWithLength(bytes);
networkStream.Write(msg, 0, msg.Length);
Interlocked.Decrement(ref sendQueueSize);
}
networkStream.EndWrite(result);
mLastPacketSentAt = Environment.TickCount;
Interlocked.Decrement(ref sendQueueSize);
}
catch (Exception ex)
{
RaiseConnectionChanged(this, false, Localisation.TCPMessageConnectionLost);
}
}
But yea, at some point when I stress test the system (say 500 or so clients sending lots of messages at once), I notice maybe 1 packet in every 4 million to just not get recieved. I'm not sure if the issue lies in the sending or the recieving, which is why I have included both methods. However I will point out that if I choose to send another packet from the client, it still sends and receives correctly, so it is not just queued or something.
Can anyone see something I am missing?
The two read loops (e.g. while (readCount < length)) are buggy. You always read at offset zero. You should read at an ever-increasing offset.
This lead to overwriting of already-read data.
Also, I'm not sure if it is a good idea to mix synchronous and asynchronous reads. You lose the benefit of asynchronous code that way and still have to deal with callbacks and such. I think you should decide on one style and stick to it.

NetworkStream.Read doesn't work the first time called

I have a simple tcp/ip chat program with a server and client. The first time I send a packet, it makes it to the client but during the NetworkStream.Read it stops execution and doesn't throw an exception. The next packet I send is read and processed perfectly. Another weird thing I noticed is that MyNetworkStream.DataAvailable is always false even if I get information from the server so I have to put a debug symbol and skip over it. I wish I could post all my code but it is long so I will post where I read and write to the network stream.
public void Listen(int byteLength)
{
var buffer = new byte[byteLength];
MySocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Read), buffer);
}
private void Read(IAsyncResult ar)
{
while (MySocket.Connected)
{
MyNetworkStream = new NetworkStream(MySocket);
var buffer = new byte[((byte[])ar.AsyncState).Length];
if (!MyNetworkStream.DataAvailable)
throw new Exception("Data not available");
MyNetworkStream.Read(buffer, 0, buffer.Length); <------Here it stops execution without throwing an exception
string content = Encoding.ASCII.GetString(buffer);
if(OnRead == null)
continue;
var e = new CommandEventArgs( null, content);
Control target = null;
if (OnRead.Target is Control)
target = (Control)OnRead.Target;
if (target != null && target.InvokeRequired)
target.Invoke(OnRead, this, e);
else
OnRead(this,e);
}
}
public void Write(string message)
{
try
{
var buffer = Encoding.ASCII.GetBytes(message);
MySocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, null);
if (OnWrite != null)
{
var target = (Control)OnWrite.Target;
if (target != null && target.InvokeRequired)
{
target.Invoke(OnWrite, this, new EventArgs());
}
else
{
OnWrite(this, new EventArgs());
}
}
}
catch
{
}
}
BeginReceive asynchronously waits for a message and fills your buffer. You then start synchronously reading from the socket, overwriting the first message in the process.
You should call EndReceive which returns the number of bytes read, then process your buffer before trying to read more bytes.
I'm not sure if it's directly related to the problem, but you are using the Read method wrong. You are reading data into the buffer, but you are ignoring how much data was actually read assuming that the Read call always returns as much data as you request, so you are decoding the entire buffer eventhough it might not be completely filled.
Get the return value of the Read call so that you know how much of the buffer is actually filled with data:
int len = MyNetworkStream.Read(buffer, 0, buffer.Length);
string content = Encoding.ASCII.GetString(buffer, 0, len);
You need to implement EndRecieve to get the complete data from the stream. Checkout the following example from MSDN :
public static void Read_Callback(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, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//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));
}
s.Close();
}
}

Categories

Resources