Re: Socket Blocking Question
Re: Socket Blocking Question
- Subject: Re: Socket Blocking Question
- From: "Carter R. Harrison" <email@hidden>
- Date: Wed, 20 Jan 2010 14:32:36 -0500
On Jan 20, 2010, at 1:40 PM, Steven Degutis wrote:
> Recently I had the same issue you were having, sort of. And I came up with a solution I really liked.
>
> When I was playing with Distributed Objects, I fell in love with the abstract simplicity. However, it blocks and that's bad. It's even worse when the server stops responding, because you could potentially have a 60 second timeout before the single method will return. It's a potential disaster.
>
> So, I wrote an elegant compromise. Code is still written inline, no callbacks or delegate messages needed. But, it requires Blocks (and thus 10.6) to work.
Steven - this is a really interesting approach. I can see how this basically achieves the same thing as all the callback methods, but does allow the code to be written somewhat inline. Unfortunately I need to at least support OS X 10.5 at this point. I definitely need to read-up on blocks b/c I can see how they can be used to work around some tricky design problems.
>
> Essentially, I wrote some code on top of AsyncSocket (which is a brilliant framework by the way) that allows me to wrap up ObjC messages as NSData, send it across the server, and unpack it on the other side. The other side then responds to the ObjC message as if it was called right inside the application. (All this is thanks to NSInvocation's ability to introspect an ObjC message, by the way).
>
> The problem came when I had to return values. As long as the return value was void, this worked like a charm. But once I wanted to return an array of strings or a number, I had to define a method in the sender's protocol to receive such information. This is akin to your "thousands of delegate messages" you would have to implement, as you stated.
>
> So, using Blocks and NSInvocation and AsyncSocket, I ended up writing code that allows me to write code like this:
>
>
> // protocol.h
>
> @protocol ServerProtocol
>
> - (NSNumber*) calculatePiAndKillTime:(NSNumber*)shouldKillTime;
>
> @end
>
>
> // client.m
>
> - (void) someMethod {
> id <ServerProtocol> server;
> NSNumber *sure = [NSNumber numberWithBool:YES];
> [[server calculatePiAndKillTime: sure]
> returnedValue:^(id value) {
> // this will be called later on at some point
> NSLog(@"pi = %@", value);
> }]
> }
>
>
> // server.m
>
> - (NSNumber*) calculatePiAndKillTime:(NSNumber*)shouldKillTime {
> if ([shouldKillTime boolValue])
> // synchronously watch some film
> [self goWatchTheNewStarTrekFilmFrom2009];
>
> return [NSNumber numberWithFloat: 3.14];
> }
>
>
>
> All methods sent to a destination's proxy are sent asynchronously. And, as you can see, the return value of the method -calculatePiAndKillTime: is not actually an NSNumber, but rather a proxy that waits for a response from the destination. When the destination responds to the source with a return value, the method -returnedValue: is called with the value.
>
> But that's only half of the coolness.
>
> The other half is that methods can simply return the value they want right inside the method, no hacks necessary or anything by the programmer. In this case, we just use this line of code: return [NSNumber numberWithFloat: 3.14]; and then the NSNumber object is packaged up and sent back to the source through the proxy, all automagically.
>
> The main downfall of this is that every argument and return value must be an ObjC type, no scalars or structs or anything else will work with this system. (Mike Ash explains pretty well on this blog why trying to support those things can lead to some unfixable trickiness, which I just wanted to avoid altogether.)
>
> If you can't support 10.6, then, this won't work. But hopefully you can soon ;)
>
> Good luck.
>
> -Steven
>
>
>
> On Wed, Jan 20, 2010 at 11:39 AM, Carter R. Harrison <email@hidden> wrote:
> I need some folks experienced with cocoa and socket programming to weigh in for me on some design problems I've been having. I'm designing an application that acts as a client in a client-server model. The client communicates with the server over the network by issuing a request and then receiving a response. Requests can only be issued one at a time, meaning that a request cannot be sent until a response from any outstanding request is first received. My application works in such a way that the it could request a handle to an object on the server and then use that handle in subsequent requests to retrieve additional information about the object. I see two ways of modeling the application - I've tried both and I'm not particularly happy with either.
>
> The first is to send a request, and then have the socket block until a response is received. This benefit to this model is that it is so much easier to write the higher level application code. The issue with this model is that over a slow network connection it can take a considerable amount of time for the response to come back from the server and while that is happening my CPU usage is through the roof b/c the thread is blocking.
>
> The second way is to send a request and then let the NSInputStream call a delegate method when the response data is available. The response data is then pushed up through my protocol stack and finally up to the higher level application code. The benefit to this method is that CPU usage is minimal due to the fact that I'm no longer blocking, but the downside is that the higher level application code is so much more difficult to write because I have to write about a thousand methods to act as a callback for each request in a series of requests.
>
> I've provided an example of how I see each working below. My first question is, is there other ways to design an application around this client-server model that I'm not thinking about? My 2nd question is, if there aren't other ways, how can I adapt either method that I have outlined to make it work a little bit better?
>
> As an example let's say the server knows about the following objects:
>
> 1. VendingMachine
> - An object that represents a vending machine.
> - A vending machine contains Soft Drink objects.
> 2. SoftDrink
> - Has the following properties: drink name, price, number of calories.
>
> If I use the blocking model, I could write my code like this. The code is simple to write but I'm forced to wait for the server to respond with information on pretty much every line of code. If the vending machine had enough soft drinks it could take a long time to iterate over each one and have the server respond with the drink's name of each drink.
>
> -(void)printDrinkNames
> {
> VendingMachine *machine = [server fetchVendingMachine];
> NSArray *softDrinks = [machine getSoftDrinks];
> for (int i = 0 ; i < softDrinks.count ; i++)
> {
> NSString *drinkName = [[softDrinks objectAtIndex:i] name];
> NSLog(@"Found a drink named %@", drinkName);
> }
> }
>
> Likewise if I do the non-blocking approach I would have to have a method that gets called for each step in the process (see below). This model drives me crazy b/c the higher level application code is long, has tons of methods, and is just difficult to read and maintain. The example I have provided is simple enough to get the point across, but in reality some of the processes I'm trying to drive are much more complex and require numerous callback methods to pull off.
>
> -(void)printDrinkNames
> {
> [server fetchVendingMachineWithCallBackObject:self selector:@selector(didFetchVendingMachine:)
> }
>
> -(void)didFetchVendingMachine:(VendingMachine *)machine
> {
> [machine fetchSoftDrinksWithCallBackObject:self selector:@selector(didFetchSoftDrinks:)];
> }
>
> -(void)didFetchSoftDrinks:(NSArray *)drinks
> {
> for (int i = 0 ; i < drinks.count ; i++)
> {
> SoftDrink *drink = [drinks objectAtIndex:i];
> [drink fetchNameWithCallBackObject:self selector:@selector(didFetchDrinkName:)]
> }
> }
>
> -(void)didFetchDrinkName:(NSString *)name
> {
> NSLog(@"Drink name is %@", name);
> }
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
>
>
>
> --
> Steven Degutis
> http://www.thoughtfultree.com/
> http://www.degutis.org/
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden