I am reading a .bin file and writing it into a Stream. Later, I am reading that stream object and then writing it into a Network Stream. Code is as following:
public async Task<bool> UploadFirmware(Stream _stream)
{
bool success = false;
try
{
_tcpclient = new TcpClient();
_tcpclient.Connect(_deviceip, port);
_stream.Seek(0, SeekOrigin.Begin);
m_NetworkStream = _tcpclient.GetStream();
byte[] buffer = new byte[1024];
m_ReadBuffer = new byte[1024];
int readcount = 0;
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new AsyncCallback(EndReceive), null);
await Task.Run(() =>
{
while ((readcount = _stream.Read(buffer, 0, buffer.Length)) > 0)
{
m_NetworkStream.Write(buffer, 0, readcount);
m_NetworkStream.Flush();
}
});
success = true;
}
catch (Exception ex)
{}
return success;
}
Normally, this code works fine, but sometimes on an IP Address, the code gets stuck at m_NetworkStream.Write(buffer, 0, readcount);. The thing is, I am updating the status in UI based on success value, but the code gets hanged at above mentioned line and doesn't come out at all. No exception is thrown at all to identify the issue. So, in UI the status is not updated, and unexpected result is produced. I am not able to identify the issue. Help of any kind will be highly appreciated.
EDIT:
Also, I have to do an operation in parallel. The code for EndReceive is as follows:
private void EndReceive(IAsyncResult ar)
{
try
{
int nBytes;
nBytes = m_NetworkStream.EndRead(ar);
if (nBytes > 0)
{
string res = Encoding.UTF8.GetString(m_ReadBuffer, 0, nBytes);
DeviceStatus status = new DeviceStatus();
string[] readlines = res.Split(new string[] { CRLF }, StringSplitOptions.RemoveEmptyEntries);
foreach (string readline in readlines)
{
if (readline.StartsWith("CODE"))
{
status.code = Convert.ToInt32(readline.Replace("CODE=", ""));
break;
}
}
status.deviceip = this._deviceip;
status.port = this.port;
status.DeviceID = this._DeviceID;
status.FirmwareID = this._FirmwareID;
status.FilePath = this._Path;
StatusUpdate(status);
m_ReadBuffer = new byte[1024];
}
}
catch (ObjectDisposedException ods)
{
return;
}
if (_tcpclient.Connected)
m_NetworkStream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new AsyncCallback(EndReceive), null);
}
I'm not sure you should have a while loop in order to write bytes read from a file (from disk) to a network stream ... you can just read all the bytes and write to the stream and flush in one move.
You can also add a write timeout to specify how much time can pass before the stream write operation fails, to prevent any possibility of 'hanging'.
With these modifications, the code would look something like this:
// make the tcp connection to the remote endpoint
_tcpclient = new TcpClient();
_tcpclient.Connect(_deviceip, port);
// read the file bytes in one operation
var allBytes = File.ReadAllBytes(fileNameOnDisk);
// get the network stream
m_NetworkStream = _tcpclient.GetStream();
// wait a max of 500ms for the write operation to happen
m_NetworkStream.WriteTimeout = 500;
// write the file bytes to the stream and flush without while/stream/seek etc.
m_NetworkStream.Write(allBytes, 0, allBytes.Length);
m_NetworkStream.Flush();
And when you've finished with the stream:
m_NetworkStream.Close();
m_NetworkStream.Dispose();
It seem odd that you starting to read from network stream (m_NetworkStream.BeginRead(...)) and right away in another thread starting to write into same stream (m_NetworkStream.Write(...)). I would suggest to finish reading first and then start writing. Also you could use Stream.CopyTo to copy data between streams.
public async Task<bool> UploadFirmware(Stream fileStream, IPEndPoint deviceEP)
{
bool success = false;
try
{
TcpClient client = new TcpClient();
client.Connect(deviceEP);
NetworkStream networkStream = client.GetStream();
BeginReadFromDevice(networkStream);
// send bin data to device
await fileStream.CopyToAsync(networkStream);
success = true;
}
catch (Exception)
{
}
return success;
}
private void BeginReadFromDevice(Stream networkStream)
{
byte[] buffer = new byte[1024];
networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(EndReceive), null);
}
Related
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);
}
}
}
}
I'm running the following piece of code which uses a delegate to return an asynchronous Network Stream:
static void Main(string[] args)
{
NetworkStream myNetworkStream;
Socket socket;
IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), xxxx);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
socket.Connect(maxPort);
myNetworkStream = new NetworkStream(socket);
byte[] buffer = new byte[1024];
int offset = 0;
int count = 1024;
string Command = "LOGIN,,,xxxx\n";
ASCIIEncoding encoder = new ASCIIEncoding();
myNetworkStream.BeginRead(buffer, offset, count, new AsyncCallback(OnBeginRead), myNetworkStream);
myNetworkStream.Write(encoder.GetBytes(Command), 0, encoder.GetByteCount(Command));
while (true) { }
}
public static void OnBeginRead(IAsyncResult ar)
{
NetworkStream ns = (NetworkStream)ar.AsyncState;
int bufferSize = 1024;
byte[] received = new byte[bufferSize];
ns.EndRead(ar);
int read;
while (true)
{
if (ns.DataAvailable)
{
string result = String.Empty;
read = ns.Read(received, 0, bufferSize);
result += Encoding.ASCII.GetString(received);
received = new byte[bufferSize];
result = result.Replace(" ", "");
result = result.Replace("\0", "");
result = result.Replace("\r\n", ",");
Console.WriteLine(result);
}
}
}
It works, but my CPU usage is through the roof (50% on an Intel Core i3), so obviously I'm doing it wrong, but how so?
Thanks
You're only reading the very first bytes asynchronously, afterwards you end up in an infinite loop with sync read operations in your OnBeginRead method (which is a confusing name BTW). At the same time, those first bytes are discarded in your current code.
You need to process the data after EndRead (which is a function returning how many bytes were read into the buffer in this async operation), and then start another async read with BeginRead and return (there is no looping in the async code!).
Edited to add a sample showing how async reading would work:
internal class StreamHelper {
private readonly NetworkStream stream;
private readonly byte[] buffer = new byte[1024];
public StreamHelper(Socket socket) {
stream = new NetworkStream(socket);
}
public NetworkStream Stream {
get {
return stream;
}
}
public byte[] Buffer {
get {
return buffer;
}
}
}
private static void Main(string[] args) {
IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), 100);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
socket.Connect(maxPort);
StreamHelper helper = new StreamHelper(socket);
helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);
string Command = "LOGIN,,,xxxx\n";
byte[] bytes = Encoding.ASCII.GetBytes(Command);
// note: the write isn't async, but should maybe be converted as well
helper.Stream.Write(bytes, 0, bytes.Length);
Console.ReadLine(); // wait for a return key press
}
private static void StreamReadCallback(IAsyncResult ar) {
StreamHelper helper = (StreamHelper)ar.AsyncState;
// note: EndRead will throw an exception if something went wrong - you should deal with that
int bytesRead = helper.Stream.EndRead(ar);
if (bytesRead > 0) {
string charsRead = Encoding.ASCII.GetString(helper.Buffer, 0, bytesRead);
Console.Write(charsRead);
helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);
}
}
You are looping continuously on the main thread:
while (true) { }
This causes the CPU core of that thread to be at full capacity at all times. Try to sleep in order to prevent the thread from taking up CPU time unnecessarily:
while (true) { Thread.Sleep(5000); }
Perhaps replace the inefficiency of spinning the processor at the bottom of your main method from
while (true) { }
to
Console.ReadLine();
Incidentally Lucero is spot on. You're moving into an infinte loop (in OnBeginRead) with the thread that calls the callback method. This feels wrong. Callbacks should be dealt with asap to let the calling thread carry on processing. Normally you would extract the data in the callback and post a signal to your own thread to process the rest. Perhaps a TPL thread will help here.
This is a continuation of the this question. I am new to network programming, so I am just writing small sample stuff to gain understanding, but somewhat struggling with explaining results.
It seems setting NetworkStream.ReceiveTimeout is not working correctly when client that was supposed to be sending data simply closes before sending all the expected data.
Here is the sample code:
public static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 10001);
listener.Start();
ThreadPool.QueueUserWorkItem(WriterThread);
using (TcpClient client = listener.AcceptTcpClient())
using (NetworkStream stream = client.GetStream())
{
client.ReceiveTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
stream.ReadTimeout = (int)new TimeSpan(0, 0, 2).TotalMilliseconds;
ReceiveMessage(stream, 1024);
}
listener.Stop();
Console.WriteLine("Done.");
Console.ReadKey(true);
}
private static void WriterThread(object state)
{
using (TcpClient client = new TcpClient())
{
client.Connect(new IPEndPoint(IPAddress.Loopback, 10001));
using (NetworkStream stream = client.GetStream())
{
byte[] bytes = Encoding.ASCII.GetBytes("obviously less than 1024 bytes");
stream.Write(bytes, 0, bytes.Length);
Thread.Sleep(10000); // comment out
}
}
}
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
bufferFill += stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
This version works correctly triggering exception on the stream.Read() call. However If I comment out Thread.Sleep(10000), the client closes connection, but listener fails to recognize it. Main thread gets stuck inside the while(true) loop. The stream.Read() keeps returning zero, but no exception thrown.
Is this normal? If so how am I expected to handle abnormal client disconnections?
Yes, this sounds normal. There is no receive- or read timeout because the client has disconnected. This means that no more data is available for reading and the stream will return 0 immediately just as documented.
I would modify your ReceiveMessage method to something like the following:
private static byte[] ReceiveMessage(Stream stream, int length)
{
byte[] buffer = new byte[length];
int bufferFill = 0;
while (true)
{
int bytesRead = stream.Read(buffer, bufferFill, buffer.Length - bufferFill);
if (bytesRead == 0)
throw new Exception("No more data available.");
bufferFill += bytesRead;
if (buffer.Length == bufferFill)
return buffer;
Thread.Sleep(100);
}
}
Clearly if the stream.Read() call returns 0 before we have received all the expected bytes there must have been some form of disconnection or similar. Either way we will never get any more data from the stream.
Edit: The Stream class has no notion of a "message". The Read method blocks until more data becomes available if none is already in the buffer. It will however return 0 when no more data can be received, which in this case means the connection is closed.
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();
}
}
I try to use stream.DataAvailable to judge if it is finished,but sometimes the value is false but after a little while it is true again,i have to set a counter and judge the end by the symbol '>' like this
int connectCounter = 0;
while (connectCounter < 1200)
{
if (stream.DataAvailable)
{
while (stream.DataAvailable)
{
byte[] buffer = new byte[bufferSize];
int flag = stream.Read(buffer, 0, buffer.Length);
string strReadXML_t = System.Text.Encoding.Default.GetString(buffer);
strReadXML = strReadXML + strReadXML_t.Replace("\0", string.Empty);
}
if (strReadXML.Substring(strReadXML.Length - 1, 1).Equals(">"))
{
break;
}
}
Thread.Sleep(100);
connectCounter++;
}
is there any good methord to deal with it?Thank you!
You have a couple options. You can use a synchronous, blocking Read, or you can use an asynchronous IO pattern.
If you simply call stream.Read(), the call will block, and wait forever (until the TCP timeout), until data is available. It seems you don't want to do that. You want to wait, at most, 120 seconds (1200ms * 100), for the data to be completely read.
Something like this:
private class AsyncState
{
public NetworkStream ns;
public ManualResetEvent e;
public byte[] b;
public String strReadXML;
}
public void Run()
{
TcpClient client ;//= ...
NetworkStream networkStream = client.GetStream();
byte[] buffer = new byte[1024];
var completedEvent = new ManualResetEvent(false);
networkStream.BeginRead(buffer, 0, buffer.Length,
AsyncRead,
new AsyncState
{
b = buffer,
ns = networkStream,
e = completedEvent,
strReadXML = ""
});
// do other stuff here. ...
// finally, wait 120s for the reading to complete
bool success = completedEvent.WaitOne(1200*100, false);
if (!success)
{
client.Close();
}
}
private void AsyncRead(IAsyncResult ar)
{
AsyncState state = ar as AsyncState;
int n = state.ns.EndRead(ar);
if (n == 0)
{
// no more bytes to read
// signal completion
state.e.Set();
return;
}
// state.buffer now contains the bytes read
string s = System.Text.Encoding.Default.GetString(state.b);
state.strReadXML = state.strReadXML + s.Replace("\0", string.Empty);
if (state.strReadXML.EndsWith(">"))
{
// got the "end". signal completion
state.e.Set();
return;
}
// read again
state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
}
Try asynchronous reading.
When your callback is called, you can read the existing data buffer and then call
BeginRead again. So that when somre more data is received, you callback will be called again.
Something like:
void DataReceived(IAsyncResult result) ///I am not sure about the parameters.
{
///read data from buffer
networkstream.BeginRead(
buffer, 0, buffer.Length,
new AsyncCallback(DataReceived),
null);
}
I think this is a pretty decent approach.
Do you have control over the sending application? If you do you can change the sending application to wrap the NetworkStream in a BinaryWriter and use .Write( string ) which will send the length of the string, and then the string itself. On the receiving application (as above) you can wrap the NetworkStream in a BinaryReader and call .ReadString() which will read the length from the stream, then read the correct length string in for you.
If you don't have control over the sending application then you can check the return from the stream.Read() call as it returns 0 at the end of the stream - though this will require the sending application to have closed the socket.
Also, stream.Read() is not guaranteed to return the number of bytes you requested, it could return less. Your flag variable should really be bytesread, and you should then pass this bytesread variable to the .GetString method.