246 lines
8.6 KiB
ReStructuredText
246 lines
8.6 KiB
ReStructuredText
|
SocketRocket Objective-C WebSocket Client (beta)
|
|||
|
================================================
|
|||
|
A conforming WebSocket (`RFC 6455 <http://tools.ietf.org/html/rfc6455>`_)
|
|||
|
client library.
|
|||
|
|
|||
|
`Test results for SocketRocket here <http://square.github.io/SocketRocket/results/>`_.
|
|||
|
You can compare to what `modern browsers look like here
|
|||
|
<http://tavendo.com/autobahn/testsuite/report/clients/index.html>`_.
|
|||
|
|
|||
|
SocketRocket currently conforms to all ~300 of `Autobahn
|
|||
|
<http://autobahn.ws/testsuite/>`_'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/SRWebSocket.h>`` or ``"SRWebSocket.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 rudimentary 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
|
|||
|
|
|||
|
.. code-block:: objective-c
|
|||
|
|
|||
|
@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
|
|||
|
|
|||
|
.. code-block:: objective-c
|
|||
|
|
|||
|
@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 <https://pypi.python.org/pypi/virtualenv>`_.
|
|||
|
Now, in your terminal::
|
|||
|
|
|||
|
source .env/bin/activate
|
|||
|
pip install git+https://github.com/tornadoweb/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 <http://localhost:9000>`_ and
|
|||
|
start chatting.
|
|||
|
|
|||
|
|
|||
|
WebSocket Server Implementation Recommendations
|
|||
|
-----------------------------------------------
|
|||
|
SocketRocket has been used with the following libraries:
|
|||
|
|
|||
|
- `Tornado <https://github.com/facebook/tornado>`_
|
|||
|
- Go's `WebSocket package <https://godoc.org/golang.org/x/net/websocket>`_ or Gorilla's `version <http://www.gorillatoolkit.org/pkg/websocket>`_
|
|||
|
- `Autobahn <http://tavendo.com/autobahn/testsuite.html>`_ (using its fuzzing
|
|||
|
client)
|
|||
|
|
|||
|
The Tornado one is dirt simple and works like a charm. (`IPython notebook
|
|||
|
<http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html>`_ 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). However 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
|
|||
|
------------
|
|||
|
We’re glad you’re interested in SocketRocket, and we’d love to see where you take it. Please read our `contributing guidelines <https://github.com/square/SocketRocket/blob/master/Contributing.md>`_ prior to submitting a Pull Request.
|