PocketSocket
Objective-C websocket library for building things that work in realtime on iOS and OS X.
Features
- Conforms fully to RFC6455 websocket protocol
- Support for websocket compression via the permessage-deflate extension
- Passes all ~519 Autobahn Client Tests & Server Tests with 100% compliance1
- Client & Server modes (see notes below)
- TLS/SSL support
- Asynchronous IO
- Standalone
PSWebSocketDriver
for easy “Bring your own” networking IO
1Some server tests are non-strict and drop connections earlier when receiving malformed WebSocket payloads.
Dependencies
- CFNetworking.framework
- Foundation.framework
- Security.framework
- libSystem.dylib
- libz.dylib
Installation
Installation is recommended via cocoapods. Add pod 'PocketSocket'
to your Podfile and run pod install
.
Major Components
PSWebSocketDriver
- Networkless driver to deal with the websocket protocol. It solely operates with parsing raw bytes into events and sending events as raw bytes.PSWebSocket
- Networking based socket aroundNSInputStream
andNSOutputStream
deals with ensuring a connection is maintained. Uses thePSWebSocketDriver
internally on the input and output.PSWebSocketServer
- Networking based socket server aroundCFSocket
. It creates one PSWebSocket instance per incoming request.
Using PSWebSocket as a client
The client supports both the ws
and secure wss
protocols. It will automatically negotiate the certificates for you from the certificate chain on the device it’s running. If you need custom SSL certificate support or pinning look at the webSocket:evaluateServerTrust:
in PSWebSocketDelegate
The client will always request the server turn on compression via the permessage-deflate extension. If the server accepts the request it will be enabled for the entire duration of the connection and used on all messages.
If the initial NSURLRequest
specifies a timeout greater than 0 the connection will timeout if it cannot open within that interval, otherwise it could wait forever depending on the system.
# import <PSWebSocket/PSWebSocket.h>
@interface AppDelegate() <PSWebSocketDelegate>
@property (nonatomic, strong) PSWebSocket *socket;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
// create the NSURLRequest that will be sent as the handshake
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"wss://example.com"]];
// create the socket and assign delegate
self.socket = [PSWebSocket clientSocketWithRequest:request];
self.socket.delegate = self;
// open socket
[self.socket open];
return YES;
}
#pragma mark - PSWebSocketDelegate
- (void)webSocketDidOpen:(PSWebSocket *)webSocket {
NSLog(@"The websocket handshake completed and is now open!");
[webSocket send:@"Hello world!"];
}
- (void)webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message {
NSLog(@"The websocket received a message: %@", message);
}
- (void)webSocket:(PSWebSocket *)webSocket didFailWithError:(NSError *)error {
NSLog(@"The websocket handshake/connection failed with an error: %@", error);
}
- (void)webSocket:(PSWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
NSLog(@"The websocket closed with code: %@, reason: %@, wasClean: %@", @(code), reason, (wasClean) ? @"YES" : @"NO");
}
@end
Using PSWebSocket via PSWebSocketServer
The server currently only supports the ws
protocol. The server binds to the host address and port specified and accepts incoming connections. It parses the first HTTP request in each connection and then asks the delegate whether or not to accept it and complete the websocket handshake. The server expects to remain the delegate of all PSWebSocket
instances it manages so be careful not to manage them yourself or detach them from the server.
# import <PSWebSocket/PSWebSocketServer.h>
@interface AppDelegate() <PSWebSocketServerDelegate>
@property (nonatomic, strong) PSWebSocketServer *server;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
_server = [PSWebSocketServer serverWithHost:nil port:9001];
_server.delegate = self;
[_server start];
}
#pragma mark - PSWebSocketServerDelegate
- (void)serverDidStart:(PSWebSocketServer *)server {
NSLog(@"Server did start…");
}
- (void)serverDidStop:(PSWebSocketServer *)server {
NSLog(@"Server did stop…");
}
- (BOOL)server:(PSWebSocketServer *)server acceptWebSocketWithRequest:(NSURLRequest *)request {
NSLog(@"Server should accept request: %@", request);
return YES;
}
- (void)server:(PSWebSocketServer *)server webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message {
NSLog(@"Server websocket did receive message: %@", message);
}
- (void)server:(PSWebSocketServer *)server webSocketDidOpen:(PSWebSocket *)webSocket {
NSLog(@"Server websocket did open");
}
- (void)server:(PSWebSocketServer *)server webSocket:(PSWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
NSLog(@"Server websocket did close with code: %@, reason: %@, wasClean: %@", @(code), reason, @(wasClean));
}
- (void)server:(PSWebSocketServer *)server webSocket:(PSWebSocket *)webSocket didFailWithError:(NSError *)error {
NSLog(@"Server websocket did fail with error: %@", error);
}
@end
Using PSWebSocketDriver
The driver is the core of PSWebSocket
. It deals with the handshake request/response lifecycle, packing messages into websocket frames to be sent over the wire and parsing websocket frames received over the wire.
It supports both client and server mode and has an identical API for each.
To create an instance of it you use either clientDriverWithRequest:
or serverDriverWithRequest:
in the client mode you are to pass in a NSURLRequest
that will be sent as a handshake request. In server mode you are to pass in the NSURLRequest
that was the handshake request.
Beyond that have a look at the PSWebSocketDriverDelegate
methods and the simple API for interacting with the driver.
Roadmap
- Examples, examples, examples!
Running Tests
- Install autobahntestsuite
sudo pip install autobahntestsuite
- Start autobahn test server
wstest -m fuzzingserver
- Run tests in Xcode
Why a new library?
Currently for Objective-C there is few options for websocket clients. SocketRocket, while probably the most notable, has a code base being entirely contained in a single file and proved difficult to build in new features such as permessage-deflate and connection timeouts.
PocketSocket firstly aims to provide a rich set of tools that are easy to dig into and modify when necessary. The decoupling of the network layer from the driver layer allows a lot of flexibility to incorporate the library with existing setups.
Secondly we intend on keeping PocketSocket at the top of it's game. As soon as any major websocket extensions come into play you can count on them being incorporated as soon as the drafts begin to stabalize.
Lastly we're set out to create the full picture from client to server all in a single, but decoupled toolkit for all use cases on iOS and OS X.
Authors
- Robert Payne (@robertjpayne)
Contributors
- Jens Alfke (@snej)
License
Copyright 2014-Present Zwopple Limited
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.