Re: Socket Blocking Question
Re: Socket Blocking Question
- Subject: Re: Socket Blocking Question
- From: Steven Degutis <email@hidden>
- Date: Wed, 20 Jan 2010 13:51:37 -0600
By the way, supporting 10.5 isn't necessarily a problem for using Blocks.
PLBlocks is a viable plugin for Xcode on 10.5 that allows you to use blocks.
I can attest that it is pretty stable and I would use it in production apps
if I had to use 10.5 and needed blocks for something like this.
-Steven
On Wed, Jan 20, 2010 at 1:32 PM, Carter R. Harrison
<email@hidden>wrote:
>
> 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/
>
>
>
--
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