jens@0: // jens@0: // TCPListener.m jens@0: // MYNetwork jens@0: // jens@0: // Created by Jens Alfke on 5/10/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: jens@0: #import "TCPEndpoint.h" jens@0: @class TCPConnection, IPAddress; jens@0: @protocol TCPListenerDelegate; jens@0: jens@0: jens@0: /** Generic TCP-based server that listens for incoming connections on a port. jens@2: jens@0: For each incoming connection, it creates an instance of (a subclass of) the generic TCP jens@0: client class TCPClient. The -connectionClass property lets you customize which subclass jens@0: to use. jens@2: jens@2: TCPListener supports SSL, Bonjour advertisements for the service, and automatic port renumbering jens@2: if there are conflicts. (The SSL related methods are inherited from TCPEndpoint.) jens@2: jens@2: You will almost always need to implement the TCPListenerDelegate protocol in your own jens@2: code, and set an instance as the listener's delegate property, in order to be informed jens@2: of important events such as incoming connections. */ jens@0: @interface TCPListener : TCPEndpoint jens@0: { jens@0: @private jens@0: uint16_t _port; jens@0: BOOL _pickAvailablePort; jens@0: BOOL _useIPv6; jens@0: CFSocketRef _ipv4socket; jens@0: CFSocketRef _ipv6socket; jens@0: jens@0: NSString *_bonjourServiceType, *_bonjourServiceName; jens@0: NSNetService *_netService; jens@0: NSDictionary *_bonjourTXTRecord; jens@0: BOOL _bonjourPublished; jens@0: NSInteger /*NSNetServicesError*/ _bonjourError; jens@0: jens@0: Class _connectionClass; jens@0: } jens@0: jens@0: /** Initializes a new TCPListener that will listen on the given port when opened. */ jens@0: - (id) initWithPort: (UInt16)port; jens@0: jens@0: /** The subclass of TCPConnection that will be instantiated. */ jens@0: @property Class connectionClass; jens@0: jens@2: /** Delegate object that will be called when interesting things happen to the listener -- jens@2: most importantly, when a new incoming connection is accepted. */ jens@0: @property (assign) id delegate; jens@0: jens@0: /** Should the server listen for IPv6 connections (on the same port number)? Defaults to NO. */ jens@0: @property BOOL useIPv6; jens@0: jens@0: /** The port number to listen on. jens@0: If the pickAvailablePort property is enabled, this value may be updated after the server opens jens@0: to reflect the actual port number being used. */ jens@0: @property uint16_t port; jens@0: jens@0: /** Should the server pick a higher port number if the desired port is already in use? jens@0: Defaults to NO. If enabled, the port number will be incremented until a free port is found. */ jens@0: @property BOOL pickAvailablePort; jens@0: jens@0: /** Opens the server. You must call this after configuring all desired properties (property jens@0: changes are ignored while the server is open.) */ jens@0: - (BOOL) open: (NSError **)error; jens@0: jens@2: /** Opens the server, without returning a specific error code. jens@2: (In case of error the delegate's -listener:failedToOpen: method will be called with the jens@2: error code, anyway.) */ jens@0: - (BOOL) open; jens@0: jens@0: /** Closes the server. */ jens@0: - (void) close; jens@0: jens@0: /** Is the server currently open? */ jens@0: @property (readonly) BOOL isOpen; jens@0: jens@0: jens@0: #pragma mark BONJOUR: jens@0: jens@0: /** The Bonjour service type to advertise. Defaults to nil; setting it implicitly enables Bonjour. jens@0: The value should look like e.g. "_http._tcp."; for details, see the NSNetService documentation. */ jens@0: @property (copy) NSString *bonjourServiceType; jens@0: jens@0: /** The Bonjour service name to advertise. Defaults to nil, meaning that a default name will be jens@0: automatically generated if Bonjour is enabled (by setting -bonjourServiceType). */ jens@0: @property (copy) NSString *bonjourServiceName; jens@0: jens@0: /** The dictionary form of the Bonjour TXT record: metadata about the service that can be browsed jens@0: by peers. Changes to this dictionary will be pushed in near-real-time to interested peers. */ jens@0: @property (copy) NSDictionary *bonjourTXTRecord; jens@0: jens@0: /** Is this service currently published/advertised via Bonjour? */ jens@0: @property (readonly) BOOL bonjourPublished; jens@0: jens@0: /** Current error status of Bonjour service advertising. See NSNetServicesError for error codes. */ jens@0: @property (readonly) NSInteger /*NSNetServicesError*/ bonjourError; jens@0: jens@22: /** The NSNetService being published. */ jens@22: @property (readonly) NSNetService* bonjourService; jens@22: jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: jens@2: /** The delegate messages sent by TCPListener. jens@2: All are optional except -listener:didAcceptConnection:. */ jens@0: @protocol TCPListenerDelegate jens@0: jens@2: /** Called after an incoming connection arrives and is opened; jens@2: the connection is now ready to send and receive data. jens@2: To control whether or not a connection should be accepted, implement the jens@2: -listener:shouldAcceptConnectionFrom: method. jens@2: To use a different class than TCPConnection, set the listener's -connectionClass property. jens@2: (This is the only required delegate method; the others are optional to implement.) */ jens@0: - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection; jens@0: jens@0: @optional jens@2: /** Called after the listener successfully opens. */ jens@0: - (void) listenerDidOpen: (TCPListener*)listener; jens@2: /** Called if the listener fails to open due to an error. */ jens@0: - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error; jens@2: /** Called after the listener closes. */ jens@0: - (void) listenerDidClose: (TCPListener*)listener; jens@2: /** Called when an incoming connection request arrives, but before the conncetion is opened; jens@2: return YES to accept the connection, NO to refuse it. jens@2: This method can only use criteria like the peer IP address, or the number of currently jens@2: open connections, to determine whether to accept. If you also want to check the jens@2: peer's SSL certificate, then return YES from this method, and use the TCPConnection jens@2: delegate method -connection:authorizeSSLPeer: to examine the certificate. */ jens@0: - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address; jens@0: @end