• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Failure attempting to implement an NSPort subclass for Distributed Objects
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Failure attempting to implement an NSPort subclass for Distributed Objects


  • Subject: Failure attempting to implement an NSPort subclass for Distributed Objects
  • From: Wade Tregaskis <email@hidden>
  • Date: Tue, 7 Oct 2003 19:00:25 +1000

Just a warning, this email is very long. I understand that most people don't particularly care for this topic, so I ask them to have patience and consideration, and ignore it if so. There's no attachments, I've tried to be concise, and it's taken me four hours to write, so please don't abuse me about it.

I'll start from the beginning. I'm implementing a subclass of NSPort which uses SecureTransport and the 3rd party NetSocket for TCP communication, i.e. Secure Distributed Objects. The current source is available from <http://www.sourceforge.net/projects/securedo/>, under the BSD license. It doesn't work at the moment, of course, which is why I'm writing this.

[I subclassed NSPort rather than NSSocketPort because it seemed about as much effort either way, and I felt that the less I had to try and bend to NSSocketPort's own socket implementation, the better. In hindsight this seems like a reasonable decision.]

In short, the problem I'm having is that after sending a message through and dispatching it, the reply the sent back is not the root object [or whatever else] requested, but apparently some sort of error. Since this is all undocumented, I'm not sure what the error (if that's what it is) is. I'll get the details a little later.

First, I'll describe the whole shebang. I've skipped over some of the less significant (in my eyes) details, so you might be best off looking at the code before asking me about anything which is obviously missing from my description.

I create two of my custom NSPort subclasses, one a listener on a known port, the other to connect to localhost on that port. The listener gets given to a server NSConnection as both the send and receive port, in another thread. The client port gets given to a client NSConnection as the send port, in the current thread. The receive port given as nil. This is documented as having the NSConnection automatically allocated a receive port as necessary, which it does (more on that later).

Then, I ask the client NSConnection for the "rootProxy". This does whatever it does inside the NSConnection, getting back into my world when the NSConnection [or one of it's servants] calls sendBeforeDate: on the client port I gave it. I then establish a connection and an SSL session. Note that on the server side this creates a new instance of my custom NSPort subclass, leaving the original server NSPort subclass still listening.

All the connection and SSL stuff is done asynchronously (as NetSocket works asynchronously), so sendBeforeDate: runs a few choice runloops until the whole process is finished. I imagine this will create potential deadlocks if you're trying to communicate between two port's in the same thread, but since that's not a useful scenario anyway, I'm content to ignore it for the time being.

Once that's done, the code in sendBeforeDate transmits the required data using SSLWrite(). The protocol it uses is:

// 5-byte header:
//
// [4] Packet length (including this 5-byte header)
// [1] Compression flag (\001 = ON, \000 = OFF)

// Packet structure (not including 5-byte header):
//
// [4] Message ID
// [*] Receiver (if any; may be nil)
// [2] Address length (including this field)
// [*] Address // [4] Object count
// [4] Number of objects
// [*] Objects:
// [4] Object length (including this field and the object type field)
// [1] Object type (\000 = invalid, \001 = NSData, \002 = SecureSocketPort)
// [*] Object data

If compression is enabled, the payload (everything except the 5-byte header) is compressed using zlib. I haven't tested this much, but I always have it disabled in my testing anyway. There are no data corruption issues, as I'll prove later.

The "Message ID" is the unsigned msgid parameter passed into the sendBeforeDate method. Although I do respect it's value and encode it properly, it is always 0 in my testing, so there's no problems there.

The "Receiver" sub-section encodes the local address of the "from" parameter passed into sendBeforeDate. This "from" parameter in the example case is an instance of my subclass created automatically by the client NSConnection (as I mentioned earlier). This client listener binds itself to a random free port (49747 in this particular example). Thus, in my particular case, the encoded form is simply "131.172.83.106:49747" (where 131.172.83.106 is my IP). Anyway, this is all encoded fine.

The "Number of Objects" is of course just the number of items in the components array that was passed in, which is apparently always 1 for requesting a root object. The single NSData instance that's always given is encoded as the spec [above] indicates. The data for this NSData instance is:

...........NSInvocation......NSDistantObject...........rootObject....@@ :..@...D

Or, in hex:

04edfe1f 0e010101 01010d4e 53496e76 6f636174 696f6e00 00010101 104e5344 69737461 6e744f62 6a656374 00000001 01010102 01010b72 6f6f744f 626a6563 74000101 0440403a 00014001 0000

I should note that every retry (caused by repetitive calls to "rootProxy" on the client NSConnection) sends exactly the same data, except with a single byte [somewhere at the start] incremented by one each time - presumably some kind of internal message count or sequencing number.

Anyway, this is exactly what you see being sent with an NSSocketPort (using tcpdump), so clearly I'm getting given the same data as an NSSocketPort, and I'm transmitting it all as expected.

At the server end, the server port (not the one I gave to the server NSConnection, but the one spawned to handle the received connection) receives the message and decodes it successfully. I have verified through multiple approaches that the data is received and is not changed by a single bit.

When the server port comes to the "Receive" portion of the packet, it looks for an existing connection to that address and port. In the first case, one doesn't exist, so it creates a new instance of my custom port to connect back to the client. This new port is passed on to the server NSConnection, as I'll later explain.

I reconstruct the components array as an NSMutableArray with the single NSData in it. The reconstructed data matches the original data perfectly, so there's no encoding or translation issues (not that I imagine there would be).

I've then tried two variations of two methods for passing the message on to the server NSConnection. In the first instance, I create an NSPortMessage with the given msgid and components, and the send port set to the new port I created [to connect back to the client], and the receive port set to 'self'. I then call the server NSConnection's "handlePortMessage:" with the NSPortMessage.

Alternatively I just create an NSPortCoder from the data, in the same manner as an NSPortMessage, and call it's dispatch method. That seems to get the message to the server NSConnection just fine, even though I don't tell it which NSConnection to go to. Presumably it uses the default one for the current thread. This seems to be what NSSocketPort does, from what I can see using 'Sample'. It seems the alternative way [described earlier] is more robust, but this is a one-liner.

I've tried swapping the send and receive ports around, by the way, on the chance that I'm interpreting the documentation wrong. This has no useful effect but to, as you'd expect, simply swap them around, resulting in the same port being given the reply, which is not the desired behaviour. It gives my client port(s) headaches anyway, because the send port doesn't have a delegate set (the client NSConnection never adds it to a run loop), so it dies when it receives any message.

Moving on, from this point on my "Sample"ing has shown that the NSConnection accepts the message just as if it were from a [working] NSSocketPort, calling it's internal methods until it finally calls sendBeforeDate: on the server's port to the client's receive port - the one I created just a few moments ago when discovering the need for it.

Anyway, the "from" port passed in is the server port that dispatched the message to start with. That seems consistent, and indicates things are working properly, so far as I can tell. Anyway, this fresh port connects back to the client at this point, causing the client to spawn yet another instance of my custom subclass, and doing all it's SSL stuff as you'd expect.

Back to the server part - as always for these simple messages, the components array contains just one item, and NSData instance, which has the following data (in hex):

04cefe2f 0e010100 0000

The 7th byte of that (01h, above) matches the count/sequence byte of the request, in each message. That's about all I can decipher out of that message; i.e. that it's not just random gibberish. The last three bytes, all 00h in my case, are normally all 01h's in the case of the reply given to an NSSocketPort. That's followed by the actual reply itself, which as you can see is depressingly absent from my version.

Anyway, this goes through the whole process I've previously described for sending and receiving the message, winding up with the client NSConnection getting the appropriate message dispatched to it. This then results in "nil" being returned for the original call to "rootProxy". In my sample code (based on the SimpleThreads sample from Apple), this just loops a thousand times. The whole process is virtually identical, except that the existing ports are reused; no more are created from here on in.

So, as you can see, I've got quite a conceptual nightmare on my hands. I wish I knew why the server NSConnection is balking at the message I give it, whereas it's quite happy receiving exactly the same message from an NSSocketPort.

My only suspicion at present is that the server NSConnection receives the message from a port it does recognise, since it comes from the spawned server port, not the listening server port that was originally given to it. In this case it seems to be sending a generic empty reply back on the send port provided to it. But then the client NSConnection seems to be accepting that, even though it too receives it from a spawned port from what is it's actual listener port...

So I'm not sure if that theory is entirely consistent. It does make some degree of sense. However, I'm understandably hesitant to rewrite large sections of my code on the off chance that this is the problem. In particular, it's my experience that these disastrous problems are more often the result of two parameters being accidentally swapped around, or something similar, so I'm trying hard to look for such "newbie" mistakes. I've experimented long and hard, through trial and error, with pretty much any and all combinations of swappable parts, without any success. Perhaps it is a glaring design error after all.

From my long experience with this problem it seems pretty clear that I'm the first and only person to get even this far with a custom NSPort. Thus, I doubt there's anyone out there in userland who can help me with this, save to point out any glaring errors. Thus, I'm hoping [as always] that someone at Apple will smile down upon me. I don't really wish to drag anyone in by name, but I do understand that Mr. Doug Davidson is on this list, and has some experience with Distributed Objects. If you're there Mr. Davidson, I really would appreciate your help. <:)

Wade Tregaskis
-- Sed quis custodiet ipsos custodes?

P.S. This whole thing may be more appropriate as a DTS incident, given how very specific it's nature is, but I'm a poor student without any income, and $200US is just not going to fall out of a tree on me. Amazingly, some very kind people on the cocoa dev list have responded to my request to 'borrow' a DTS incident, so to speak, but I feel very bad using such an item at their expense. Thus, yet another "last hope post" to try and resolve the issue cheaply. :)
_______________________________________________
macnetworkprog mailing list | email@hidden
Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/macnetworkprog
Do not post admin requests to the list. They will be ignored.
  • Prev by Date: Re: OTOptionManagement
  • Next by Date: Open Transport autopush
  • Previous by thread: Re: OTOptionManagement
  • Next by thread: Re: Failure attempting to implement an NSPort subclass for Distributed Objects
  • Index(es):
    • Date
    • Thread