1 #import <Foundation/Foundation.h>
2 #import "../AsyncSocket.h"
6 @interface EchoServer : NSObject
8 NSMutableArray *sockets;
12 -(void) acceptOnPortString:(NSString *)str;
13 -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
14 -(void) onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
15 -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
16 -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag;
17 -(void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
21 #pragma mark Implementation
23 @implementation EchoServer
27 This method sets up the accept socket, but does not actually start it.
28 Once started, the accept socket accepts incoming connections and creates new
29 instances of AsyncSocket to handle them.
30 Echo Server keeps the accept socket in index 0 of the sockets array and adds
31 incoming connections at indices 1 and up.
36 sockets = [[NSMutableArray alloc] initWithCapacity:2];
38 AsyncSocket *acceptor = [[AsyncSocket alloc] initWithDelegate:self];
39 [sockets addObject:acceptor];
46 This method will never get called, because you'll be using Ctrl-C to exit the
51 // Releasing a socket will close it if it is connected or listening.
58 This method actually starts the accept socket. It is the first thing called by
61 - (void) acceptOnPortString:(NSString *)str
63 // AsyncSocket requires a run-loop.
64 NSAssert ([[NSRunLoop currentRunLoop] currentMode] != nil, @"Run loop is not running");
66 UInt16 port = [str intValue];
67 AsyncSocket *acceptor = (AsyncSocket *)[sockets objectAtIndex:0];
70 if ([acceptor acceptOnPort:port error:&err])
71 NSLog (@"Waiting for connections on port %u.", port);
74 // If you get a generic CFSocket error, you probably tried to use a port
75 // number reserved by the operating system.
77 NSLog (@"Cannot accept connections on port %u. Error domain %@ code %d (%@). Exiting.", port, [err domain], [err code], [err localizedDescription]);
84 This will be called whenever AsyncSocket is about to disconnect. In Echo Server,
85 it does not do anything other than report what went wrong (this delegate method
86 is the only place to get that information), but in a more serious app, this is
87 a good place to do disaster-recovery by getting partially-read data. This is
88 not, however, a good place to do cleanup. The socket must still exist when this
91 I do not implement -onSocketDidDisconnect:. Normally, that is where you would
92 release the disconnected socket and perform housekeeping, but I am satisfied
93 to leave the disconnected socket instances alone until Echo Server quits.
95 -(void) onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
98 NSLog (@"Socket will disconnect. Error domain %@, code %d (%@).",
99 [err domain], [err code], [err localizedDescription]);
101 NSLog (@"Socket will disconnect. No error.");
106 This method is called when a connection is accepted and a new socket is created.
107 This is a good place to perform housekeeping and re-assignment -- assigning an
108 controller for the new socket, or retaining it. Here, I add it to the array of
109 sockets. However, the new socket has not yet connected and no information is
110 available about the remote socket, so this is not a good place to screen incoming
111 connections. Use onSocket:didConnectToHost:port: for that.
113 -(void) onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
115 NSLog (@"Socket %d accepting connection.", [sockets count]);
116 [sockets addObject:newSocket];
121 At this point, the new socket is ready to use. This is where you can screen the
122 remote socket or find its DNS name (the host parameter is just an IP address).
123 This is also where you should set up your initial read or write request, unless
124 you have a particular reason for delaying it.
126 -(void) onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
128 NSLog (@"Socket %d successfully accepted connection from %@ %u.", [sockets indexOfObject:sock], host, port);
129 NSData *newline = [@"\n" dataUsingEncoding:NSASCIIStringEncoding];
131 // In Echo Server, each packet consists of a line of text, delimited by "\n".
132 // This is not a technique you should use. I do not know what "\n" actually
133 // means in terms of bytes. It could be CR, LF, or CRLF.
135 // In your own networking protocols, you must be more explicit. AsyncSocket
136 // provides byte sequences for each line ending. These are CRData, LFData,
137 // and CRLFData. You should use one of those instead of "\n".
140 [sock readDataToData:newline withTimeout:-1 tag:[sockets indexOfObject:sock]];
145 This method is called whenever a packet is read. In Echo Server, a packet is
146 simply a line of text, and it is transmitted to the connected Echo clients.
147 Once you have dealt with the incoming packet, you should set up another read or
148 write request, or -- unless there are other requests queued up -- AsyncSocket
151 -(void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag
153 NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
156 NSString *trimStr = [str stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
158 NSLog (@"Socket %d sent text \"%@\".", tag, trimStr);
160 // Forward string to other sockets.
161 int i; for (i = 1; i < [sockets count]; ++i)
162 [(AsyncSocket *)[sockets objectAtIndex:i] writeData:data withTimeout:-1 tag:i];
164 // Read more from this socket.
165 NSData *newline = [@"\n" dataUsingEncoding:NSASCIIStringEncoding];
166 [sock readDataToData:newline withTimeout:-1 tag:tag];
171 This method is called when AsyncSocket has finished writing something. Echo
172 Server does not need to do anything after writing, but your own app might need
173 to wait for a command from the remote application, or begin writing the next
176 -(void) onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
178 NSLog (@"Wrote to socket %d.", tag);
186 int main (int argc, const char * argv[])
188 printf ("ECHO SERVER by Dustin Voss copyright 2003. Sample code for using AsyncSocket.");
189 printf ("\nSYNTAX: %s port", argv[0]);
190 printf ("\nAccepts multiple connections from ECHO clients, echoing any client to all\nclients.\n");
191 if (argc != 2) exit(1);
193 printf ("Press Ctrl-C to exit.\n");
196 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
197 EchoServer *es = [[EchoServer alloc] init];
198 NSString *portString = [NSString stringWithCString:argv[1]];
200 // Here, I use perform...afterDelay to put an action on the run-loop before
201 // it starts running. That action will actually start the accept socket, and
202 // AsyncSocket will then be able to create other activity on the run-loop.
203 // But main() will have no other opportunity to do so; the run-loop does not
204 // give me any way in, other than the AsyncSocket delegate methods.
206 // Note that I cannot call AsyncSocket's -acceptOnPort:error: outside of the
209 [es performSelector:@selector(acceptOnPortString:) withObject:portString afterDelay:1.0];
210 [[NSRunLoop currentRunLoop] run];
211 [EchoServer release];