problem with two .NET threads and hardware access - c#

I'm creating an application which communicates with the device via FT2232H USB/RS232 converter. For communication I'm using FTD2XX_NET.dll library from FTDI website.
I'm using two threads:
first thread continuously reads data from the device
the second thread is the main thread of the Windows Form Application
I've got a problem when I'm trying to write any data to the device while the receiver's thread is running. The main thread simply hangs up on ftdiDevice.Write function.
I tried to synchronize both threads so that only one thread can use Read/Write function at the same time, but it didn't help.
Below code responsible for the communication. Note that following functions are methods of FtdiPort class.
Receiver's thread
private void receiverLoop()
{
if (this.DataReceivedHandler == null)
{
throw new BackendException("dataReceived delegate is not set");
}
FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
byte[] readBytes = new byte[this.ReadBufferSize];
while (true)
{
lock (FtdiPort.threadLocker)
{
UInt32 numBytesRead = 0;
ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
this.DataReceivedHandler(readBytes, numBytesRead);
}
else
{
Trace.WriteLine(String.Format("Couldn't read data from ftdi: status {0}", ftStatus));
Thread.Sleep(10);
}
}
Thread.Sleep(this.RXThreadDelay);
}
}
Write function called from main thread
public void Write(byte[] data, int length)
{
if (this.IsOpened)
{
uint i = 0;
lock (FtdiPort.threadLocker)
{
this.ftdiDevice.Write(data, length, ref i);
}
Thread.Sleep(1);
if (i != (int)length)
{
throw new BackendException("Couldnt send all data");
}
}
else
{
throw new BackendException("Port is closed");
}
}
Object used to synchronize two threads
static Object threadLocker = new Object();
Method that starts the receiver's thread
private void startReceiver()
{
if (this.DataReceivedHandler == null)
{
return;
}
if (this.IsOpened == false)
{
throw new BackendException("Trying to start listening for raw data while disconnected");
}
this.receiverThread = new Thread(this.receiverLoop);
//this.receiverThread.Name = "protocolListener";
this.receiverThread.IsBackground = true;
this.receiverThread.Start();
}
The ftdiDevice.Write function doesn't hang up if I comment following line:
ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);

An alternative is to use the event notification mechanism from FTDI, this way you don't need a blocking thread to read out data:
public FTDISample()
{
private AutoResetEvent receivedDataEvent;
private BackgroundWorker dataReceivedHandler;
private FTDI ftdi;
public FTDISample(string serialNumber){
ftdi = new FTDI();
FTDI.FT_STATUS status = ftdi.OpenBySerialNumber(serialNumber);
receivedDataEvent = new AutoResetEvent(false);
status = mFTDI.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, receivedDataEvent);
dataReceivedHandler = new BackgroundWorker();
dataReceivedHandler.DoWork += ReadData;
if (!dataReceivedHandler.IsBusy)
{
dataReceivedHandler.RunWorkerAsync();
}
}
private void ReadData(object pSender, DoWorkEventArgs pEventArgs)
{
UInt32 nrOfBytesAvailable = 0;
while (true)
{
// wait until event is fired
this.receivedDataEvent.WaitOne();
// try to recieve data now
FTDI.FT_STATUS status = ftdi.GetRxBytesAvailable(ref nrOfBytesAvailable);
if (status != FTDI.FT_STATUS.FT_OK)
{
break;
}
if (nrOfBytesAvailable > 0)
{
byte[] readData = new byte[nrOfBytesAvailable];
UInt32 numBytesRead = 0;
status = mFTDI.Read(readData, nrOfBytesAvailable, ref numBytesRead);
// invoke your own event handler for data received...
//InvokeCharacterReceivedEvent(fParsedData);
}
}
}
public bool Write(string data)
{
UInt32 numBytesWritten = 0;
ASCIIEncoding enconding = new ASCIIEncoding();
byte[] bytes = enconding.GetBytes(data);
FTDI.FT_STATUS status = ftdi.Write(bytes, bytes.Length, ref numBytesWritten);
if (status != FTDI.FT_STATUS.FT_OK)
{
Debug.WriteLine("FTDI Write Status ERROR: " + status);
return false;
}
if (numBytesWritten < data.Length)
{
Debug.WriteLine("FTDI Write Length ERROR: " + status + " length " + data.Length +
" written " + numBytesWritten);
return false;
}
return true;
}

A few things:
Check to see if your Read call is blocking. If so, you might not be able to call Write while the Read is blocking waiting for a response. Your API documentation may have more details on this.
Some APIs do not support multiple threads very well, even when synchronizing access. If that's the case here, you can use a design where you delegate your Write commands to your comm thread. When I've used this pattern in the past, I typically queue up some sort of Command class containing the information I wish to write, and either use a threading Signal class to allow my calling 'command' methods to block or provide some sort of asynchronous notification.

I've found more detailed API documentation. Indeed the ftdiDevice.read function is blocking unless you set the readTimeout value other then 0. Setting this timeout value solved the problem.
Thanks for your quick response.
Regards

Checking out the API, it looks to me that the driver is capable of emulating a COM port. I see the GetComPort() method returning a "COMx" string. That makes it pretty likely that you can use the System.IO.Ports.SerialPort class. Which already does what your wrapper is trying to do, it supports a DataReceived event. Worth a shot.

Related

Possible data loss on TcpClient?

I'm writing some code for a production line. In particular I need to integrate some barcode reader into the line.
For many (wrong) reason I had to end up like you are about to see.
Also, I was new to that field at the time I wrote that.
The issue: Sometimes, the reader reads the code (and send the image via ftp) but I get no data in my software and I need to understand why.
This is how initialize my reader (I have multiple reader, each with different IP but same port)
server = new TcpListener(IPAddress.Any, scannerPort);
server.Start();
Thread thread = new Thread(() =>
{
log.Debug("Waiting for scanner connection");
server.BeginAcceptTcpClient(HandleAsyncConnection, server);
});
thread.Start();
and this is my main method:
try
{
object _lockObj = new object();
using (TcpClient client = server.EndAcceptTcpClient(res))
{
int i;
server.BeginAcceptTcpClient(HandleAsyncConnection, server);
string address = client.Client.RemoteEndPoint.ToString();
string senderIp = address.Split(':')[0];
NetworkStream stream = client.GetStream();
Byte[] bytes = new Byte[2048];
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
log.Debug("Start");
lock (_lockObj)
{
log.Debug("Lock");
StringBuilder sb = new StringBuilder(string.Empty);
string rawCode = string.Empty;
bool isCrFound = false;
foreach (byte c in bytes.Where(x => x != 0))
{
if (c != (char)13 && !isCrFound)
{
if (c == (char)29)
{
sb.Append("<GS>");
}
else
sb.Append(Convert.ToChar(c));
}
else
{
isCrFound = true;
}
}
rawCode = sb.ToString();
log.Debug("Raw code from {0} -> {1}", senderIp, rawCode);
if (rawCode.StartsWith("ERROR", StringComparison.InvariantCultureIgnoreCase))
{
log.Debug("Operator - Errore reading code {0} from {1}", rawCode, senderIp);
}
else
{
//Process code
}
log.Debug("End");
}
log.Debug("Unlock");
}
}
}
catch (Exception ex)
{
log.Error("Error on the async connect operation: {0}, Stack: {1}", ex.Message, ex.StackTrace);
}
So basically everytime a sensor trigger, data is sent to my socket.
This works pretty well until the line starts to speed up (like 3/4 code/second); that is when data loss happen.
Now, I don't know where the issue is. Could be the devide, my implementation, resources management (byte array was 1024, I still have to try the 2048)
Any help would be much appreciated.
Thanks
EDIT:
I already stripped some log for brevity
I need a lock because when a sensor triggers too close to another, messages got overlapped (to that add different business reason)
When I loose Codes, I don't have anything in my log; the log, usually looks like:
start
Lock
lot of stuff
end
unlock
Lost codes don't leave any trace on my log... so I don't think it's something related to data discarded.
What I'd like to know if there is a way to do this kind of thing better in a context like mine.

SocketAsyncEventArgs Send/Receive Order

I've been using SocketAsyncEventArgs for a project recently and I've come across issues where it appears that ReceiveAsync is occasionally getting data in a different order from what is being sent via SendAsync. Each block of data sent in the SendAsync method is maintained, but the blocks are not necessarily in the right order. Maybe I have an incorrect understanding of the SendAsync method, but I thought that especially using SocketType.Stream and ProtocolType.Tcp would ensure the order is maintained. I understand that the underlying process will inevitably break the message up and that ReceiveAsync will commonly read less than the buffer allocation. But I assumed that the send and receive streams would maintain order.
I carved out a test console program which shows the issue. It tries to run about 20 times using a different set of sockets and ports each time. On my laptop, it usually makes it through one time and then fails the second time; usually receiving a later block when it's expecting the second. From other testing, I know that expected block eventually does come, just out of sequence.
One caveat is that I was able to test it on a Windows 2008 remote server and had no issues. However, it has never come close to completing on my laptop. In fact, if I let the debug execution hang in the exception break for a while I've had it completely freeze my laptop more than once and had to do a hard reboot. This is my work laptop running on Windows 7, using VS2017. I'm not sure if it could be a factor, but it is running Symantec Endpoint Protection though I haven't found anything in the logs.
So my question is, do I have an incorrect view of how the SocketAsyncEventArgs operate? Or is my code a disaster (perhaps both)? Is it somehow unique to my laptop? (This last one makes me feel like I'm setting up for embarrassment like when you're new to programming and you think there must be something wrong with the compiler.)
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
static class DumTest
{
static void Main(string[] args)
{
for (int i = 9177; i < 9199; i++)
{
RunDum(i);
//Thread.Sleep(350);
}
Console.WriteLine("all done.");
Console.ReadLine();
}
static void RunDum(int port)
{
var dr = new DumReceiver(port);
var ds = new DumSender(port);
dr.Acception.Wait();
ds.Connection.Wait();
dr.Completion.Wait();
ds.Completion.Wait();
Console.WriteLine($"Completed {port}. " +
$"sent: {ds.SegmentsSent} segments, received: {dr.SegmentsRead} segments");
}
}
class DumReceiver
{
private readonly SocketAsyncEventArgs eva = new SocketAsyncEventArgs();
private readonly TaskCompletionSource<object> tcsAcc = new TaskCompletionSource<object>();
private TaskCompletionSource<object> tcsRcv;
private Socket socket;
internal DumReceiver(int port)
{
this.eva.Completed += this.Received;
var lstSock = new Socket(
AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList
.First(i => i.AddressFamily == AddressFamily.InterNetwork);
lstSock.Bind(new IPEndPoint(localIP, port));
lstSock.Listen(1);
var saea = new SocketAsyncEventArgs();
saea.Completed += this.AcceptCompleted;
lstSock.AcceptAsync(saea);
}
internal Task Acception => this.tcsAcc.Task;
internal Task Completion { get; private set; }
internal int SegmentsRead { get; private set; }
private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
this.socket = e.AcceptSocket;
e.Dispose();
try
{
this.Completion = this.ReceiveLupeAsync();
}
finally
{
this.tcsAcc.SetResult(null);
}
}
else
{
this.tcsAcc.SetException(new SocketException((int)e.SocketError));
}
}
private async Task ReceiveLupeAsync()
{
var buf = new byte[8196];
byte bufSeg = 1;
int pos = 0;
while (true)
{
this.tcsRcv = new TaskCompletionSource<object>();
this.eva.SetBuffer(buf, pos, 8196 - pos);
if (this.socket.ReceiveAsync(this.eva))
{
await this.tcsRcv.Task.ConfigureAwait(false);
}
if (this.eva.SocketError != SocketError.Success)
{
throw new SocketException((int)eva.SocketError);
}
if (this.eva.BytesTransferred == 0)
{
if (pos != 0)
{
throw new EndOfStreamException();
}
break;
}
pos += this.eva.BytesTransferred;
if (pos == 8196)
{
pos = 0;
for (int i = 0; i < 8196; i++)
{
if (buf[i] != bufSeg)
{
var msg = $"Expected {bufSeg} but read {buf[i]} ({i} of 8196). " +
$"Last read: {this.eva.BytesTransferred}.";
Console.WriteLine(msg);
throw new Exception(msg);
}
}
this.SegmentsRead++;
bufSeg = (byte)(this.SegmentsRead + 1);
}
}
}
private void Received(object s, SocketAsyncEventArgs e) => this.tcsRcv.SetResult(null);
}
class DumSender
{
private readonly SocketAsyncEventArgs eva = new SocketAsyncEventArgs();
private readonly Socket socket = new Socket(
AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private readonly TaskCompletionSource<object> tcsCon = new TaskCompletionSource<object>();
private TaskCompletionSource<object> tcsSnd;
internal DumSender(int port)
{
this.eva.Completed += this.Sent;
var saea = new SocketAsyncEventArgs();
var localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList
.First(i => i.AddressFamily == AddressFamily.InterNetwork);
saea.RemoteEndPoint = new IPEndPoint(localIP, port);
saea.Completed += this.ConnectionCompleted;
this.socket.ConnectAsync(saea);
}
internal Task Connection => this.tcsCon.Task;
internal Task Completion { get; private set; }
internal int SegmentsSent { get; private set; }
private void ConnectionCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
e.Dispose();
try
{
this.Completion = this.SendLupeAsync();
}
finally
{
this.tcsCon.SetResult(null);
}
}
else
{
this.tcsCon.SetException(new SocketException((int)e.SocketError));
}
}
private async Task SendLupeAsync()
{
var buf = new byte[8196];
byte bufSeg = 1;
while (true)
{
for (int i = 0; i < 8196; i++)
{
buf[i] = bufSeg;
}
this.tcsSnd = new TaskCompletionSource<object>();
this.eva.SetBuffer(buf, 0, 8196);
if (this.socket.SendAsync(this.eva))
{
await this.tcsSnd.Task.ConfigureAwait(false);
}
if (this.eva.SocketError != SocketError.Success)
{
throw new SocketException((int)this.eva.SocketError);
}
if (this.eva.BytesTransferred != 8196)
{
throw new SocketException();
}
if (++this.SegmentsSent == 299)
{
break;
}
bufSeg = (byte)(this.SegmentsSent + 1);
}
this.socket.Shutdown(SocketShutdown.Both);
}
private void Sent(object s, SocketAsyncEventArgs e) => this.tcsSnd.SetResult(null);
}
I believe the problem is in your code.
You must check the return of Socket's *Async methods that use SocketAsyncEventArgs. If they return false, them the SocketAsyncEventArgs.Completed event won't be raised, you must handle the result synchronously.
Reference documentation: SocketAsyncEventArgs Class. Search for willRaiseEvent.
In DumReceiver's constructor, you don't check AcceptAsync's result and you don't handle the case when it completes synchronously.
In DumSender's constructor, you don't check ConnectAsync's result and you don't handle the case when it completes synchronously.
On top of this, the SocketAsyncEventArgs.Completed event may be raised in some other thread, most probably an I/O thread from the ThreadPool.
Each time you assign to DumReceiver.tcsRcv and DumSender.tcsSnd without proper synchronization, you can't be sure that DumReceiver.Received and DumSender.Sent are using the latest TaskCompletionSource.
Actually, you could get a NullReferenceException on the first iteration.
You lack synchronization in:
DumReceiver, the fields tcsRcv and socket and the properties Completion and SegmentsRead
DumSender, the field tcsSnd and the properties Completion and SegmentsSent
I suggest you consider using a single SemaphoreSlim instead of creating a new TaskCompletionSource on each time you invoke ReceiveAsync and SendAsync. You'd initialize the semaphore to 0 in the constructor. If the *Async operation is pending, you'd await WaitAsync on the semaphore, and the Completed event would Release the semaphore.
This should be enough to get rid of the race conditions in the TaskCompletionSource fields. You'd still need proper synchronization on the other fields and properties. For instance, there's no reason why Completion can't be created in the constructors, and SegmentsRead and SegmentsSent could be read-only and refer to a field which would be accessed internally with one or more of the Interlocked methods (e.g. Interlocked.Increment or Interlocked.Add).

Using Threads in a C# function for Excel

I am writing a ping function for Excel. This function suppose to take a column of IP address (about 200 address) and ping each one of them.
To avoid Excel getting stuck, I have decided to use a thread that will send the pings instead of the main thread. The problem is that the thread can't access the cell values and the Excel crashes.
I hope that someone could help me with this.
Here is the code :
private LinkedList<string> Iplist;
private Thread t;
public NetworkPing()
{
}
public int CalcPingColumn(Range IPcells, Range ANScells)
{
CreateIpList(IPcells);
this.t = new Thread(() => CalcPing_method(ANScells));
this.t.Start();
return 1;
}
//THE PROBLEM IS PREOBBLY HERE
private void CalcPing_method(Range ANScells)
{
foreach (Range ans in ANScells.Cells)
{
ans.Value2 = Ping(Iplist.Last.Value);
Iplist.RemoveLast();
Thread.Sleep(50);
}
}
private void CreateIpList(Range IPcells)
{
Iplist = new LinkedList<string>();
foreach (Range ip in IPcells.Cells)
{
Iplist.AddFirst(ip.Value2);
}
}
Try to use the BackgroundWorker-class instead of the regular Thread. It makes it more easy to "send" objects/instances between threads. From that you can also get progress: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
why not one function?
public string PingIpadress(string ipadress)
{
var pingSender = new Ping();
var options = new PingOptions();
// Use the default Ttl value which is 128,
// but change the fragmentation behavior.
options.DontFragment = true;
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
PingReply reply = pingSender.Send(ipadress, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
return "alive " + reply.Options.Ttl;
}
return "no response";
}

serial port threading hang

I got the code below from a website,and this way of serial port reading is my only option because DataReceived event often doesn't work.but this code has a problem,if I close the application while transfering the application hangs forever,but I can't see why?neither freezing nor aborting the thread work.actually aborting the thread causes the program to crash.
public class CommPort
{
SerialPort _serialPort;
Thread _readThread;
bool _keepReading;
//begin Singleton pattern
static readonly CommPort instance = new CommPort();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static CommPort()
{
}
CommPort()
{
_serialPort = new SerialPort();
_readThread = null;
_keepReading = false;
}
public static CommPort Instance
{
get
{
return instance;
}
}
//end Singleton pattern
//begin Observer pattern
public delegate void EventHandler(string param);
public EventHandler StatusChanged;
public EventHandler DataReceived;
private void StartReading()
{
if (!_keepReading)
{
_keepReading = true;
_readThread = new Thread(new ThreadStart(ReadPort));
_readThread.Start();
}
}
private void StopReading()
{
if (_keepReading)
{
_keepReading = false;
_serialPort.Close();
//_readThread.Join(); //block until exits
_readThread.Abort();
//_readThread = null;
}
}
private void ReadPort()
{
while (_keepReading)
{
if (_serialPort.IsOpen)
{
byte[] readBuffer = new byte[_serialPort.ReadBufferSize + 1];
try
{
// If there are bytes available on the serial port,
// Read returns up to "count" bytes, but will not block (wait)
// for the remaining bytes. If there are no bytes available
// on the serial port, Read will block until at least one byte
// is available on the port, up until the ReadTimeout milliseconds
// have elapsed, at which time a TimeoutException will be thrown.
int count = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize);
String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);
DataReceived(SerialIn);
}
catch (TimeoutException)
{
}
}
else
{
TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50);
Thread.Sleep(waitTime);
}
}
}
/// <summary> Open the serial port with current settings. </summary>
public void Open()
{
Close();
try
{
_serialPort.PortName = Properties.Settings.Default.COMPort;
_serialPort.BaudRate = Properties.Settings.Default.BPS;
_serialPort.Parity = Properties.Settings.Default.Parity;
_serialPort.DataBits = Properties.Settings.Default.DataBit;
_serialPort.StopBits = Properties.Settings.Default.StopBit;
_serialPort.Handshake = Properties.Settings.Default.HandShake;
// Set the read/write timeouts
_serialPort.ReadTimeout = 50;
_serialPort.WriteTimeout = 50;
_serialPort.Open();
StartReading();
}
catch (IOException)
{
StatusChanged(String.Format("{0} does not exist", Properties.Settings.Default.COMPort));
}
catch (UnauthorizedAccessException)
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
catch (Exception ex)
{
StatusChanged(String.Format("{0}", ex.ToString()));
}
// Update the status
if (_serialPort.IsOpen)
{
string p = _serialPort.Parity.ToString().Substring(0, 1); //First char
string h = _serialPort.Handshake.ToString();
if (_serialPort.Handshake == Handshake.None)
h = "no handshake"; // more descriptive than "None"
StatusChanged(String.Format("{0}: {1} bps, {2}{3}{4}, {5}",
_serialPort.PortName, _serialPort.BaudRate,
_serialPort.DataBits, p, (int)_serialPort.StopBits, h));
}
else
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
}
/// <summary> Close the serial port. </summary>
public void Close()
{
StopReading();
StatusChanged("connection closed");
}
public bool IsOpen
{
get
{
return _serialPort.IsOpen;
}
}
}
When you close the Port in StopReading() it will cause an Exception in _serialPort.Read(...).
Not sure which one exactly but it's not a TimeOut. Your current code lets that escape and that's when your thread and your App are killed.
So add a catch(Exceptiopn ex) around the while loop in ReadPort().
Two issues at first glance.
First off, you have created a singleton class that exposes an event subscription. That's dangerous, because you should keep track of any of the subscribers. Otherwise they may be never released. That's probably what's going on in your case.
Secondly, when you manage something "IDisposable", you should take care about it. So, the SerialPort should be disposed inside a IDisposable implementation within your class. However, that won't make any sense in a singleton class.
The singleton pattern should be used only for centralized passive resources, and never to host "active" objects, events, etc.
Hope this helps.

.NET NetworkStream Read slowness

I've got some network code to process an arbitary TCP connection.
It all seems to work as expected but seems slow. When i've profiled the code the it seems to spend a good 600 ms in NetworkStream.Read() and I'm wondering how to improve it. I've fiddled with the buffer sizes and alternated between a massive buffer to read all of the data in one go or a small one which should concatenate the data into a StringBuilder. Currently the client i'm using is a web-browser but this code is generic and it may well not be HTTP data that is being sent to it. Any ideas?
My code is this:
public void StartListening()
{
try
{
lock (oSyncRoot)
{
oTCPListener = new TcpListener(oIPaddress, nPort);
// fire up the server
oTCPListener.Start();
// set listening bit
bIsListening = true;
}
// Enter the listening loop.
do
{
// Wait for connection
TcpClient newClient = oTCPListener.AcceptTcpClient();
// queue a request to take care of the client
oThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient);
}
while (bIsListening);
}
catch (SocketException se)
{
Logger.Write(new TCPLogEntry("SocketException: " + se.ToString()));
}
finally
{
// shut it down
StopListening();
}
}
private void ProcessConnection(object oClient)
{
TcpClient oTCPClient = (TcpClient)oClient;
try
{
byte[] abBuffer = new byte[1024];
StringBuilder sbReceivedData = new StringBuilder();
using (NetworkStream oNetworkStream = oTCPClient.GetStream())
{
// set initial read timeout to nInitialTimeoutMS to allow for connection
oNetworkStream.ReadTimeout = nInitialTimeoutMS;
int nBytesRead = 0;
do
{
try
{
bool bDataAvailable = oNetworkStream.DataAvailable;
while (!bDataAvailable)
{
Thread.Sleep(5);
bDataAvailable = oNetworkStream.DataAvailable;
}
nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length);
if (nBytesRead > 0)
{
// Translate data bytes to an ASCII string and append
sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead));
// decrease read timeout to nReadTimeoutMS second now that data is coming in
oNetworkStream.ReadTimeout = nReadTimeoutMS;
}
}
catch (IOException)
{
// read timed out, all data has been retrieved
nBytesRead = 0;
}
}
while (nBytesRead > 0);
//send the data to the callback and get the response back
byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToString(), oTCPClient);
if (abResponse != null)
{
oNetworkStream.Write(abResponse, 0, abResponse.Length);
oNetworkStream.Flush();
}
}
}
catch (Exception e)
{
Logger.Write(new TCPLogEntry("Caught Exception " + e.StackTrace));
}
finally
{
// stop talking to client
if (oTCPClient != null)
{
oTCPClient.Close();
}
}
}
Edit: I get roughly the same figures on two entirely seperate machines (my XP development machine and a 2003 box in a colo). I've put some timing into the code around the relevant parts (using System.Diagnostic.StopWatch) and dump it to a log:
7/6/2009 3:44:50 PM : Debug : While DataAvailable took 0 ms
7/6/2009 3:44:50 PM : Debug : Read took 531 ms
7/6/2009 3:44:50 PM : Debug : ProcessConnection took 577 ms
I recommend you use Microsoft Network Monitor or something like it to see what's going on in terms of those 600ms. NetworkStream is a piece of networking software - when looking at its behavior, always consider what the network is doing.
Another vote for the use of network monitoring software. Either Network Monitor or WireShark should do. Make sure you record what time the networkstream.read call begins and ends in your program so you can know where in the recorded network traffic your program events happened.
Also, I'd recommend waiting for the NetworkStream.DataAvailable property to become true before you call the Read method, and record the time it becomes true as well. If your network monitor shows data arriving 600 ms before your program indicates it can be read, something else on your computer may be holding up the packet - e.g. antivirus or your firewall.
Addendum 2009/7/6 3:12 PM EDT:
The extra timing information you posted is interesting. If data is available, why is it taking so long to read? I ran your code on my development machine, and both waiting for dataavailable and the read function itself comes out as 0 milliseconds. Are you sure you have the latest service packs, etc. installed? I'm running Visual Studio Professional 2005 with .NET 2.0.50727. I also have .NET 3.0 and 3.5 installed, but I don't think VS 2005 is using those. Do you have a fresh OS installation (real or virtual machine) with no extra programs (even/especially ones "required" by corporate IT) that you could try this on?
Here's the code I ran:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace stackoverflowtest
{
class Program
{
static private object oSyncRoot = new object();
static private TcpListener oTCPListener;
static private IPAddress oIPaddress = IPAddress.Parse("10.1.1.109");
static private int nPort = 8009;
static bool bIsListening = true;
static void Main(string[] args)
{
StartListening();
Thread.Sleep(500000);
bIsListening = false;
}
public static void StartListening()
{
try
{
lock (oSyncRoot)
{
oTCPListener = new TcpListener(oIPaddress, nPort);
// fire up the server
oTCPListener.Start();
// set listening bit
bIsListening = true;
}
// Enter the listening loop.
do
{
// Wait for connection
TcpClient newClient = oTCPListener.AcceptTcpClient();
// queue a request to take care of the client
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient);
}
while (bIsListening);
}
catch (SocketException se)
{
Console.WriteLine("SocketException: " + se.ToString());
}
finally
{
// shut it down
//StopListening();
}
}
private static void ProcessConnection(object oClient)
{
TcpClient oTCPClient = (TcpClient)oClient;
try
{
byte[] abBuffer = new byte[1024];
StringBuilder sbReceivedData = new StringBuilder();
using (NetworkStream oNetworkStream = oTCPClient.GetStream())
{
int nInitialTimeoutMS = 1000;
// set initial read timeout to nInitialTimeoutMS to allow for connection
oNetworkStream.ReadTimeout = nInitialTimeoutMS;
int nBytesRead = 0;
do
{
try
{
bool bDataAvailable = oNetworkStream.DataAvailable;
Stopwatch sw = new Stopwatch();
while (!bDataAvailable)
{
Thread.Sleep(5);
bDataAvailable = oNetworkStream.DataAvailable;
}
Console.WriteLine("DataAvailable loop took " + sw.ElapsedMilliseconds);
sw.Reset();
nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length);
Console.WriteLine("Reading " + nBytesRead + " took " + sw.ElapsedMilliseconds);
if (nBytesRead > 0)
{
// Translate data bytes to an ASCII string and append
sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead));
// decrease read timeout to nReadTimeoutMS second now that data is coming in
int nReadTimeoutMS = 100;
oNetworkStream.ReadTimeout = nReadTimeoutMS;
}
}
catch (IOException)
{
// read timed out, all data has been retrieved
nBytesRead = 0;
}
}
while (nBytesRead > 0);
byte[] abResponse = new byte[1024];
for (int i = 0; i < abResponse.Length; i++)
{
abResponse[i] = (byte)i;
}
oNetworkStream.Write(abResponse, 0, abResponse.Length);
oNetworkStream.Flush();
//send the data to the callback and get the response back
//byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToString(), oTCPClient);
//if (abResponse != null)
//{
// oNetworkStream.Write(abResponse, 0, abResponse.Length);
// oNetworkStream.Flush();
//}
}
}
catch (Exception e)
{
Console.WriteLine("Caught Exception " + e.StackTrace);
}
finally
{
// stop talking to client
if (oTCPClient != null)
{
oTCPClient.Close();
}
}
}
}
}
After some more research it seems that the only way to speed this up is to break after the first x bytes have been read. The delay seems to be on the second read. If I change the buffer to be 8096 bytes (probably the max my application will be sent at any one go) and break here:
if (nBytesRead > 0)
{
// Translate data bytes to an ASCII string and append
sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead));
if (bTurboMode)
{
break;
}
else
{
// decrease read timeout to nReadTimeoutMS second now that data is coming in
oNetworkStream.ReadTimeout = nReadTimeoutMS;
}
}
Then the response time goes from 600ms to about 80ms. This is an acceptable solution for me currently. I can toggle the bTurboMode from the calling application and speed things up substantially for this case

Categories