1 #import <Foundation/Foundation.h>
2 #import <CoreFoundation/CFStream.h>
3 #import "../AsyncSocket.h"
7 #pragma mark Declarations
9 @interface Echo : NSObject
13 NSMutableString *text;
19 -(void)readFromServer;
22 -(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
23 -(void)onSocketDidDisconnect:(AsyncSocket *)sock;
24 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
25 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)t;
31 #pragma mark Implementation
33 @implementation Echo : NSObject
37 This method creates the socket. Echo reuses this one socket throughout its life.
38 Echo also sets up the input. While a command-line app is waiting for input, it
39 is usually blocked; I make the input non-blocking so that the run-loop remains
47 NSLog (@"Creating socket.");
48 socket = [[AsyncSocket alloc] initWithDelegate:self];
50 // Create command buffer.
51 text = [[NSMutableString alloc] init];
53 // Set up stdin for non-blocking.
54 if (fcntl (STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
56 NSLog (@"Can't make STDIN non-blocking.");
65 I release allocated resources here, including the socket. The socket will close
66 any connection before releasing itself, but it will not need to. I explicitly
67 close any connections in the "quit" command handler.
78 Echo spends one second handling any run-loop activity (i.e. socket activity)
79 and then comes up for air to check if any new commands have been entered and,
80 if so, executing them. Wash, rinse, repeat.
82 Note the use of the shouldExitLoop flag to control when the run-loop ends and
83 the app quits. I could have just called exit(), but this allows the app to clean
84 up after itself properly. You should use a similar technique if you create a
85 thread for socket activity and processing.
90 while (!shouldExitLoop)
93 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
98 /* This method just abstracts the stop-run-loop operation. */
101 shouldExitLoop = YES;
106 This method simply abstracts the read-from-server operation. It is called
107 from -onSocket:didReadData:withTag: to set up another read operation. If it did
108 not set up another read operation, AsyncSocket would not do anything with any
109 further packets from Echo Server.
111 You should not use "\n" as a packet delimiter in your own code. I explain why
114 - (void)readFromServer
116 NSData *newline = [@"\n" dataUsingEncoding:NSASCIIStringEncoding];
117 [socket readDataToData:newline withTimeout:-1 tag:0];
122 This method queues up a message that will be sent immediately upon connecting
123 to Echo Server. Note that the message consists of two write requests. They will
124 be sent consecutively, though, and will appear as one packet to Echo Server,
125 because Echo Server looks for the "\n" to determine when the packet ends.
129 // Yes, you can call methods on NSString constants.
132 [@"Hello, Echo Server! " dataUsingEncoding:NSASCIIStringEncoding];
135 [@"I am a new client.\n" dataUsingEncoding:NSASCIIStringEncoding];
137 [socket writeData:message withTimeout:-1 tag:0];
138 [socket writeData:newline withTimeout:-1 tag:0];
143 I only runs the run-loop for a second at a time, because I need to receive and
144 act on input from the user (which won't result in run-loop activity).
147 - (void)readFromStdIn
150 while (read (STDIN_FILENO, &c, 1) == 1)
152 [text appendString: [NSString stringWithFormat:@"%c", c]];
153 if (c == '\n') [self doTextCommand];
159 This method sends text to Echo Server, or executes a command.
161 - (void)doTextCommand
163 NSArray *params = [text componentsSeparatedByString:@" "];
164 if ([text hasPrefix: @"quit"] || [text hasPrefix: @"exit"])
166 // I don't technically need to call -disconnect here. When the app quits,
167 // the socket will disconnect itself if needed. But it is easier to keep
168 // track of here. Note that -onSocket:willDisconnectWithError: will NOT
169 // be called here, though -onSocketDidDisconnect: will be.
171 // I stop the run loop like a gentleman instead of simply calling exit()
172 // so that the app exits cleanly. This is also technically unnecessary;
173 // the OS will destroy the socket connection when it cleans up after the
176 NSLog (@"Disconnecting & exiting.");
180 else if ([text hasPrefix: @"disconnect"])
182 NSLog (@"Disconnecting.");
185 else if ([text hasPrefix: @"connect"])
189 if ([params count] == 3)
191 port = [[params objectAtIndex:2] intValue];
192 host = [params objectAtIndex:1];
196 port = [[params objectAtIndex:1] intValue];
200 // This starts to establish a connection to the server.
201 // The connection will not be finished until later, when
202 // -onSocket:didConnectToPort:host: is called. But even so, you can
203 // immediately queue a read or write operation here. It will be
204 // performed when the connection is established.
206 // Note that I enclose the connect method call in a @try block. An
207 // exception will be thrown if the socket is already connected. Usually,
208 // if an exception was thrown, that indicates programmer error. In this
209 // case, an exception would indicate that I did not forsee a user trying
210 // to make a new connection when one already exists. I could and should
211 // have used -isConnected to check for that case in advance, but I wanted
212 // to demonstrate the exception-handling.
214 // Note also, that I call -prepareHello (which writes to the socket)
215 // before the socket is actually connected. The write request will be
216 // queued up and transmitted as soon as the connection is complete.
222 if ([socket connectToHost:host onPort:port error:&err])
224 NSLog (@"Connecting to %@ port %u.", host, port);
229 NSLog (@"Couldn't connect to %@ port %u (%@).", host, port, err);
232 @catch (NSException *exception)
234 NSLog ([exception reason]);
237 else if ([text hasPrefix: @"dump"])
239 // This demonstrates AsyncSocket's -description method.
240 NSLog (@"%@", socket);
242 else if ([text hasPrefix: @"help"])
248 // Anything other than a command is sent to Echo Server. Note that data
249 // will include the final press of the Return key, which is "\n". That
250 // is why I use "\n" verbatim as the packet delimiter, instead of
251 // specifying CRLF or something.
253 NSData *data = [text dataUsingEncoding:NSASCIIStringEncoding];
254 [socket writeData:data withTimeout:-1 tag:0];
256 [text setString:@""];
261 #pragma mark AsyncSocket Delegate Methods
265 This will be called whenever AsyncSocket is about to disconnect. In Echo Server,
266 it does not do anything other than report what went wrong (this delegate method
267 is the only place to get that information), but in a more serious app, this is
268 a good place to do disaster-recovery by getting partially-read data. This is
269 not, however, a good place to do cleanup. The socket must still exist when this
272 -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
275 NSLog (@"Socket will disconnect. Error domain %@, code %d (%@).",
276 [err domain], [err code], [err localizedDescription]);
278 NSLog (@"Socket will disconnect. No error.");
283 Normally, this is the place to release the socket and perform the appropriate
284 housekeeping and notification. But I intend to re-use this same socket for
285 other connections, so I do nothing.
287 -(void) onSocketDidDisconnect:(AsyncSocket *)sock
289 NSLog (@"Disconnected.");
294 This method is called when Echo has connected to Echo Server. I immediately
295 wait for incoming data from the server, but I already have two write requests
296 queued up (from -prepareHello), and will also be sending data when
297 the user gives me some to send.
299 -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
301 NSLog (@"Connected to %@ %u.", host, port);
302 [self readFromServer];
307 This method is called when Echo has finished reading a packet from Echo Server.
308 It prints it out and immediately calls -readFromServer, which will queue up a
309 read operation, waiting for the next packet.
311 You'll note that I do not implement -onSocket:didWriteDataWithTag:. That is
312 because Echo does not care about the data once it is transmitted. AsyncSocket
313 will still send the data, but will not notify Echo when that it done.
315 -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)t
317 NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
318 printf ([str cString]);
321 [self readFromServer];
328 #pragma mark C Functions
332 printf ("ECHO by Dustin Voss copyright 2003. Sample code for using AsyncSocket.");
333 printf ("\nReads and writes text to an ECHO SERVER. The following commands are available:");
334 printf ("\n\tquit, exit -- exit the program");
335 printf ("\n\thelp -- display this message");
336 printf ("\n\tconnect host port -- connects to the server on the given host and port");
337 printf ("\n\tdisconnect -- disconnects from the current server");
338 printf ("\n\tdump -- displays the socket's status");
339 printf ("\nAnything else gets transmitted to the server. Begin!\n");
343 int main (int argc, const char * argv[])
346 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
347 Echo *e = [[Echo alloc] init];