There are plenty of places that deal with terminating C# threads gracefully. However, they rely on a loop or if condition executing inside a loop, which assumes that this statement will be executed frequently; thus, when the stop bool flag is set, the thread exits quickly.
What if I have a thread in which this is not true? In my case, this is a thread set up to receive from a server, which frequently blocks on a call to read data from the input stream, where none is yet provided so it waits.
Here is the thread in question's loop:
while (true)
{
if (EndThread || Commands.EndRcvThread)
{
Console.WriteLine("Ending thread.");
return;
}
data = "";
received = new byte[4096];
int bytesRead = 0;
try
{
bytesRead = stream.Read(received, 0, 4096);
}
catch (Exception e)
{
Output.Message(ConsoleColor.DarkRed, "Could not get a response from the server.");
if (e.GetType() == Type.GetType("System.IO.IOException"))
{
Output.Message(ConsoleColor.DarkRed, "It is likely that the server has shut down.");
}
}
if (bytesRead == 0)
{
break;
}
int endIndex = received.Length - 1;
while (endIndex >= 0 && received[endIndex] == 0)
{
endIndex--;
}
byte[] finalMessage = new byte[endIndex + 1];
Array.Copy(received, 0, finalMessage, 0, endIndex + 1);
data = Encoding.ASCII.GetString(finalMessage);
try
{
ProcessMessage(data);
}
catch (Exception e)
{
Output.Message(ConsoleColor.DarkRed, "Could not process the server's response (" + data + "): " + e.Message);
}
}
The if statement at the top of the block does what the usual stopping-a-thread-gracefully setup does: checks a flag, terminates the thread if it's set. However, this thread is usually to be found waiting a few lines further down, at stream.Read.
Given this, is there any way to gracefully terminate this thread (i.e. no Aborting), and clean up its resources (there's a client that needs to be closed)?
Assuming you can use async / Tasks, the way to do clean stopping of async and IO operations is with a CancelationToken that is connected to a CancelationTokenSource. The below code snippet illustrates a simplified example of its usage when applied to a simplified version of your code.
class MyNetworkThingy
{
public async Task ReceiveAndProcessStuffUntilCancelled(Stream stream, CancellationToken token)
{
var received = new byte[4096];
while (!token.IsCancellationRequested)
{
try
{
var bytesRead = await stream.ReadAsync(received, 0, 4096, token);
if (bytesRead == 0 || !DoMessageProcessing(received, bytesRead))
break; // done.
}
catch (OperationCanceledException)
{
break; // operation was canceled.
}
catch (Exception e)
{
// report error & decide if you want to give up or retry.
}
}
}
private bool DoMessageProcessing(byte[] buffer, int nBytes)
{
try
{
// Your processing code.
// You could also make this async in case it does any I/O.
return true;
}
catch (Exception e)
{
// report error, and decide what to do.
// return false if the task should not
// continue.
return false;
}
}
}
class Program
{
public static void Main(params string[] args)
{
using (var cancelSource = new CancellationTokenSource())
using (var myStream = /* create the stream */)
{
var receive = new MyNetworkThingy().ReceiveAndProcessStuffUntilCancelled(myStream, cancelSource.Token);
Console.WriteLine("Press <ENTER> to stop");
Console.ReadLine();
cancelSource.Cancel();
receive.Wait();
}
}
}
.
Related
I'm implementing a small ClientWebSocket to send messages to the IPAddress.Loopback address of the computer so that a browser utility page can pick them up. however as soon as I connect, the client is immediately closed without message, reason, or exception.
ClientWebSocket cws;
Uri address = new Uri("ws://" + IPAddress.Loopback.ToString() + ":80/"
async void Start(){
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls |
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
using (cws = new ClientWebSocket()) {
cws.Options.KeepAliveInterval = new TimeSpan(1, 0, 0);
//Start response listener loop
_ = Task.Run(() => async_ListenerLoop(receiveCancelTokenSrc.Token).ConfigureAwait(false));
try {
//Start the connection
cws.ConnectAsync(address, sendCancelTokenSrc.Token);
} catch(Exception ex) {
MessageBox.Show("Unable to connect.\n\r\n\r" + ex.Message);
}
}
}
async Task async_ListenerLoop(CancellationToken cancelToken) {
ArraySegment<byte> buffer = WebSocket.CreateServerBuffer(RECIEVE_BUFFER);
List<byte> rawData = new List<byte>();
try {
while (state != WebSocketState.Closed && state != WebSocketState.Aborted && !cancelToken.IsCancellationRequested) {
if(state == WebSocketState.Connecting) {
//Sometimes it waits 2-3 cycles, other times it closes immediately
await Task.Delay(500);
continue;
}
WebSocketReceiveResult receiveResult = await cws.ReceiveAsync(buffer, cancelToken);
if (!cancelToken.IsCancellationRequested) {
if (state == WebSocketState.CloseReceived && receiveResult.MessageType == WebSocketMessageType.Close) {
sendCancelTokenSrc.Cancel();
await cws.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", CancellationToken.None);
//The socket state changes to closed at this point
}
if (state == WebSocketState.Open) {
if (receiveResult.MessageType == WebSocketMessageType.Text) {
rawData.AddRange(buffer.Array);
if (receiveResult.EndOfMessage) {
string strData = Encoding.UTF8.GetString(rawData.ToArray(), 0, rawData.Count);
ProcessData(JsonConvert.DeserializeObject<RequestDigest>(strData));
rawData.Clear();
}
} else if (receiveResult.MessageType == WebSocketMessageType.Binary) {
rawData.AddRange(buffer.Array);
if (receiveResult.EndOfMessage) {
ProcessData(rawData.ToArray());
rawData.Clear();
}
}
}
}
}
//Descripion is always null
MessageBox.Show("Connection was closed because '"+cws.CloseStatusDescription+"'");
} catch (OperationCanceledException ex) {
// normal upon task/token cancellation, disregard
MessageBox.Show("Connection to server cancelled.\n\r\n\r" + ex.Message);
} catch (JsonReaderException ex) {
MessageBox.Show("Unable to read incoming message.\n\r\n\r" + ex.Message);
} catch (Exception ex) {
MessageBox.Show("Unable recieve incoming message.\n\r\n\r" + ex.Message);
} finally {
sendCancelTokenSrc.Cancel();
AbortConnection();
}
}
void AbortConnection() {
// don't leave the socket in any potentially connected state
if (state != WebSocketState.Closed) {
cws.Abort();
}
cws.Dispose();
}
There may be other problems with your code (wiring up sockets and clients takes a bit of messing about before you get it right) but there are two glaring problems:
You need to await the result of ConnectAsync(...), to stop the code of Start() continuing synchronously without it:
await cws.ConnectAsync(address, sendCancelTokenSrc.Token);
And you need to make sure that cws does not get disposed until you've finished with it. The using(cws) { ... } block will dispose cws as soon as the code leaves the squiggly braces (which happens straight away currently), even though your listener is still running, and needs it to remain alive.
You can either do that by keeping the code within the using block by awaiting an asynchronous result (such as hint! a final result from your listener) - or by not using using at all and manually disposing cws yourself at the right time.
Also make sure to only start your listener after the connection call has completed, to ensure cws is in a good state.
Something like this:
using(cws = new ClientWebSocket())
{
await cws.ConnectAsync(...);
await DoSomeListeningForABit();
} //cws gets implicitly disposed here
And... your listener will doubtlessly require more work too, but the above will get you past your immediate problem (hopefully!)
I have a simple socket connection that runs a read task in async. When I disconnect I get an exception:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'SslStream'.
I don't know how to stop it trying to read when it is sitting and waiting to read a message when I call disconnect. This is my read code:
public async Task<int> Read(byte[] readBuffer)
{
if (!IsConnected)
{
return 0;
}
try
{
await Stream.ReadAsync(_header, 0, _header.Length);
return await Stream.ReadAsync(readBuffer, 0, _header[0]);
}
catch (Exception e)
{
Debug.Log(e);
Disconnect();
return 0;
}
}
protected async Task _RunReader()
{
var totalBytes = await Read(_readBuffer);
if (totalBytes > 0)
{
HandleReader(_readBuffer, totalBytes);
_RunReader();
}
}
And this is my disconnect code:
public async Task Disconnect(bool graceful = false)
{
if (IsConnected && graceful)
{
lockPackets = true;
Empty[0] = 1; // send disconnect message to server for graceful
await Send(Empty,1);
}
Stream?.Close();
TcpClient.Close();
IsConnected = false;
}
Is there any way to abandon ReadAsync when Disconnect is called to avoid getting an exception? It doesn't effect the program since I try catch, but I feel like there's probably a more elegant way than just allowing it throw silent exceptions?
I have to read a file and after processing it's data write result to another file. This process takes too much time so I tried to do read/process/write in a parallel way. Code works well. But there is a problem, when I was testing it for processing very huge file (100GB) after about 40GB program stops working. It do not throw any exception (except 'TimeoutException'). I spend several days and tried to change many things. I know it is not about method or memory. But I am really confused about reason and way to make it works well.
Note: I wanted to post this on Code Review, but Code Review rules says do not post for trouble shooting and else so I posted it here.
The way I use code (It is on backgroundworker)
BackgroundWorker worker = (BackgroundWorker)sender;
ReaderWriterMultiThread readerWriterMultiThread = null;
int bufferSize = 2 * 1024 * 1024;
int readerWriterMultiThreadPartsNumber = 10;
int sizeToReadInThisIteration = 0;
int oldprecentage = 0;
long fileDid = 0;
using (FileStream streamReader = new FileStream(fromAddress, FileMode.Open))
using (BinaryReader binaryReader = new BinaryReader(streamReader))
using (FileStream streamWriter = new FileStream(toAddress, FileMode.Open))
using (BinaryWriter binaryWriter = new BinaryWriter(streamWriter))
{
sizeToReadInThisIteration = bufferSize * readerWriterMultiThreadPartsNumber;
streamWriter.Seek(0, SeekOrigin.Begin);
while (streamWriter.Position < length)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
//change sizeToReadInThisIteration if needs
if (streamWriter.Position + sizeToReadInThisIteration > length)
{ sizeToReadInThisIteration = Convert.ToInt32(length - streamWriter.Position); }
//new it
readerWriterMultiThread = new ReaderWriterMultiThread();
//read/do/write
readerWriterMultiThread.Start(binaryReader, binaryWriter, bufferSize, sizeToReadInThisIteration,
(ref byte[] bytes) => DoNothing(ref bytes));
//report process if needs
fileDid += sizeToReadInThisIteration;
if (((int)(fileDid * 100 / length)) > oldprecentage)
{
oldprecentage = (int)(fileDid * 100 / length);
worker.ReportProgress(oldprecentage);
}
}//while
}//using
DoNothing method is:
public void DoNothing(ref byte[] bufferToCode)
{ }
and ReaderWriterMultiThread class is:( Originally code used threads but I changed it to use tasks.)
public class ReaderWriterMultiThread
{
#region variables
//buffer(contain several part)
List<byte[]> buffer = new List<byte[]>();
//lock objects
private object bufferLockForRead = new object();
private object bufferLockForWrite = new object();
//indexes
int readIndex = 0;
int doReadIndex = 0;
int doWriteIndex = 0;
int writeIndex = 0;
//complete vars
int lastIndex = int.MaxValue;
bool readCompleted = false;
//waiting properties
private bool doIsWaiting = false;
private bool writerIsWaiting = false;
//error properties
private bool anyErrorHappend = false;
private string errorsMessage = string.Empty;
//proc delegate
public delegate void DelegateMethod(ref byte[] bytes);
//proc delegate instance
DelegateMethod delegateM;
//
#endregion variables
//==============================
#region methods
//
public void Start(BinaryReader binaryReader, BinaryWriter binaryWriter, int bufferPartsSize, int size, DelegateMethod delegateMethod)
{
//new delegate
delegateM = new DelegateMethod(delegateMethod);
//for wait all
Task[] tasks = new Task[3];
//run
var parentTask = Task.Factory.StartNew(() =>
{
tasks[0] = Task.Factory.StartNew(() =>
{
Writer(binaryWriter);
});
tasks[1] = Task.Factory.StartNew(() =>
{
Do();
});
tasks[2] = Task.Factory.StartNew(() =>
{
Reader(binaryReader, bufferPartsSize, size);
});
});
//wait
parentTask.Wait();
if (!Task.WaitAll(tasks, 10000))
{ throw new TimeoutException(); }
if (anyErrorHappend)
{ throw new Exception(errorsMessage); }
}
private void AddByReader(byte[] newBytes, bool completed)
{
try
{
lock (bufferLockForRead)
{
//add data to buffer
buffer.Add(newBytes);
//updare readIndex
readIndex++;
//if completed show it
if (completed)
{
readCompleted = true;
lastIndex = buffer.Count;//it uses as <lastIndex (so lastIndex = buffer.Count is ok)
}
//manage happend error
if (anyErrorHappend)
{
readCompleted = true;
lastIndex = doReadIndex + 1;
}
//if do is waiting pulse it
if (doIsWaiting)
{ Monitor.Pulse(bufferLockForRead); }
}
}
catch (Exception ex)
{ Debug.Assert(false, ex.ToString()); }
}
private byte[] GetByDo()
{
try
{
lock (bufferLockForRead)
{
//if data did not read already wait
if (doReadIndex == readIndex)
{
doIsWaiting = true;
Monitor.Wait(bufferLockForRead);
}
//do is not waiting now
doIsWaiting = false;
//in case of emergency
if (doReadIndex > readIndex)
{ return new byte[0]; }
//return
return buffer[doReadIndex++];
}
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
return new byte[0];
}
}
private void AddByDo(byte[] newBytes, string errorMessageFromDO)
{
try
{
lock (bufferLockForWrite)
{
//add data
buffer[doWriteIndex] = newBytes;
//update doWriteIndex
doWriteIndex++;
//error happend in Do
if (errorMessageFromDO.Length > 0)
{
anyErrorHappend = true;
errorsMessage += errorMessageFromDO;
lastIndex = -1;
Monitor.Pulse(bufferLockForWrite);
}
//if reader completed and writer is in wait state pulse it
if (readCompleted && writerIsWaiting)
{
Monitor.Pulse(bufferLockForWrite);
}
}
}
catch (Exception ex)
{ Debug.Assert(false, ex.ToString()); }
}
private byte[] GetByWriter()
{
try
{
lock (bufferLockForWrite)
{
//if data did not proccessed wait
if (writeIndex == doWriteIndex)
{
writerIsWaiting = true;
Monitor.Wait(bufferLockForWrite);
}
//writer is not waithing
writerIsWaiting = false;
//return
return buffer[writeIndex++];
}
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
return new byte[0];
}
}
private void Reader(BinaryReader binaryReader, int bufferPartSize, int sizeToRead)
{
try
{
//vars
bool completed = false;
int readedSize = 0;
byte[] readedBytes = new byte[0];
while (readedSize < sizeToRead && !anyErrorHappend)
{
//change bufferPartSize & completed if needs
if (readedSize + bufferPartSize >= sizeToRead)
{
bufferPartSize = sizeToRead - readedSize;
completed = true;
}
try
{
//read
readedBytes = binaryReader.ReadBytes(bufferPartSize);
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
//error happend
anyErrorHappend = true;
errorsMessage += ex.Message;
//for pulse Do() if it is waiting
byte[] amptyBytesArray = new byte[0];
AddByReader(amptyBytesArray, true);//it is better to do it instead change lastIndex here
break;
}
//add to buffer
AddByReader(readedBytes, completed);
//update readedSize
readedSize += bufferPartSize;
}
}
catch (Exception ex)
{ Debug.Assert(false, ex.ToString()); }
}
private void Writer(BinaryWriter binaryWriter)
{
try
{
//vars
byte[] bytesToWrite = new byte[0];//for put getted data in
for (int i = 0; i < lastIndex; i++)
{
//get data from buffer
bytesToWrite = GetByWriter();
try
{
//write
binaryWriter.Write(bytesToWrite);
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
lastIndex = -1;
anyErrorHappend = true;
errorsMessage = ex.Message;
break;
}
}
}
catch (Exception ex)
{ Debug.Assert(false, ex.ToString()); }
}
private void Do()
{
try
{
//vars
byte[] bytes = new byte[0];//for put readed data/result in
for (int i = 0; i < lastIndex; i++)
{
//get data from buffer
bytes = GetByDo();
try
{
//do
delegateM(ref bytes);
}
catch (Exception ex)
{
Debug.Assert(false, ex.ToString());
//add
AddByDo(new byte[0], "error: " + ex.Message);
break;
}
//add data to buffer
AddByDo(bytes, string.Empty);
}
}
catch (Exception ex)
{ Debug.Assert(false, ex.ToString()); }
}
//
#endregion methods
}
You code is throwing an exception here:
if (!Task.WaitAll(tasks, 10000))
{ throw new TimeoutException(); }
Which means that one of your tasks is taking more than 10 seconds to complete.
This could be because it needs more time. It could also be because one of your tasks is getting stuck waiting to enter a lock. It could also because the Monitor.Wait is getting stuck waiting for a lock.
You can add logging to see where it is getting hung. Additionally if one of your assert statements fail it will prevent a task from completing in time.
As a side note, Disk IO is time consuming and in general trying to parallelize IO operations like this typically aren't going to help because the threads end up stomping all over each other contending for disk access time. The disk ends up needing to spin to numerous positions back and forth to read and write data and you end up with a net effect of actually slowing things down. You might be able to speed things up if you are using RAID or you are reading from one disk and adding the reads to a queue and your write thread is reading from that queue and writing that data to a different disk.
Do 3 chunks sent by 3 calls to Linux 'C' write(), through TCP, get received as the same, 3 chunks by Windows C# .BeginReceive(), or a single, contiguous chunk, or however many have been received when .BeginReceived is called?
A 'C' app on Linux sends a message by 3 calls to write(), through TCP connection, to a Windows C# app, which receives using BeginReceive().
Does BeginReceive() need to be called three times, to receive each of the three chunks sent by write()? Or is the size received by BeginReceive() equal to the size of what Windows has received when BeginReceive() is called? Which could be all bytes sent by the 3 writes(), or a partial amount, so .BeginReceive() should be called UNTIL all are received?
The Linux C app is running on an embedded TI ARM, and inside the same box the Windows C# app is running a Single Board Computer. The ARM has a direct Ethernet connection to the SBC.
The communication between the ARM and SBC sometimes fails to start at boot time, and I'm reverse engineering the source code to check for bad design.
ARM side is TCP listener, and Windows client initiates the TCP connection.
Using MontaVista(R) Linux(R) Professional Edition 5.0.0 (0702774)
and Windows-7 Visual-Studio 2010 Visual-C#.
Here is the ARM sending software, and the Windows receiving software........................
LINX 'C'
char msgBuffer[64];
sprintf(msgBuffer, START_MSG_ENVELOPE, msgId++, ack);
write(connection->fd, msgBuffer, strlen(msgBuffer));
write(connection->fd, msg, strlen(msg));
write(connection->fd, END_MSG_ENVELOPE, strlen(END_MSG_ENVELOPE));
HERE IS THE WINDOWS C# SIDE OF IT.............................................
private static void makeConnection(Socket clientSocket, int iPortNo)
{
TimeoutObject.Reset();
socketexception = null;
IPAddress ip;
//create the end point
IPEndPoint ipEndPoint;
ip = IPAddress.Parse(ipAddress);
try
{
ipEndPoint = new IPEndPoint(ip, iPortNo);
//connect to the remote host...
clientSocket.BeginConnect(ip, iPortNo, new AsyncCallback(CallBackMethod), clientSocket);
if (TimeoutObject.WaitOne(5 * 1000, false)) //5 secs connection timeout
{
if (!IsConnectionSuccessful)
{
string msg = VNResourceManager.Instance.GetString(VNMessages.DAM_NOT_FOUND);
if (socketexception != null)
msg += ": " + socketexception.Message;
throw new Exception(msg);
}
}
else
{
clientSocket.Close();
throw new TimeoutException(VNResourceManager.Instance.GetString(VNMessages.CONNECTION_TO_DAM_TIMED_OUT));
}
//watch for data ( asynchronously )...
WaitForData();
}
catch (SocketException e)
{
socketexception = e;
throw;
}
}
private static void CallBackMethod(IAsyncResult asyncresult)
{
try
{
IsConnectionSuccessful = false;
Socket socket = asyncresult.AsyncState as Socket;
if (socket.Connected)
{
socket.EndConnect(asyncresult);
IsConnectionSuccessful = true;
}
}
catch (Exception ex)
{
IsConnectionSuccessful = false;
socketexception = ex;
}
finally
{
TimeoutObject.Set();
}
}
public static void WaitForData()
{
try
{
if (asyncCallBack == null)
{
asyncCallBack = new AsyncCallback(OnDataReceived);
}
CSocketPacket theSocPkt = new CSocketPacket();
theSocPkt.thisSocket = clientSocket;
asyncResult = clientSocket.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, asyncCallBack, theSocPkt);
}
catch (SocketException se)
{
notifyErrorEventSubscribers(se);
}
}
public static void send(string message)
{
try
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(message);
clientSocket.Send(byData);
}
catch (SocketException se)
{
notifyErrorEventSubscribers(se);
throw;
}
}
//[MethodImpl(MethodImplOptions.Synchronized)]
public static void OnDataReceived(IAsyncResult result)
{
try
{
CSocketPacket theSockId = (CSocketPacket)result.AsyncState;
//end receive...
int messageSize = 0;
messageSize = theSockId.thisSocket.EndReceive(result);
Console.WriteLine(">>>>>>>>> messageSize = " + messageSize); // !!!
char[] chars = new char[messageSize + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int charLen = d.GetChars(theSockId.dataBuffer, 0, messageSize, chars, 0);
string replyMessage = new System.String(chars);
lock (syncLock) //LastIndexOf function accesses the current culture info and we clear it in WM_TIMECHANGE handler (protecting from that race condition here)
{
if (replyMessage.LastIndexOf("\0") > 0)
replyMessage = replyMessage.Remove(replyMessage.LastIndexOf("\0"), 1);
if (replyMessage.LastIndexOf(Terminator) > 0)
replyMessage = replyMessage.Remove(replyMessage.LastIndexOf(Terminator), 1);
}
// Continue the waiting for data on the Socket
WaitForData();
receivedMsg += replyMessage;
// only serialize when we feel we have a message or we have reached the message line limit
if (((receivedMsg.Contains("message") && receivedMsg.Contains("/>")) || receivedMsg.Contains("</message>")) /* || (mRecvdMsgLineCount == Message.kMaxLines) */ )
{
List<XmlMessage> msgList = new List<XmlMessage>();
int index = -1;
do
{
index = receivedMsg.IndexOf("</message>");
if (index != -1)
{
XmlMessage message;
string strMessage = receivedMsg.Substring(0, index + "</message>".Length);
//MessageBox.Show(strMessage);
strMessage = strMessage.TrimStart(new char[] { '\r', '\n' });
receivedMsg = receivedMsg.Remove(0, index + "</message>".Length);
try
{
message = (XmlMessage)XmlMessage.GetXmlSerializer().Deserialize(XmlReader.Create(new StringReader(strMessage)));
}
catch (InvalidOperationException error)
{
string strErrorMessage = error.Message;
if (error.InnerException != null)
strErrorMessage += "\r\n" + error.InnerException.Message;
notifyErrorEventSubscribers(new Exception(strErrorMessage + "\r\n-----------------------------------------------------------------\r\n" + strMessage));
return;
}
msgList.Add(message);
}
} while (index != -1);
StringWriter sw = new StringWriter();
string serializedXml = string.Empty;
string strXmlMessage = string.Empty;
foreach (XmlMessage message in msgList)
{
if (message.ack_required && (message.update == null))
{
XmlMessage messageAcknowledge = new XmlMessage();
messageAcknowledge.ack_required = false;
messageAcknowledge.ack = new ack();
messageAcknowledge.ack.success = true;
messageAcknowledge.ack.id = message.id;
try
{
sendMessage(messageAcknowledge);
}
catch(Exception ex)
{
Logger.Log(EnumLoggingLevel.Error, "SocketCommunicationXMl.OnDataReceived", ex.Message);
}
}
if (dataReceivedEvent != null)
{
dataReceivedEvent(new object(), new DataReceivedEventArgs(message));
}
if ((ackRequiredMsg != null) && (message.ack != null))
{
if ((message.ack.id == ackRequiredMsg.id) && message.ack.success)
{
eventWaitForAck.Set();
}
}
}
}
}
catch (ObjectDisposedException objectDisposedException)
{
// Dispatcher.dispatchDebug(Debug.Level_3,"Socket has been closed", this);
notifyErrorEventSubscribers(objectDisposedException);
}
catch (SocketException se)
{
if (se.ErrorCode == 10054)
{
/*
for (int i = 0; i < 50; i++)
{
Thread.Sleep(1000);
}
try
{
SocketCommunicationDaq.Reconnect();
}
catch(Exception ex)
{
VNMsgBox.Show(ex.Message, MsgButtonType.OkOnly, MsgIconType.Error);
return;
}
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
for (int i = 0; i < 3; i++)
{
try
{
connect();
break;
}
catch (Exception ex)
{
System.Threading.Thread.Sleep(5000);
}
}
*/
Logger.Log(EnumLoggingLevel.Error, "OnDataReceived: ", se.ToString());
}
else
{
notifyErrorEventSubscribers(se);
}
}
}
As others have mentioned, TCP is a streaming protocol, so you never can tell how many DataReceived callbacks it will take to get all 100 bytes. Could be 1, could be 100.
The receive code is fairly complex and performance could be improved (too many string operations). Hard to tell if there are control-flow issues. I would suggest breaking the DataReceived method up to simplify. Here's a reasonable skeleton:
public static void OnDataReceived(IAsyncResult result)
{
//1) copy all data received into a buffer of some sort, like MemoryStream
//2) Dispatch any/all full messages that have been received
// to a queue or handler method (maybe handle on another thread)
//(hold onto any leftover bytes received that are part of the next message)
//Call BeginReceive() again
}
Also, it can help simplify Framing if you use a Length-Prefixed message format.
As #nos already stated, number of receives does not equal the number of sends, regardless of the client application was written in.
See also When does TcpClient's NetworkStream finish one read operation?
I wrote a service to send sensor data over bluetooth on android. Although I got no errors my C# client stops getting data after I sent exactly 561K data. At this moment it seems like my android continues to send data but my client doesn't get any. After a while, android also stops sending data. I tried different configurations. My program always stops sending data after "Service->Server". I don't get any errors but it stops sending. Here is android program.
#Override
public synchronized int onStartCommand(Intent intent, int flags, int startId) {
Log.i(EXTRA_MESSAGE,"onStartCommand");
if(isRunning)
Log.e(EXTRA_MESSAGE, "I am already running");
else
{
isRunning = true;
BluetoothDevice selectedDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRA_MESSAGE);
if(selectedDevice == null)
{Log.i(EXTRA_MESSAGE,"null it is "); return -1;}
connect = new ConnectThread(selectedDevice);
connect.start();
mHandler =new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case SUCCESS_CONNECT:
connected = new ConnectedThread((BluetoothSocket) msg.obj);
Toast.makeText(getApplicationContext(), "Connected", 0).show();
//connected.write(("Connected").getBytes());
Log.i(EXTRA_MESSAGE, "we are connected");
isConnected = true;
break;
case MESSAGE_READ:
Toast.makeText(getApplicationContext(), ((byte[]) msg.obj).toString(), 0).show();
break;
}
}
};
mSensor = (SensorManager) getSystemService(SENSOR_SERVICE);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
// sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
}
return super.onStartCommand(intent, flags, startId);
}
#Override
#Override
public void onSensorChanged(SensorEvent event) {
Log.i(EXTRA_MESSAGE, "Sensor data arrived");
if(isConnected )
{
String toSend = Integer.toString(event.sensor.getType())+ ":" + Long.toString(event.timestamp)+ ":";
for(float f : event.values){
toSend = toSend + Float.toString(f)+":";
}
//
connected.write(toSend.getBytes());
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.i(EXTRA_MESSAGE,"connectThread started successfully");
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
Log.i(EXTRA_MESSAGE,"connectThread connect successfully");
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.i(EXTRA_MESSAGE,"connectThread connect exception");
try {
mmSocket.close();
} catch (IOException closeException) { }
}
// Do work to manage the connection (in a separate thread)
mHandler.obtainMessage(SUCCESS_CONNECT, mmSocket).sendToTarget();
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public boolean shouldContinue = true;
int nBytes =0;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
Log.i(EXTRA_MESSAGE,"connectedThread sockets");
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while(shouldContinue) {
try {
// Read from the InputStream
buffer = new byte[1024];
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
Log.e(EXTRA_MESSAGE, " We read");
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
Log.i(EXTRA_MESSAGE,"Service->Server");
mmOutStream.write(bytes);
nBytes += bytes.length;
Log.i(EXTRA_MESSAGE,"ok" + String.valueOf(nBytes ));
} catch (IOException e) {
Log.i(EXTRA_MESSAGE,"exception");
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
shouldContinue = false;
mmSocket.close();
} catch (IOException e) { }
}
}
Also my c# thread is as follows
public void ServerConnectThread()
{
serverStarted = true;
int counter = 0;
updateUI("Server started, waiting for clients");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
BluetoothClient conn = blueListener.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mStream = conn.GetStream();
while (true)
{
try
{
//handle server connection
byte[] received = new byte[1024];
mStream.Read(received, 0, received.Length);
counter += Encoding.ASCII.GetString(received).Length;
String[] fields = Encoding.ASCII.GetString(received).Split(':');
double[] data = new double[3];
for (int i = 2; i < 5; i++) data[i-2] = double.Parse(fields[i]);
//mSource.notifyObserver(Int16.Parse(fields[0]), data);
updateUI(counter.ToString() + " "+ fields[2]+ ":" + fields[3] + ":" + fields[4]);
byte[] sent = Encoding.ASCII.GetBytes("Hello World");
mStream.Write(sent, 0, sent.Length);
}
catch (IOException exception)`enter code here`
{
updateUI("Client has disconnected!!!!");
}
}
}
One final thing is I've found thousands of 561K android program which sounded a little interesting.
Ok I found my own problem. Basically I m sending a reply from my client when I got the sensor data and It is not handled by android app.