0

I'm opening two NSStreams pairs (input/output) between a client app and a server app, and need to associate those two streams on the same server-side object. The two stream approach means that didAcceptConnectionWithInputStream will be called twice, once for each stream open command coming from the client. Both open commands will be performed sequentially, one immediately after the other. I tried using the fact that the two opens will be done in conjunction to pair them on the server side, but there is definitely a problem with race conditions and stream confusion if two different clients attempt to connect at the same time.

What would be perfect is if there were a key/value pair that could be sent when connecting to the service, and I would send a client-generated GUID to identify the client.

Identifying by IP address, although a possible solution because the clients will be on a local network (not having to traverse a NAT), seems complex to implement with NSNetService.

Rob Reuss
  • 1,400
  • 10
  • 20
  • Similar protocols, such as SIP, use a third connection to convey supervisory information. Your client could, for example, connect to the server via TCP and use this connection to exchange information with the server "I have just connected to you and the port # at your end is 1234" or "I want to connect, which UDP ports should I use?"- the server can then reply with the send/receive ports and start listening on those ports – Paulw11 Dec 12 '15 at 00:05
  • Thanks - good idea I had not thought of. Unfortunately, NSNetService abstracts away from the port number, so it is unavailable based on the incoming stream, and as a result I think the approach is problematic for the same reason I can't use IP address. And I'm hoping to solve this problem without using lower level programming. – Rob Reuss Dec 12 '15 at 01:06
  • If you can't use out of band then you need to use in-band signalling; the data at the start of the stream will need to identify the client somehow – Paulw11 Dec 12 '15 at 02:41
  • Unfortunately there is no data included with the didAcceptConnectionWithInputStream method, which is when the stream gets announced to the server, so it would mean waiting for the client to subsequently send data, while maintaining a pool of unidentified streams. It is kind of an elaborate approach but I'll probably go with it unless someone here comes up with something simpler. It amazes me that there is no (simple) way to get the IP address from the NSStream. – Rob Reuss Dec 12 '15 at 03:09
  • A stream is an abstract object, so there may not be an associated IP address. I would probably go with my first option; advertise a service for clients to connect to. Use this service to advise the clients of the (unique) name of a service to connect to for their streams and then advertise this new service. – Paulw11 Dec 12 '15 at 03:22
  • I ended up putting my incoming stream requests into a pool of requests, and made them active there, able to receive client-identifying information from the clients. I gave the object in the pool a UID property (received from the client). Each time the identifying information is received from the client, I iterate through the set looking matching streams based on the UID, and when I find them, I'm able to move forward with that matched pair knowing they belong to the same client. Works well. Thanks so much for your help. – Rob Reuss Dec 12 '15 at 06:53

1 Answers1

0

Paulw11 in the comments above helped me sort through the options. I ended up solving the problem in the following way:

I created a class PendingStream to act as a temporary holder for each incoming stream request (that is, the pair of input and output streams) and add that to a set that holds all PendingStream objects. PendingStream is setup as a delegate for streaming, so that it could receive incoming data from the clients on the input stream. Clients then send an identifier, and that identifier is also stored as a property of PendingStream. Each time an identifier arrives from a client, PendingStream calls back to the original object that created it (it's delegate) so that the set of PendingStreams can be iterated through and matching pairs of streams identified on the basis of the client identifier. When a match is found, the PendingStream object is removed from the set and the stream pairs re-assigned to where they are needed.

A timer-based approach is used to ensure that there are no PendingStream orphans (where only one of the two matching stream requests arrived).

It is a bit more convoluted and complex than I would like, but it works well.

Rob Reuss
  • 1,400
  • 10
  • 20