Using WebSockets to Communicate Between Two Devices

Default blog image of logo on dark grey

In this article, we will share how we used WebSockets to enable two devices to communicate with each other.

What is a WebSocket?

WebSockets are a type of client-server protocol primarily designed for use by Web browsers and Web servers but they can be used in any client-server scenario. WebSockets allow developers to push data between devices in real time. They are one of the many tools developers like us use to build apps that provide instant updates and communication to users.

WebSockets are a firm favourite of ours because they provide real-time digital in-app experiences that support great user experiences.

Enabling WebSocket Connectivity for iOS and Android

One of the projects we’ve been working on recently required two devices to communicate with each other. The first device would primarily send commands and the other would receive them.

This particular project needed communication between iOS and Android, so we needed a WebSocket library for both platforms. We usedSocketRocket for iOS and Java-WebSocket for Android. What follows is our how-to guide for setting up WebSockets on Android and iOS so that both devices can connect to and communicate with each other.

How to Set-up WebSockets for Android

Adding the library to your project is as simple as adding the following line to your build.gradle:

compile 'org.java-websocket:Java-WebSocket:1.3.0'

We can now create a basic server implementation by extending the WebSocketServer class

public class MySocketServer extends WebSocketServer {

 private WebSocket mSocket;

 public MySocketServer(InetSocketAddress address) {
 super(address);
 }

 @Override
 public void onOpen(WebSocket conn, ClientHandshake handshake) {
 mSocket = conn;
 }

 @Override
 public void onClose(WebSocket conn, int code, String reason, boolean remote) {

 }

 @Override
 public void onMessage(WebSocket conn, String message) {

 }

 @Override
 public void onError(WebSocket conn, Exception ex) {

 }
}

Receiving WebSocket Messages into the Server

There are many different ways that you could handle messages coming into the server, we’ll use the EventBus library for this example. Similar to broadcasts, this allows us to distribute the message through the app for anyone to act on. First add the library to your build.gradle:

compile 'de.greenrobot:eventbus:2.4.0'

Then create an event object:

public class SocketMessageEvent {
 private String mMessage;

 public SocketMessageEvent(String message) {
 mMessage = message;
 }

 public String getMessage() {
 return mMessage;
 }
}

Then update the onMessage method in MySocketServer:

@Override
public void onMessage(WebSocket conn, String message) {
 EventBus.getDefault().post(new SocketMessageEvent(message));
}

Now any object can listen for a message being received by the socket and process it by calling EventBus.getDefault().register(this); and implementing:

public void onEvent(SocketMessageEvent event) {

}

We also add a way for the server to send a message back to the client:

public void sendMessage(String message) {
 mSocket.send(message);
}

Initialise the Server

To initialise the server we extend the Application class and create an instance of our MySocketServer class:

public class MyApplication extends Application {
 private static final String TAG = "MyApplication";
 private static final int SERVER_PORT = 12345;

 private MySocketServer mServer;

 @Override
 public void onCreate() {
 super.onCreate();

 startServer();
 }

 private void startServer() {
 InetAddress inetAddress = getInetAddress();
 if (inetAddress == null) {
 Log.e(TAG, "Unable to lookup IP address");
 return;
 }

 mServer = new MySocketServer(new InetSocketAddress(inetAddress.getHostAddress(), SERVER_PORT));
 mServer.start();
 }

 private static InetAddress getInetAddress() {
 try {
 for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
 NetworkInterface networkInterface = (NetworkInterface) en.nextElement();

 for (Enumeration enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements();) {
 InetAddress inetAddress = (InetAddress) enumIpAddr.nextElement();

 if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
 return inetAddress;
 }
 }
 }
 } catch (SocketException e) {
 e.printStackTrace();
 Log.e(TAG, "Error getting the network interface information");
 }

 return null;
 }
}

How to Initiate a WebSocket for an iOS App

As mentioned earlier, we used SocketRocket for the iOS app. Unfortunately the latest tag is quite old so to use the master branch we add the following to our Podfile:

pod "SocketRocket", :git => 'https://github.com/square/SocketRocket.git'

We need to instantiate a SRWebSocket object and create a class that implements the SRWebSocketDelegate protocol. Both of these will be handled by WSDSocket. The header file for this class is:

@class WSDSocket;


@protocol WSDSocketDelegate 
@optional
- (void)socketDidOpen:(WSDSocket *)socket;
- (void)socket:(WSDSocket *)socket didReceiveMessage:(id)message;
- (void)socket:(WSDSocket *)socket didFailWithError:(NSError *)error;
- (void)socket:(WSDSocket *)socket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
@end


@interface WSDSocket : NSObject
@property (weak, nonatomic) id delegate;

- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port;
- (void)open;
- (void)close;
- (BOOL)isConnectionOpen;
- (BOOL)sendString:(NSString *)string;

@end

This gives us our own delegate for whoever is holding on to the socket (we’ll come back to that later) and also provides methods for interacting with the socket. In the implementation file, we’ll create a private property to hold the socket:

@interface WSDSocket () 
@property (strong, nonatomic) SRWebSocket *socket;
@end

Passing the Calls to the Socket

The implementation of the methods that we defined in the header is fairly straightforward, just passing the calls to the socket:

- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port
{
 self = [super init];
 if (self) {
 NSString *urlString = [NSString stringWithFormat:@"ws://%@:%ld", host, port];
 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
 _socket = [[SRWebSocket alloc] initWithURLRequest:request];
 _socket.delegate = self;
 }
 return self;
}


- (void)open
{
 if (self.socket.readyState == SR_OPEN) {
 return;
 }

 [self.socket open];
}


- (void)close
{
 if (self.socket.readyState != SR_OPEN) {
 return;
 }

 [self.socket close];
}


- (BOOL)sendString:(NSString *)string
{
 if (self.socket.readyState != SR_OPEN) {
 return NO;
 }

 [self.socket send:string];
 return YES;
}


- (BOOL)isConnectionOpen
{
 return self.socket.readyState == SR_OPEN;
}

App Delegate

We’ll use the AppDelegate to open the socket and hold on to the connection so add a property to the AppDelegate interface:

@interface AppDelegate ()
@property (strong, nonatomic, readwrite) WSDSocket *socket;
@end

Then add a method to create the socket:

- (void)openSocketWithIP:(NSString *)ipAddress port:(NSInteger)port delegate:(id)delegate
{
 self.socket = [[WSDSocket alloc] initWithHost:ipAddress port:port];
 self.socket.delegate = delegate;
 [self.socket open];
}

This also needs to go into the AppDelegate header file.

Working example

To show the code working, we’ll create a view controller to send messages to the server and show the response. In the viewDidLoad method, open the connection:

- (void)viewDidLoad
{
 [super viewDidLoad];

 AppDelegate *delegate = [UIApplication sharedApplication].delegate;
 [delegate openSocketWithIP:kIpAddress port:kPort delegate:self];
}

kIpAddress and kPort will need to be defined as the IP Address of the Android device and the port that it’s listening on (defined as SERVER_PORT in the code above). The interface for the view controller should have a textfield to enter the message, a button to send the message and a label to display any messages from the server. A simple target action for the button would be:

- (IBAction)sendMessageButtonWasTouched:(UIButton *)sender
{
 AppDelegate *delegate = [UIApplication sharedApplication].delegate;
 [delegate.socket sendString:self.textField.text];
}

The view controller should also implement the WSDSocketDelegate protocol so we’ll add:

- (void)socket:(WSDSocket *)socket didReceiveMessage:(NSString *)message
{
 self.responseLabel.text = message;
}

To make the server respond to our message, we add a couple of bits to the Application class. First, make the application listen for events by adding the following line to the onCreate method of MyApplication:

EventBus.getDefault().register(this);

Then implement the onEvent method to listen for the socket messages:

@SuppressWarnings("UnusedDeclaration")
public void onEvent(SocketMessageEvent event) {
 String message = event.getMessage();
 mServer.sendMessage("echo: " + message);
}

Now, when the iOS client sends a message to the Android server, it will respond by echoing the message back.

Final Words

Well, that should have WebSockets all set up and your devices connected. You can find our example WebSockets projects on GitHub for both Android & iOS.

Finally, to see our work in action, check out our portfolio. We are proud to work with incredible clients, building apps that get results! See what we’ve been working on and get inspired.


Looking for something else?

Search over 400 blog posts from our team

Want to hear more?

Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!