Websockets communication is established through an initial handshake.
That is, the client sends a GET request to the server in the following format
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
The server responds with a socket key which is computed with concatenating Sec-WebSocket-Key and 258EAFA5-E914-47DA-95CA-C5AB0DC85B11, taking the SHA1-hash of the result and return the base64 encoding of the hash.
The response will be in the following format containing the response key
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Only after the server respons with the accept key, client will be able to proceed with the communication, which are also transmitted in a encoded format called frames
Therefore, implementation of websockets can be quite heavy for certain embedded systems MCs like Arduino UNO
1) We will handle websocket with external library OkHttp. Add the following line to Gradle.scripts corresponding to app module
implementation 'com.squareup.okhttp3:okhttp:3.10.0'2) Permission to use internet is needed
<uses-permission android:name="android.permission.INTERNET"></uses-permission>2) OkHttp uses its own background thread to send and receive messages. So the user need not have to worry about the work allocations. Reference
1) We use a demo socket server which echos the sent messages
2) Socket connection is opened when start is pressed. Similarly, connection is stopped on pressing stop / when the app is paused
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
public class MainActivity extends AppCompatActivity {
private Button mStart;
private Button mStop;
private TextView mTextView;
private OkHttpClient client;
private WebSocket mWs;
private final class EchoWebSocketListener extends WebSocketListener{
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
webSocket.send("Hello!");
webSocket.send("Connection established!");
Log.i("Ws", "New ws created");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
// on receiving message
mTextView.setText(mTextView.getText().toString() + "\n" + text);
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
mTextView.setText(mTextView.getText().toString() + "\n\n" + reason);
Log.i("Ws", "ws closed");
}
}
float mTilt;
private SensorManager mSensorManager;
private Sensor mSensor;
private SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
mTilt = event.values[0];
// OkHttp executes send in the background thread
mWs.send(String.valueOf(mTilt));
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
client = new OkHttpClient();
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mTextView = (TextView) findViewById(R.id.output);
mTextView.setMovementMethod(new ScrollingMovementMethod()); // for scrolling
mStart = (Button) findViewById(R.id.start_btn);
mStart.setOnClickListener( new View.OnClickListener(){
@Override
public void onClick(View v) {
// create new connection
Request request = new Request.Builder().url("ws://echo.websocket.org").build();
EchoWebSocketListener listener = new EchoWebSocketListener();
mWs = client.newWebSocket(request,listener);
if(mSensor!=null)
mSensorManager.registerListener(mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
});
mStop = (Button) findViewById(R.id.stop_btn);
mStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mSensor!=null)
mSensorManager.unregisterListener(mSensorEventListener);
if(mWs != null){
mWs.close(1000,"Goodbye");
}
}
});
}
@Override
protected void onPause() {
// stop connections when paused
mStop.callOnClick();
super.onPause();
}
}