Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 23 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
23
Dung lượng
625,04 KB
Nội dung
CHAPTER 2: Mike Ash’s Deep Dive Into Peer-to-Peer Networking 49 continue; if(CFSwapInt32BigToHost(packet.header.datatype) != kSphereNetPositionPacketType) continue; This is all nice and simple, since we only take one datatype. If we took more than one, the length check would get a bit more complex, and we’d have to take different actions based on the datatype field. Since we’re accepting only one datatype, we can simply go back to the top of the loop if it isn’t that one. The next thing to do is to notify the delegate. We don’t want to notify the delegate directly, though, since we’re not on the main thread, and that could cause all kinds of trouble. Instead, we’ll bundle up the appropriate data and then bounce the call over to the main thread and call the delegate from there: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSData *packetData = [NSData dataWithBytes:&packet length:sizeof(packet)]; NSData *addressData = [NSData dataWithBytes:&addr length:socklen]; NSArray *arguments = [NSArray arrayWithObjects:packetData, addressData, nil]; SEL mainThreadSEL = @selector(mainThreadReceivedPositionPacket:); [self performSelectorOnMainThread:mainThreadSEL withObject:arguments waitUntilDone:YES]; [pool release]; } } The implementation of -mainThreadReceivedPositionPacket: is straightforward. Unpack the arguments, build a SphereNetSphereUpdate from the packet information and then notify the delegate: - (void)mainThreadReceivedPositionPacket:(NSArray *)arguments { // extract the objects from the array created above NSData *packetData = [arguments objectAtIndex:0]; NSData *addressData = [arguments objectAtIndex:1]; const PositionPacket *packet = [packetData bytes]; SphereNetSphereUpdate update; // and update the SphereNetSphereUpdate struct update.r = (float)packet->r / 255.0; update.g = (float)packet->g / 255.0; update.b = (float)packet->b / 255.0; CHAPTER 2: Mike Ash’s Deep Dive Into Peer-to-Peer Networking50 // accounting for differences in endianness int32_t x = CFSwapInt32BigToHost(packet->x); int32_t y = CFSwapInt32BigToHost(packet->y); update.position = CGPointMake(x, y); [_delegate networkController:self didReceiveUpdate:update fromAddress:addressData]; } That is everything in the network controller. Now all that remains is to integrate this control- ler into the view controller, and our work will be complete! Integrating Networking and the GUI There are three basic capabilities that need to be added to SphereNetViewController to get it up to speed with all of this nifty networking code we just wrote: N We need to have it actually create an instance of SphereNetNetworkController and notify that instance whenever the user moves the local sphere around. N We need to keep track of all the remote spheres and move them whenever an update comes in from the network controller. N We need to remove inactive spheres after they’ve been idle for a certain amount of time. To support these three tasks, we’ll be adding three instance variables to the class. SphereNetNetworkController *_netController; NSMutableDictionary *_remoteSpheres; NSTimer *_idleRemovalTimer; The network controller and idle timer will be set up in -viewDidLoad, just like _localSphere is. Back in SphereNetViewController.m, add the following to the end of -viewDidLoad: if(!_netController) { netController = [[SphereNetNetworkController alloc] initWithDelegate:self]; [_netController localSphereDidMove:_localSphere]; } if(!_idleRemovalTimer) CHAPTER 2: Mike Ash’s Deep Dive Into Peer-to-Peer Networking 51 { _idleRemovalTimer = [[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(idleRemoval) userInfo:nil repeats:YES] retain]; } Notifying the network controller when the local sphere moves takes one additional line in -moveLocalSphereFromTouch:, which now looks like this: if(touch) { [_localSphere setPosition:[touch locationInView:[self view]]]; [_netController localSphereDidMove:_localSphere]; } To respond to updates, we need to implement the SphereNetNetworkController delegate method. To make sure that we update the right sphere, the address of the sender will be used as the key to look up an existing sphere. If it doesn’t exist, we’ll make a new one and set it up in the list of remote spheres, and we’ll set up its CALayer too. If the remote spheres dictionary hasn’t been created yet, we’ll create that as well. Finally, we’ll set the color and position of the sphere. Here’s the code: - (void)networkController:(SphereNetNetworkController *)controller didReceiveUpdate:(SphereNetSphereUpdate)update fromAddress:(NSData *)address { SphereNetSphere *sphere = [_remoteSpheres objectForKey:address]; if(!sphere) { sphere = [[[SphereNetSphere alloc] init] autorelease]; if(!_remoteSpheres) _remoteSpheres = [[NSMutableDictionary alloc] init]; [_remoteSpheres setObject:sphere forKey:address]; [[[self view] layer] addSublayer:[sphere layer]]; } [sphere setColorR:update.r g:update.g b:update.b]; [sphere setPosition:update.position]; } For the final task, removal of idle spheres, the goal is to remove any spheres that haven’t been updated in the past 10 seconds. Since there’s no explicit disconnection command, this kind of time-out mechanism is the only way for us to detect that a remote copy of SphereNet has gone away. CHAPTER 2: Mike Ash’s Deep Dive Into Peer-to-Peer Networking52 I’ve left out an explicit disconnection command more for simplicity than anything else. It simply isn’t needed, though adding one might be nice so that disconnected spheres would disappear immediately. Note, however, that even with such a command, this idle removal would still be needed, for the simple reason that the disconnection command isn’t guar- anteed to arrive. When working with networking code, the fundamental unreliability of networks always has to be considered, and this is no exception. The idle removal method is nice and straightforward. We simply iterate through the remote spheres dictionary and check each sphere’s last update time, a property we cleverly defined at the beginning for exactly this purpose. If the last update time is more than 10 seconds in the past, we remove the sphere from the dictionary and remove its layer from the screen. There’s a bit of a dance involved because NSMutableDictionary really dislikes being mutated while it’s being iterated, so we store all the dead addresses in an array and remove them all after the fact. Here’s the code: - (void)idleRemoval { NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; NSMutableArray *toRemove = [NSMutableArray array]; for(NSData *address in _remoteSpheres) { SphereNetSphere *sphere = [_remoteSpheres objectForKey:address]; if(now - [sphere lastUpdate] > 10) { [toRemove addObject:address]; [[sphere layer] removeFromSuperlayer]; } } [_remoteSpheres removeObjectsForKeys:toRemove]; } This could potentially get inefficient if there are thousands of remote machines on the net- work, but in practice, the number of machines is unlikely to ever exceed more than a couple of dozen, so this sort of brute-force search works out just fine. That’s everything! All the pieces are in place now (take a look back at Figure 2-1 to see the completed game). You can start up SphereNet on a bunch of different computers, and every- thing should be synchronized. If you don’t have a bunch of different computers, running one copy on your iPhone and one copy in the simulator is enough to see the network code at work. If you’re looking to build up your experience with networking, the completed SphereNet example offers a good platform to experiment with more sophisticated code. Here are a few ideas for enhancements that you could make: CHAPTER 2: Mike Ash’s Deep Dive Into Peer-to-Peer Networking 53 N Expand the current message format to allow for different shapes and different sizes. N Add a disconnection message so that disconnected spheres can be removed immedi- ately instead of waiting for a time-out. N Make updates more efficient by sending only position, not color, data. Send the color separately, in a one-time introduction message. Don’t forget to make the protocol detect the introduction and resend it if it gets lost in transit! Summary The SphereNet example is now fully functional, and that brings this chapter to a close. First, we developed a basic local version of the program, with an eye toward networking. Next, we examined exactly what we wanted from the networking support and designed a network protocol to reach those goals. From there, we wrote a network controller that implemented that protocol. Finally, we plugged that network controller into the original local code to build the final networked product, and I offered some ideas for extending the application. The iPhone’s great strength is its always-available unlimited network connection. By inte- grating networking into your application, you can make your iPhone application better by giving your customers the ability to connect with each other from anywhere. [...]... threading concepts, I am going to apply them in a basic iPhone application that will enable the user to spawn off multiple threads as well as control their termination and access to data With this application, you will understand basic threading concepts and be able to decide when to use threading Most importantly you can use this information to create cool iPhone applications Knowing When to Thread In computer... function, I was left with no choice Fortunately, the iPhone SDK made threading possible without adding much complexity People sometimes ask, “Why should I thread; I only have one processor?” Right now, that is correct; today’s iPhone currently has only one CPU That will surely change over time as multiple CPUs and graphics processors are added, and iPhone applications are made available on other Apple... button3On; bool button4On; int int int int int total; // keeps track of the total count countThread1; //keeps track of the count for each thread countThread2; countThread3; countThread4; NSLock *myLock; //mutex used to create our Critical Section IBOutlet IBOutlet IBOutlet IBOutlet UILabel UILabel UILabel UILabel *thread1Label; //thread value labels *thread2Label; *thread3Label; *thread4Label; IBOutlet... *button3Title; *button4Title; *totalCount; //getter and setters *thread1Label; *thread2Label; *thread3Label; *thread4Label; *updatedByThread; CHAPTER 3: Doing Several Things at Once: Performance Enhancements with Threading -(IBAction)launchThread1:(id)sender; //methods each button will trigger //when clicked -(IBAction)launchThread2:(id)sender; -(IBAction)launchThread3:(id)sender; -(IBAction)launchThread4:(id)sender;... process gets its own address space and call stack to keep track of methods, variables, and events Your Application (Process) Thread 2 Application Address Space Time Thread 1 Thread 3 Thread 4 Thread 5 Figure 3-2 An iPhone application spawning off threads over the period of time it is running As an application is running and needs to spawn a thread, it passes to the thread an object for the thread to... are allowed to access this section of code, as shown in Figure 3 -4 CHAPTER 3: Doing Several Things at Once: Performance Enhancements with Threading Your Application (Process) Enter Critical Section Blocked Character 2 Thread Time Character 1 Thread gameScore = 100 ***Thread Safe*** Application Address Space Exit Critical Section Figure 3 -4 Two threads successfully accessing data at the same time using... will connect our outlets and actions and then write our threading code Building Our Application This simple iPhone application example demonstrates how to overcome one of the most common issues developers have with threading applications—how to share data between threads Let’s write the simple iPhone application shown in Figure 3-6, so that we can focus on the specifics of threading The application... shown in Figure 3-8 Figure 3-8 Save your project as Threading 4 This will create a project with two Interface Builder XIB files, named MainWindow.xib and RootViewController.xib, and the Threading-Info.plist where application-launching metadata is stored 5 Once we have created our project, we need to open Interface Builder to lay out our iPhone application Double-click the ThreadingViewController.xib... chapter will cover the main principles of threading We will develop a neat little iPhone application that demonstrates all these principles Additionally, we will consider common pitfalls associated with threading and how to avoid them Beginning to Write Threading Applications I am simply amazed that, when I am holding the iPhone, I am holding a preemptive, multitasking computer with Unix, a graphical... the iPhone As your application is running and a phone call comes in, the operating system steps in, puts your application to sleep, services the phone call, and after the phone call is complete, services your application again In a preemptive, multitasking environment, the operating system prioritizes processes and timeshares as they run, so no process starves by not being serviced Figure 3-1 The iPhone . found the learning curve steep on my first iPhone application. I also found the pro- cess of getting my first iPhone application to actually run on the iPhone very difficult. Having said that,. amazed at the power, features, and maturity of the iPhone SDK. A year after the iPhone SDK came out, developers had access to tools, the SDK, documentation, a simulator, and a software distribution. extending the application. The iPhone s great strength is its always-available unlimited network connection. By inte- grating networking into your application, you can make your iPhone application better