Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Accept multiple connections; convert to ARC

Now requires Xcode 4.5 too.
  • Loading branch information...
commit f7a71455119a607e7f02df2cdc168a2279c9384c 1 parent da85666
@mxcl authored
Showing with 69 additions and 68 deletions.
  1. +6 −6 MBWebSocketServer.h
  2. +52 −58 MBWebSocketServer.m
  3. +11 −4 README.md
View
12 MBWebSocketServer.h
@@ -11,10 +11,8 @@
@interface MBWebSocketServer : NSObject {
- NSUInteger port;
- id<MBWebSocketServerDelegate> delegate;
AsyncSocket *socket;
- AsyncSocket *client;
+ NSMutableArray *clients;
}
- (id)initWithPort:(NSUInteger)port delegate:(id<MBWebSocketServerDelegate>)delegate;
@@ -22,13 +20,15 @@
- (void)send:(id)utf8StringOrData;
@property (nonatomic, readonly) NSUInteger port;
-@property (nonatomic, assign) id<MBWebSocketServerDelegate> delegate;
+@property (nonatomic, weak) id<MBWebSocketServerDelegate> delegate;
@property (nonatomic, readonly) BOOL connected;
+@property (nonatomic, readonly) NSUInteger clientCount;
@end
@protocol MBWebSocketServerDelegate
-- (void)webSocketServerDidConnect:(MBWebSocketServer *)webSocket;
-- (void)webSocketServerDidDisconnect:(MBWebSocketServer *)webSocket;
+// return a response for this client, NSData or NSString are valid
+- (id)webSocketServerDidAcceptConnection:(MBWebSocketServer *)webSocket;
+- (void)webSocketServerLastClientDisconnected:(MBWebSocketServer *)webSocket;
- (void)webSocketServer:(MBWebSocketServer *)webSocket didReceiveData:(NSData *)data;
@end
View
110 MBWebSocketServer.m
@@ -27,38 +27,37 @@ static unsigned long long ntohll(unsigned long long v) {
@implementation MBWebSocketServer
+@dynamic connected;
+@dynamic clientCount;
-- (id)initWithPort:(NSUInteger)aport delegate:(id<MBWebSocketServerDelegate>)adelegate {
- port = aport;
- delegate = adelegate;
+- (id)initWithPort:(NSUInteger)port delegate:(id<MBWebSocketServerDelegate>)delegate {
+ _port = port;
+ _delegate = delegate;
socket = [[AsyncSocket alloc] initWithDelegate:self];
+ clients = [NSMutableArray new];
NSError *error = nil; //TODO
- [socket acceptOnPort:port error:&error];
+ [socket acceptOnPort:_port error:&error];
return self;
}
-- (void)close {
- if (client) {
- [client release];
- client = nil;
- [delegate webSocketServerDidDisconnect:self];
- }
+- (BOOL)connected {
+ return clients.count > 0;
}
-- (BOOL)connected {
- return client != nil;
+- (NSUInteger)clientCount {
+ return clients.count;
}
-- (void)respondToHandshake:(NSData *)data {
- NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
+- (void)respondToHandshake:(NSData *)data client:(AsyncSocket *)client {
+ NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *strings = [string componentsSeparatedByString:@"\r\n"];
if (strings.count == 0 || ![strings[0] isEqualToString:@"GET / HTTP/1.1"]) {
NSLog(@"MBWebSocketServer invalid handshake from client");
- return [self close];
+ return [client disconnect];
}
NSString *response = [NSString stringWithFormat:
@@ -106,23 +105,21 @@ - (uint64_t)readFrame:(const unsigned char*)bytes block:(void (^)(char *data, NS
return n + 4 + N;
}
-- (void)readFrames:(NSData *)indata {
- @try {
- NSMutableData *data = [NSMutableData new];
- uint x = 0;
- while (x < indata.length) {
- x += [self readFrame:indata.bytes + x block:^(char *rawdata, NSUInteger length){
- [data appendBytes:rawdata length:length];
- }];
- }
- [delegate webSocketServer:self didReceiveData:data];
- }
- @catch (id e) {
- NSLog(@"%@", e);
+- (NSData *)readFrames:(NSData *)indata {
+ NSMutableData *data = [NSMutableData data];
+ uint x = 0;
+ while (x < indata.length) {
+ x += [self readFrame:indata.bytes + x block:^(char *rawdata, NSUInteger length){
+ [data appendBytes:rawdata length:length];
+ }];
}
+ return data;
}
-- (void)send:(NSData *)payload {
+- (void)send:(NSData *)payload client:(id)client {
+ if (!payload.length)
+ return;
+
if ([payload isKindOfClass:[NSString class]]) {
payload = [[(NSString *)payload dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
}
@@ -152,55 +149,52 @@ - (void)send:(NSData *)payload {
}
[data appendData:payload];
- [client writeData:data withTimeout:-1 tag:3];
+
+ NSArray *cc = [client isKindOfClass:[NSArray class]] ? client : client ? @[client] : clients;
+ for (AsyncSocket *client in cc)
+ [client writeData:data withTimeout:-1 tag:3];
+}
+
+- (void)send:(id)payload {
+ [self send:payload client:nil];
}
#pragma mark - ASyncSocketDelegate
-- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)aclient {
- client = [aclient retain];
+- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)client {
+ [clients addObject:client];
[client readDataWithTimeout:-1 tag:1];
}
-- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
+- (void)onSocket:(AsyncSocket *)client didReadData:(NSData *)data withTag:(long)tag
{
if (tag == 1) { // waiting for handshake
- [self respondToHandshake:data];
+ [self respondToHandshake:data client:client];
} else {
- [self readFrames:data];
+ [_delegate webSocketServer:self didReceiveData:[self readFrames:data]];
[client readDataWithTimeout:-1 tag:3];
}
}
-- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag {
- if (sock == client) {
- switch (tag) {
- case 2:
- [delegate webSocketServerDidConnect:self];
- case 3:
- [client readDataWithTimeout:-1 tag:3];
+- (void)onSocket:(AsyncSocket *)client didWriteDataWithTag:(long)tag {
+ switch (tag) {
+ case 2: {
+ id response = [_delegate webSocketServerDidAcceptConnection:self];
+ [self send:response client:client];
+ // fall through
}
+ case 3:
+ [client readDataWithTimeout:-1 tag:3];
}
}
-- (void)onSocketDidDisconnect:(AsyncSocket *)sock {
- if (sock == client)
- [self close];
+- (void)onSocketDidDisconnect:(AsyncSocket *)client {
+ [clients removeObjectIdenticalTo:client];
+ if (clients.count == 0)
+ [_delegate webSocketServerLastClientDisconnected:self];
}
-
-#pragma mark - Boilerplate
-
-- (void)dealloc {
- [socket release];
- [client release];
- [super dealloc];
-}
-
-@synthesize port;
-@synthesize delegate;
-@dynamic connected;
@end
@@ -253,7 +247,7 @@ - (id)sha1base64 {
}
out[-2] = map[(input[19] & 0x0F) << 2];
out[-1] = '=';
- return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
+ return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
@end
View
15 README.md
@@ -1,14 +1,21 @@
MBWebSocket
===========
-So far just a *server implementation*, (you cannot instantiate an instance that does not bind to a port). Also, the server can only manage a single connection at a time.
+A websocket *server implementation*, (you cannot instantiate an instance that
+does not bind to a port).
-So, seriously, you will probably have to do some work to this class to make it
-work how you need it too. But it’s nice and simple so just get straight in.
+NOTE! I have not tested it extensively and there are many old WS versions. So
+you may well have to hack it to make it work. But! There is not much code.
+And! I believe it is quite readable. I will help! Mail me!
-Tested against Chrome 10/2011. Probably will only work against that.
+Tested against recent Chrome, Safari and Firefox versions. Only tested on Mac.
If you want a client implementation, use Square’s SocketRocket.
+Requirements
+------------
+* ARC or Garbage Collection
+* Xcode 4.5
+
Example Usage
-------------
```objc
Please sign in to comment.
Something went wrong with that request. Please try again.