I have been studying the bonjour/NSStream sample code from lecture #17 of Stanford's CS193p course (iOS programming) on iTunes U from the winter of 2010. The example code is available here.
In a nut shell, the sample code creates a socket and binds to port 0 so that it will be given a free port. It then publishes a service with that port using NSNetService (bonjour). An NSNetServiceBrowser is also started when the app starts up. Available services are placed in a UITableView. When a cell is selected, the corresponding service is resolved, an NSOutputStream is created, and data can be sent.
This is a naive implementation because connections are rejected if a connection already exists. My question is, what is the proper way to handle multiple connections? Once multiple clients are connected to the server, how does the server distinguish between them? i.e. How can data be sent specifically to one client and not the others?
- (void) _acceptConnection:(int)fd
{
int junk;
// If we already have a connection, reject this new one. This is one of the
// big simplifying assumptions in this code. A real server should handle
// multiple simultaneous connections.
if ( self.isReceiving ) {
junk = close(fd);
assert(junk == 0);
} else {
[self _startReceive:fd];
}
}
// Called by CFSocket when someone connects to our listening socket.
// This implementation just bounces the request up to Objective-C.
static void AcceptCallback(CFSocketRef s,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
ReceiveServer * obj;
assert(type == kCFSocketAcceptCallBack);
assert(data != NULL);
obj = (ReceiveServer *) info;
assert(obj != nil);
assert(s == obj->_listeningSocket);
[obj _acceptConnection:*(int *)data];
}
I'm not specifically familiar with that course or sample code, but: separate out the code for handling a connection into a different class from the code which accepts new connections. So, in the posted code, you'd move the -startReceive: method and everything which it calls to another class.
Then, each time a connection is accepted, create an instance of that other class. That instance will be responsible for processing all of the communication on that connection. It will be given the information about the connection (mainly the fd) during initialization. The main controller object of the server could hold those instances in an array.
Where things go from here will depend on what your server actually does. At the very least, your connection objects will need to inform the main controller object when they are closed, so the main controller can remove them from the array. You can use notifications or the delegate pattern for that.
Related
I'm trying to figure out how to make certain callbacks trigger.
On the peripheral peripheralManager:central:didSubscribeToCharacteristic: is called correctly and it sends a chunk (first of two) of data to the central which receives it in peripheral:didUpdateValueForCharacteristic:error: as expected.
Now there's one chunk left which is supposed to be sent in the peripheral's callback peripheralManagerIsReadyToUpdateSubscribers: according to Apple's test application.
I've tested and verified and it works fine there. It's a bit fishy though as according to the docs it's only supposed to be called when the peripheral manager's updateValue:forCharacteristic:onSubscribedCentrals: fails.
How do I make the peripheral send the remaining chunk? I can supply you with code, but it's almost identical (I'm using an array of NSData chunks instead of one large NSData like the example) to the example application I linked to, I'm more curious as to how the callback chain works and what needs to be in place for the different selectors to trigger.
What you are doing is the normal way of operation. The peripheral manager handles the data sending and implements flow control according to the current settings. E.g. if you are using indications instead of notifications, then each update has to be acknowledged by the receiver before you can send again.
Notifications on the other hand are similar to UDP packets. They can get lost. To make sure that the data arrived error free, you need to implement additional control flow management.
All in all, you are doing it right.
I managed to trigger peripheralManagerIsReadyToUpdateSubscribers: by using a loop in sendData (which is called from peripheralManagerIsReadyToUpdateSubscribers: and peripheralManager:central:didSubscribeToCharacteristic:).
- (void)sendData {
BOOL success = YES;
while (success && ([_outgoingDataQueue count] > 0)) {
NSData *chunk = [_outgoingDataQueue peek];
success = [self.peripheralManager updateValue:chunk
forCharacteristic:self.characteristic
onSubscribedCentrals:nil];
if (success) {
[_outgoingDataQueue dequeue];
}
}
}
This does not feel like the correct way to send data as chunks to the central.
I have a class method which requires an underlying CFSocket, and this method gets called very often so it's too expensive to create and destroy a new socket each time the method runs. So instead I've created a static instance of a CFSocketRef so I can share a single socket between method calls:
+(void)myStaticMethod {
static CFSocketRef udpSocket;
#synchronized(self) {
if (!udpSocket) {
udpSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, NULL, NULL);
int yes = 1;
setsockopt(CFSocketGetNative(udpSocket), SOL_SOCKET, SO_NOSIGPIPE, (void *)&yes, sizeof(yes));
}
}
//method body
}
I have two questions:
Do I have to worry about destroying (invalidating) the socket when the app will terminate, or does it close itself?
Which events might cause the socket to close itself and how would you prevent potentially writing to a bad CFSocketRef?
If an app really terminates then all resources, including sockets, are released. But normally apps don't terminate but go into the background and in that case the socket can become invalid. This is described quite well in Technical Note TN2277. See in particular the section "Data Socket".
Since the creation of an UDP socket is a simple operation, the following advice from TN2277 would apply to your code:
If reopening your data socket is simple, cheap, and has no user
visible consequences, the best approach is to close the socket when
the app goes into the background, and reopen it when it comes back
into the foreground. This makes your life very simple; in fact, if you
do this, you can ignore the rest of this section entirely!
I'm using Objective-C Distributed Objects (DO) to share data from one application (that collects data from the network) to another (a patch inside Quartz Composer). When the connection to the distant object fails (when I shut down the first application), I'm get:
5/16/12 8:17:06.373 PM Quartz Composer: *** EXCEPTION IGNORED: connection went invalid while waiting for a reply because a mach port died
After that point, the Quartz composition is hung. Even after I bring the first application back up, it's still hung. I want the Quartz patch to reconnect.
I am using the Notification Center to put shut down the old objects, like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(connectionDidDie)
name:NSConnectionDidDieNotification
object:theConnection];
Right now, my counnectionDidDie looks like this:
- (void) connectionDidDie
{
NSLog(#"Connection died and we detected it");
[[self proxyObject] release];
[self setProxyObject:nil];
theConnection = nil;
}
I also check to be sure the connection is still alive, just before accessing any part of the proxyObject, like this:
if ([NSConnection defaultConnection]) { // this line triggers the exception
// access proxyObject
}
I've also tried
if ([theConnection isValid]) { // this line triggers the exception
// access proxyObject
}
In both cases, it's this test that triggers this EXCEPTION.
What can I do to prevent the hang of Quartz when I shutdown the first application, who has the vended object?
I was never able to find a way to shut down the DO connection fast enough to prevent QC, who draws as 30-60 frames per second, front testing the connection (and crashing) before connectionDidDie: was called. Ultimately, I've decided to use DO, only to get an initial copy of the object, which then do a deep copy of, and then kill the DO connection while nothing is trying to access it. DO just doesn't seem to be that great of solution, after you dig into it. :(
I am currently developing an iPad application. I want this to communicate with a Windows c# application using WCF, and discovery over BonJour.
I have already made the windows/c# part, using Mono.ZeroConf library, and tested that it works as supposed on Windows.
Now when I try to get my MonoTouch application discover the Windows application, all goes fine until I need to resolve the address. I simply can't figure out what is wrong.
I have the following code in my iPad app.
NSNetServiceBrowser browser = new NSNetServiceBrowser();
browser.FoundService += delegate(object sender, NSNetServiceEventArgs e){
e.Service.AddressResolved += delegate(object service, EventArgs e1){
//No matter how I treat the Bytes here it resolves to me as 8.135.207.208
//When it was supposed to resolve to some 172.x.x.x ip.
long address = ((NSNetService)service).Addresses[0].Bytes.ToInt64();
};
e.Service.Resolve();
};
browser.SearchForServices("_myService._tcp", "local");
So my question is, how am I supposed to treat the NSData objects in the Addresses array to resolve them correct.
I have tried searching, but can't seem to figure out.
All the other properties of the resolved service is correct, like name, port, etc.
I hope some of you have been here, and can provide me an answer.
Best regards
/Anders
The data in the Addresses you receive is NSData in an NSArray. The description for the delegate itself says it is a collection of sockaddr objects (plain C struct). So definitely not a Int64 as is, but a pointer to sockaddr object.
The way it is done in MT is a bit different then. You received the delegate to the NSNetService, and now you have to wait for the service to resolve the address, this is done by another resolving.
I usually store the NSNetService returned by the NSNetServiceBrowser delegate and then wait until the address is resolved, once this is done, it is then when I use the service as it is already initialized and I do not care about the address itself.
I'm a fairly novice obj-c developer and have a question on how to set up a client-server relationship. I'm designing (mostly as a hobby) a board game to be played over the internet with friends and family (think monopoly). My problem: how do I set up the appropriate client-server relationship to have one server with multiple clients?
My thinking was to have one server contain all the information on the state of the game as well as send appropriate messages to a variety of objects through Cocoa's excellent distributed objects framework. However, I can't figure out how to have one server accept multiple clients.
firstConnection = [NSConnection defaultConnection];
[firstConnection setRootObject: firstPlayer];
[[NSRunLoop currentRunLoop] run];
But then what? Is there a way to tell the run loop to stop when a client is attached? I'd like to avoid multiple threading if possible as that would be a whole new complication to learn and this project is already challenging enough!
Any help would be greatly appreciated and I'd be happy to clarify anything at all if necessary.
Thanks in advance.
Basically the strategy to take is to have the server register itself as the root object. When the client connects to the server, it sends the server a connection message (defined by the server's protocol you create) that allows the server to register that client in order to send messages to it in the future. This could be as simple as adding the client to an array; no special run loops or threads should be needed.
Here's a quick example to communicate across processes, from a test app I wrote back when I was learning DO for the first time. Once the setup is done you can add code to make the server send messages to one or more objects in the _clients array based on any event you'd like, including setting up a timer for a rough game loop.
Server:
- (void)registerClient:(byref Client *)client;
{
[_clients addObject:client];
}
- (void)awakeFromNib;
{
_clients = [[NSMutableArray alloc] init];
[[NSConnection defaultConnection] setRootObject:self];
if ( [[NSConnection defaultConnection] registerName:#"server"] == NO )
{
// error code!
}
}
Client:
- (void)awakeFromNib;
{
id theProxy;
theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:#"server" host:nil] retain];
[theProxy setProtocolForProxy:#protocol(ServerP)];
if ( theProxy == nil )
// error code!
[theProxy registerClient:self];
}
Keep in mind that there are a lot of "gotchas" in distributed objects! Start simple, even if it means developing a rough prototype of your game idea first.
Cocoa's excellent distributed objects framework
That's the first time I've seen those words together like that ;)