August 2009


I’ve carried out the same testing on Windows Mobile now.  I carried out the test various times, including one test that repeated the 5MB PUT 150 times.  In all cases I saw no problems.  So that looks like the issue is with the Widcomm support on Win32.  If there were issues on Windows Mobile perhaps they were particular to the remote server device.

Advertisements

In the forums there have been complaints that OBEX on the Widcomm stack on WindowsMobile devices didn’t work in particular cases (e.g. PDF to a mobile phone).  I’ve been doing some testing to see if I can reproduce any faults.

I’ve been running a test of OBEX sending 5MB files repeatedly from a Windows XP PC with Widcomm to a PC running an XP with the Microsoft stack.  Two faults in Widcomm behaviour have been seen, both after many reps, e.g. about 60 of, that is after about 300MB of data.  Both result in data transfer stopping (I’m glad to report however that we see no data corruption).

Firstly we see sometimes that Widcomm stops sending its “buffer empty, you can now send some more” events.  When we have new data to send we call Widcomm’s CRfCommPort.Write(void *p_data, UINT16 len_to_write, UINT16 *p_len_written) method with the buffer of data we have.  As can be seen from the method signature this method can return having read only part of the data; in that case we store the data ourselves and wait for an indication that we can write again.  We use the TXEMPTY event for this, in the case here, the event never comes.  We see e.g.

... ...
m_port.Write: len in: 16384, len out: 8820
HandleEvent: 0x10000=FC
HandleEvent:  0x4000=TXCHAR
HandleEvent:  0x4000=TXCHAR
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x34004=TXEMPTY, TXCHAR, FC, FCS
m_port.Write: len in: 7564, len out: 7564
HandleEvent: 0x10000=FC
HandleEvent:  0x4000=TXCHAR
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x10000=FC
HandleEvent: 0x34000=TXCHAR, FC, FCS
HandleEvent: 0x34004=TXEMPTY, TXCHAR, FC, FCS
HandleReceive: len: 3
m_port.Write: len in: 16384, len out: 8820
HandleEvent: 0x10000=FC

So there we are progressing as normal, sending any remaining data on TXEMPTY, and then we do send which accepts only part of the data but no TXEMPTY then occurs.  In fact we see only one event an FC (Flow Control) event which means that the peer has sent a “stop” message.  It’s hard to know whether that’s truly what occurs, or whether something has gone wrong in the Widcomm stack locally.

Secondly I’ve seen a case where the call to the Widcomm Write method simply never returns.  Yikes!  That’s a really nasty one, and we’d have to have clever code to stop that causing a deadlock.  Anyway, both of these could cause the observed problems, we stop sending data due to these faults, the server has a timeout on its side and closes the connection and thus we see the EndOfStreamException or IOException.

I’ll try the same test on Windows Mobile and see what occurs.

The support for server-side in 2.4 (beta) was preliminary.  WidcommBluetoothListener only attempted to handle one pending connection at a time and rather than implementing its own initiating and event handling code it just piggy-backed on the consumer code’s Begin-/ AcceptBluetoothClient call.  (The latter aspect, as we discussed at http://inthehand.com/forums/p/2223/7752.aspx#7752, meaning of course that a connection couldn’t be accepted until BeginAcceptBluetoothClient had been called, and thus we couldn’t support the Pending method).

So for a full implementation we need to keep: 1. a queue of accepted connections, 2. a queue of waiting ports, and also 3. a queue of waiting acceptors (if the consumer has called Begin-/AcceptBluetoothClient one or more times before connection(s) have arrived).

If one looks at the UNIX Sockets internal e.g. via W. Richard Stevens’ book then one sees that there’s no “waiting sockets” queue.  The Widcomm API however requires a port is created and set into server-mode for a connection to be accepted.

I’v implemented this and my current version of the code is at https://32feetnetdev.wordpress.com/widcommbtlistener-2-0/.  However testing it threw up interesting behaviour so I have not committed it yet.  If anyone can have a play and confirm my solution then we can get it checked in.

The odd behaviour that I saw was that if I opened a connection to the server and closed it from the remote device, then the next connection would reuse the old port (Widcomm CRfCommPort) instead of using a port from the waiting queue.  This was surprising behaviour to me, and I had to add lots of diagnostic code to the stream and port classes (WidcommRfcommStream and WidcommRfcommPort) to confirm it.  (The odd behaviour became apparent promptly as an Assert on the old port instance reported that a CONNECT event was occurring when it was in PeerDidClose state, without the assert I may have been confused for much longer).  It is not discussed in the Widcomm documentation, neither explicitly not by implication as far as I can see.

Anyway, the solution to this should be simple.  Currently we don’t call the CRfCommPort.Close method until we Close/ Dispose the NetworkStream/ BluetoothClient (actually WidcommRfcommStream).   Thus the port is still active and apparently Widcomm will reuse it then.  So we simply have to Close the port as soon as we receive the event signalling that the peer close (i.e. CONNECT_ERR).

The diff is this:

--- d:\temp\temp\WidcommRfcommStream;C56017.cs	2009-08-06 21:54:50.250000000 +0100
+++ d:\working\InTheHand.Net.Personal\InTheHand.Net.Personal\Net.Bluetooth.Widcomm\WidcommRfcommStream.cs	2009-08-06 21:53:24.734375000 +0100
@@ -614,6 +614,7 @@
                         DebugId + ": " +
                         eventId + ": m_state wanted: " + "Connected" + ", but was: " + m_state_);
                     Console.WriteLine("HandlePortEvent: closed when open.");
+                    m_port.Close();
                     // No information whether this is a hard or clean close! :-(
                     m_state_ = State.PeerDidClose;
                     if (GetPendingReceiveDataLength() == 0) {

There’s another possibility, that is to return the disconnected port to a pool from where it can be used when we start the next waiter.  However we’re not creating enough ports for that to be necessary and it would be rather more complex and could make debugging more difficult so we’ll just go ahead and close the port.

This change affects all connection both client as well server.  We have to make sure it doesn’t break anything.  In particular we have to make sure that once a port is disconnected by the remote device (state PeerClose) that we don’t access the port.   All feedback  welcomed.