/SocketRocket

A conforming Objective-C WebSocket client library.

Primary LanguageObjective-COtherNOASSERTION

SocketRocket Objective-C WebSocket Client (beta)

A conforming WebSocket (RFC 6455) client library.

Test results for SocketRocket here. You can compare to what modern browsers look like here.

SocketRocket currently conforms to all ~300 of Autobahn's fuzzing tests (aside from two UTF-8 ones where it is merely non-strict. tests 6.4.2 and 6.4.4)

Features/Design

  • TLS (wss) support. It uses CFStream so we get this for free
  • Uses NSStream/CFNetworking. Earlier implementations used dispatch_io, however, this proved to be make TLS nearly impossible. Also I wanted this to work in iOS 4.x. (SocketRocket only supports 5.0 and above now)
  • Uses ARC. It uses the 4.0 compatible subset (no weak refs).
  • Seems to perform quite well
  • Parallel architecture. Most of the work is done in background worker queues.
  • Delegate-based. Had older versions that could use blocks too, but I felt it didn't blend well with retain cycles and just objective C in general.

Changes

v0.3.1-beta2 - 2013-01-12

  • Stability fix for closeWithCode:reason: (Thanks @michaelpetrov!)
  • Actually clean up the NSStreams and remove them from their runloops
  • _SRRunLoopThread's main wasn't correctly wrapped with @autoreleasepool

v0.3.1-beta1 - 2013-01-12

  • Cleaned up GCD so OS_OBJECT_USE_OBJC_RETAIN_RELEASE is optional
  • Removed deprecated dispatch_get_current_queue in favor of dispatch_queue_set_specific and dispatch_get_specific
  • Dropping support for iOS 4.0 (it may still work)

Installing (iOS)

There's a few options. Choose one, or just figure it out

  • You can copy all the files in the SocketRocket group into your app.

  • Include SocketRocket as a subproject and use libSocketRocket

    If you do this, you must add -ObjC to your "other linker flags" option

  • For OS X you will have to repackage make a .framework target. I will take contributions. Message me if you are interested.

Depending on how you configure your project you may need to #import either <SocketRocket/SRSocketRocket.h> or "SRSocketRocket.h"

Framework Dependencies

Your .app must be linked against the following frameworks/dylibs

  • libicucore.dylib
  • CFNetwork.framework
  • Security.framework
  • Foundation.framework

Installing (OS X)

SocketRocket now has (64-bit only) OS X support. SocketRocket.framework inside Xcode project is for OS X only. It should be identical in function aside from the unicode validation. ICU isn't shipped with OS X which is what the original implementation used for unicode validation. The workaround is much more rhudimentary and less robust.

  1. Add SocketRocket.xcodeproj as either a subproject of your app or in your workspace.
  2. Add SocketRocket.framework to the link libraries
  3. If you don't have a "copy files" step for Framework, create one
  4. Add SocketRocket.framework to the "copy files" step.

API

The classes

SRWebSocket

The Web Socket.

Note

SRWebSocket will retain itself between -(void)open and when it closes, errors, or fails. This is similar to how NSURLConnection behaves. (unlike NSURLConnection, SRWebSocket won't retain the delegate)

What you need to know

@interface SRWebSocket : NSObject

// Make it with this
- (id)initWithURLRequest:(NSURLRequest *)request;

// Set this before opening
@property (nonatomic, assign) id <SRWebSocketDelegate> delegate;

- (void)open;

// Close it with this
- (void)close;

// Send a UTF8 String or Data
- (void)send:(id)data;

@end

SRWebSocketDelegate

You implement this

@protocol SRWebSocketDelegate <NSObject>

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;

@optional

- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;

@end

Known Issues/Server Todo's

  • Needs auth delegates (like in NSURLConnection)
  • Move the streams off the main runloop (most of the work is backgrounded uses GCD, but I just haven't gotten around to moving it off the main loop since I converted it from dispatch_io)
  • Re-implement server. I removed an existing implementation as well because it wasn't being used and I wasn't super happy with the interface. Will revisit this.
  • Separate framer and client logic. This will make it nicer when having a server.

Testing

Included are setup scripts for the python testing environment. It comes packaged with vitualenv so all the dependencies are installed in userland.

To run the short test from the command line, run:

make test

To run all the tests, run:

make test_all

The short tests don't include the performance tests. (the test harness is actually the bottleneck, not SocketRocket).

The first time this is run, it may take a while to install the dependencies. It will be smooth sailing after that. After the test runs the makefile will open the results page in your browser. If nothing comes up, you failed. Working on making this interface a bit nicer.

To run from the app, choose the SocketRocket target and run the test action (cmd+u). It runs the same thing, but makes it easier to debug. There is some serious pre/post hooks in the Test action. You can edit it to customize behavior.

Note

Xcode only up to version 4.4 is currently supported for the test harness

TestChat Demo Application

SocketRocket includes a demo app, TestChat. It will "chat" with a listening websocket on port 9900.

It's a simple project. Uses storyboard. Storyboard is sweet.

TestChat Server

We've included a small server for the chat app. It has a simple function. It will take a message and broadcast it to all other connected clients.

We have to get some dependencies. We also want to reuse the virtualenv we made when we ran the tests. If you haven't run the tests yet, go into the SocketRocket root directory and type:

make test

This will set up your virtualenv. Now, in your terminal:

source .env/bin/activate
pip install git+https://github.com/facebook/tornado.git

In the same terminal session, start the chatroom server:

python TestChatServer/py/chatroom.py

There's also a Go implementation (with the latest weekly) where you can:

cd TestChatServer/go
go run chatroom.go

Chatting

Now, start TestChat.app (just run the target in the XCode project). If you had it started already you can hit the refresh button to reconnect. It should say "Connected!" on top.

To talk with the app, open up your browser to http://localhost:9000 and start chatting.

WebSocket Server Implementation Recommendations

SocketRocket has been used with the following libraries:

  • Tornado
  • Go's weekly build (the official release has an outdated protocol, so you may have to use weekly until Go 1 is released)
  • Autobahn (using its fuzzing client)

The Tornado one is dirt simple and works like a charm. (IPython notebook uses it too). It's much easier to configure handlers and routes than in Autobahn/twisted.

As far as Go's goes, it works in my limited testing. I much prefer go's concurrency model as well. Try it! You may like it. It could use some more control over things such as pings, etc., but I am sure it will come in time.

Autobahn is a great test suite. The Python server code is good, and conforms well (obviously). Hovever, for me, twisted would be a deal-breaker for writing something new. I find it a bit too complex and heavy for a simple service. If you are already using twisted though, Autobahn is probably for you.

Contributing

Any contributors to the master SocketRocket repository must sign the Individual Contributor License Agreement (CLA). It's a short form that covers our bases and makes sure you're eligible to contribute.

When you have a change you'd like to see in the master repository, send a pull request. Before we merge your request, we'll make sure you're in the list of people who have signed a CLA.