A Secure WebSocket client example that shows how to use the WebSocket library in SharkSSL-Lite
Dependencies: EthernetInterface SharkSSL-Lite mbed-rtos mbed
The example connects to an online echo service and requires that you connect a terminal via the ''mbed Serial Port'. Windows users must install the Windows serial port driver.
The following screenshot shows Putty connected to the mbed serial terminal and after entering a few lines of text.
WsEchoClient.cpp@0:3a00a9689a7e, 2016-04-06 (annotated)
- Committer:
- wini
- Date:
- Wed Apr 06 16:43:26 2016 +0000
- Revision:
- 0:3a00a9689a7e
First release
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
wini | 0:3a00a9689a7e | 1 | /** |
wini | 0:3a00a9689a7e | 2 | * ____ _________ __ _ |
wini | 0:3a00a9689a7e | 3 | * / __ \___ ____ _/ /_ __(_)___ ___ ___ / / ____ ____ _(_)____ |
wini | 0:3a00a9689a7e | 4 | * / /_/ / _ \/ __ `/ / / / / / __ `__ \/ _ \/ / / __ \/ __ `/ / ___/ |
wini | 0:3a00a9689a7e | 5 | * / _, _/ __/ /_/ / / / / / / / / / / / __/ /___/ /_/ / /_/ / / /__ |
wini | 0:3a00a9689a7e | 6 | * /_/ |_|\___/\__,_/_/ /_/ /_/_/ /_/ /_/\___/_____/\____/\__, /_/\___/ |
wini | 0:3a00a9689a7e | 7 | * /____/ |
wini | 0:3a00a9689a7e | 8 | * |
wini | 0:3a00a9689a7e | 9 | * SharkSSL Embedded SSL/TLS Stack |
wini | 0:3a00a9689a7e | 10 | **************************************************************************** |
wini | 0:3a00a9689a7e | 11 | * PROGRAM MODULE |
wini | 0:3a00a9689a7e | 12 | * |
wini | 0:3a00a9689a7e | 13 | * $Id: WsEchoClient.c 3616 2014-12-03 00:40:53Z wini $ |
wini | 0:3a00a9689a7e | 14 | * |
wini | 0:3a00a9689a7e | 15 | * COPYRIGHT: Real Time Logic LLC, 2016 |
wini | 0:3a00a9689a7e | 16 | * |
wini | 0:3a00a9689a7e | 17 | * This software is copyrighted by and is the sole property of Real |
wini | 0:3a00a9689a7e | 18 | * Time Logic LLC. All rights, title, ownership, or other interests in |
wini | 0:3a00a9689a7e | 19 | * the software remain the property of Real Time Logic LLC. This |
wini | 0:3a00a9689a7e | 20 | * software may only be used in accordance with the terms and |
wini | 0:3a00a9689a7e | 21 | * conditions stipulated in the corresponding license agreement under |
wini | 0:3a00a9689a7e | 22 | * which the software has been supplied. Any unauthorized use, |
wini | 0:3a00a9689a7e | 23 | * duplication, transmission, distribution, or disclosure of this |
wini | 0:3a00a9689a7e | 24 | * software is expressly forbidden. |
wini | 0:3a00a9689a7e | 25 | * |
wini | 0:3a00a9689a7e | 26 | * This Copyright notice may not be removed or modified without prior |
wini | 0:3a00a9689a7e | 27 | * written consent of Real Time Logic LLC. |
wini | 0:3a00a9689a7e | 28 | * |
wini | 0:3a00a9689a7e | 29 | * Real Time Logic LLC. reserves the right to modify this software |
wini | 0:3a00a9689a7e | 30 | * without notice. |
wini | 0:3a00a9689a7e | 31 | * |
wini | 0:3a00a9689a7e | 32 | * http://sharkssl.com |
wini | 0:3a00a9689a7e | 33 | **************************************************************************** |
wini | 0:3a00a9689a7e | 34 | |
wini | 0:3a00a9689a7e | 35 | |
wini | 0:3a00a9689a7e | 36 | Secure WebSocket Example |
wini | 0:3a00a9689a7e | 37 | |
wini | 0:3a00a9689a7e | 38 | WebSocket (WS) is a new standard enabling full duplex asynchronous |
wini | 0:3a00a9689a7e | 39 | communication between a web server and a client and vice versa. WS can |
wini | 0:3a00a9689a7e | 40 | be used as a base for M2M communication. See the following page for an |
wini | 0:3a00a9689a7e | 41 | introduction to the protocol: http://en.wikipedia.org/wiki/WebSocket |
wini | 0:3a00a9689a7e | 42 | |
wini | 0:3a00a9689a7e | 43 | The example is by default connecting to the WebSocket echo service at |
wini | 0:3a00a9689a7e | 44 | realtimelogic.info. Compiling the code with ECHO_EX makes the example |
wini | 0:3a00a9689a7e | 45 | connect to echo.websocket.org. |
wini | 0:3a00a9689a7e | 46 | |
wini | 0:3a00a9689a7e | 47 | The WebSocket service at realtimelogic.info can respond to both RSA |
wini | 0:3a00a9689a7e | 48 | and ECC clients, but the echo service echo.websocket.org will only |
wini | 0:3a00a9689a7e | 49 | respond with an RSA certificate. For this reason, connecting to |
wini | 0:3a00a9689a7e | 50 | echo.websocket.org will fail if you have compiled SharkSSL with the |
wini | 0:3a00a9689a7e | 51 | option to exclude RSA and to only include support for ECC. |
wini | 0:3a00a9689a7e | 52 | |
wini | 0:3a00a9689a7e | 53 | */ |
wini | 0:3a00a9689a7e | 54 | |
wini | 0:3a00a9689a7e | 55 | #include "WsClientLib.h" |
wini | 0:3a00a9689a7e | 56 | #include <mbed.h> |
wini | 0:3a00a9689a7e | 57 | #include <stdarg.h> |
wini | 0:3a00a9689a7e | 58 | |
wini | 0:3a00a9689a7e | 59 | /* |
wini | 0:3a00a9689a7e | 60 | The #WSHOST and #WSURI macros must point to your WebSocket server. |
wini | 0:3a00a9689a7e | 61 | */ |
wini | 0:3a00a9689a7e | 62 | #ifdef ECHO_EX |
wini | 0:3a00a9689a7e | 63 | #define WSHOST "echo.websocket.org" |
wini | 0:3a00a9689a7e | 64 | #define WSURI "/" |
wini | 0:3a00a9689a7e | 65 | #else |
wini | 0:3a00a9689a7e | 66 | #define WSHOST "realtimelogic.info" |
wini | 0:3a00a9689a7e | 67 | #define WSURI "/WS-ELIZA/" |
wini | 0:3a00a9689a7e | 68 | #endif |
wini | 0:3a00a9689a7e | 69 | |
wini | 0:3a00a9689a7e | 70 | |
wini | 0:3a00a9689a7e | 71 | /************************* Helper functions ******************************/ |
wini | 0:3a00a9689a7e | 72 | |
wini | 0:3a00a9689a7e | 73 | Serial pc(USBTX, USBRX); // tx, rx |
wini | 0:3a00a9689a7e | 74 | |
wini | 0:3a00a9689a7e | 75 | /* Example code and selib.c use function xprintf. |
wini | 0:3a00a9689a7e | 76 | */ |
wini | 0:3a00a9689a7e | 77 | void _xprintf(const char* fmt, ...) |
wini | 0:3a00a9689a7e | 78 | { |
wini | 0:3a00a9689a7e | 79 | va_list varg; |
wini | 0:3a00a9689a7e | 80 | va_start(varg, fmt); |
wini | 0:3a00a9689a7e | 81 | vprintf(fmt, varg); |
wini | 0:3a00a9689a7e | 82 | va_end(varg); |
wini | 0:3a00a9689a7e | 83 | } |
wini | 0:3a00a9689a7e | 84 | |
wini | 0:3a00a9689a7e | 85 | |
wini | 0:3a00a9689a7e | 86 | /************************************************************************** |
wini | 0:3a00a9689a7e | 87 | The following code is designed specifically for this example and |
wini | 0:3a00a9689a7e | 88 | enables non blocking read from the console. The example's main loop is |
wini | 0:3a00a9689a7e | 89 | single threaded and we most therefore use non blocking functions for |
wini | 0:3a00a9689a7e | 90 | reading from the console. |
wini | 0:3a00a9689a7e | 91 | ***************************************************************************/ |
wini | 0:3a00a9689a7e | 92 | |
wini | 0:3a00a9689a7e | 93 | |
wini | 0:3a00a9689a7e | 94 | /* Function pollkb requires non blocking keyboard I/O. |
wini | 0:3a00a9689a7e | 95 | */ |
wini | 0:3a00a9689a7e | 96 | |
wini | 0:3a00a9689a7e | 97 | /* Platform specific function for non blocking keyboard read. |
wini | 0:3a00a9689a7e | 98 | */ |
wini | 0:3a00a9689a7e | 99 | static int |
wini | 0:3a00a9689a7e | 100 | pollkb(void) |
wini | 0:3a00a9689a7e | 101 | { |
wini | 0:3a00a9689a7e | 102 | if(pc.readable()) |
wini | 0:3a00a9689a7e | 103 | { |
wini | 0:3a00a9689a7e | 104 | int c = pc.getc(); |
wini | 0:3a00a9689a7e | 105 | return c=='\r' ? '\n' : c; |
wini | 0:3a00a9689a7e | 106 | } |
wini | 0:3a00a9689a7e | 107 | return 0; |
wini | 0:3a00a9689a7e | 108 | } |
wini | 0:3a00a9689a7e | 109 | |
wini | 0:3a00a9689a7e | 110 | |
wini | 0:3a00a9689a7e | 111 | |
wini | 0:3a00a9689a7e | 112 | /************ End non blocking console functions ***********************/ |
wini | 0:3a00a9689a7e | 113 | |
wini | 0:3a00a9689a7e | 114 | |
wini | 0:3a00a9689a7e | 115 | |
wini | 0:3a00a9689a7e | 116 | /* |
wini | 0:3a00a9689a7e | 117 | The main function connects to a WS echo server, by using the generic |
wini | 0:3a00a9689a7e | 118 | WS functions defined above. |
wini | 0:3a00a9689a7e | 119 | |
wini | 0:3a00a9689a7e | 120 | The function connects to the server defined by the macro #WSHOST. See |
wini | 0:3a00a9689a7e | 121 | WsClientLib.h for details. |
wini | 0:3a00a9689a7e | 122 | */ |
wini | 0:3a00a9689a7e | 123 | void |
wini | 0:3a00a9689a7e | 124 | mainTask(SeCtx* ctx) |
wini | 0:3a00a9689a7e | 125 | { |
wini | 0:3a00a9689a7e | 126 | /* Info printed to the console when the program starts |
wini | 0:3a00a9689a7e | 127 | */ |
wini | 0:3a00a9689a7e | 128 | static const char info[] = { |
wini | 0:3a00a9689a7e | 129 | "SharkSSL Websocket client demo.\n" |
wini | 0:3a00a9689a7e | 130 | "Copyright (c) 2016 Real Time Logic. All rights reserved.\n" |
wini | 0:3a00a9689a7e | 131 | "Connecting to wss://" WSHOST |
wini | 0:3a00a9689a7e | 132 | }; |
wini | 0:3a00a9689a7e | 133 | |
wini | 0:3a00a9689a7e | 134 | SharkSsl sharkSsl; |
wini | 0:3a00a9689a7e | 135 | SharkSslCon* sharkSslCon; |
wini | 0:3a00a9689a7e | 136 | SOCKET sock; |
wini | 0:3a00a9689a7e | 137 | int rc,status; |
wini | 0:3a00a9689a7e | 138 | |
wini | 0:3a00a9689a7e | 139 | WscReadState wss={0}; |
wini | 0:3a00a9689a7e | 140 | |
wini | 0:3a00a9689a7e | 141 | xprintf(("%s",info)); |
wini | 0:3a00a9689a7e | 142 | xprintf(("y\n\n")); |
wini | 0:3a00a9689a7e | 143 | xprintf(("Connecting to " WSHOST "...\n")); |
wini | 0:3a00a9689a7e | 144 | /* Port 443 is the listen port for secure servers i.e. HTTPS */ |
wini | 0:3a00a9689a7e | 145 | status=se_connect(&sock, WSHOST, 443); |
wini | 0:3a00a9689a7e | 146 | if(status) |
wini | 0:3a00a9689a7e | 147 | { |
wini | 0:3a00a9689a7e | 148 | const char* msg; |
wini | 0:3a00a9689a7e | 149 | switch(status) |
wini | 0:3a00a9689a7e | 150 | { |
wini | 0:3a00a9689a7e | 151 | case -1: msg="Socket error!"; |
wini | 0:3a00a9689a7e | 152 | break; |
wini | 0:3a00a9689a7e | 153 | case -2: msg="Cannot resolve IP address for " WSHOST "."; |
wini | 0:3a00a9689a7e | 154 | break; |
wini | 0:3a00a9689a7e | 155 | default: msg="Cannot connect to " WSHOST "."; |
wini | 0:3a00a9689a7e | 156 | } |
wini | 0:3a00a9689a7e | 157 | xprintf(( |
wini | 0:3a00a9689a7e | 158 | "%s\n%s", |
wini | 0:3a00a9689a7e | 159 | msg, |
wini | 0:3a00a9689a7e | 160 | status == -1 ? "" : |
wini | 0:3a00a9689a7e | 161 | "Note: this example is not designed to connect via a HTTP proxy.\n")); |
wini | 0:3a00a9689a7e | 162 | return; |
wini | 0:3a00a9689a7e | 163 | } |
wini | 0:3a00a9689a7e | 164 | |
wini | 0:3a00a9689a7e | 165 | /* It is common to create one (or several) SharkSsl object(s) at |
wini | 0:3a00a9689a7e | 166 | system start and to keep these objects for the lifetime of the |
wini | 0:3a00a9689a7e | 167 | program/firmware. |
wini | 0:3a00a9689a7e | 168 | */ |
wini | 0:3a00a9689a7e | 169 | SharkSsl_constructor(&sharkSsl, |
wini | 0:3a00a9689a7e | 170 | SharkSsl_Client, /* Two options: client or server */ |
wini | 0:3a00a9689a7e | 171 | 0, /* Not using SSL cache */ |
wini | 0:3a00a9689a7e | 172 | 4000, /* initial inBuf size: Can grow */ |
wini | 0:3a00a9689a7e | 173 | 4000); /* outBuf size: Fixed */ |
wini | 0:3a00a9689a7e | 174 | |
wini | 0:3a00a9689a7e | 175 | /* It is very important to seed the SharkSSL RNG generator (Ref-seed) */ |
wini | 0:3a00a9689a7e | 176 | sharkssl_entropy(baGetUnixTime() ^ (U32)&sharkSsl); |
wini | 0:3a00a9689a7e | 177 | |
wini | 0:3a00a9689a7e | 178 | if( (sharkSslCon = SharkSsl_createCon(&sharkSsl)) == 0) |
wini | 0:3a00a9689a7e | 179 | xprintf(("Cannot create SharkSslCon object.\n")); |
wini | 0:3a00a9689a7e | 180 | else /* We are now connected to the server. */ |
wini | 0:3a00a9689a7e | 181 | { |
wini | 0:3a00a9689a7e | 182 | /* Keep seeding (Make it more secure: Ref-seed) */ |
wini | 0:3a00a9689a7e | 183 | sharkssl_entropy(baGetUnixTime() ^ (U32)&sharkSsl); |
wini | 0:3a00a9689a7e | 184 | /* Establish a WS connection */ |
wini | 0:3a00a9689a7e | 185 | if( ! wscProtocolHandshake(&wss, sharkSslCon, &sock,6000,WSHOST,WSURI,0)) |
wini | 0:3a00a9689a7e | 186 | { |
wini | 0:3a00a9689a7e | 187 | U8 sbuf[255]; |
wini | 0:3a00a9689a7e | 188 | int sbufIx=0; /* sbuf cursor */ |
wini | 0:3a00a9689a7e | 189 | U8* rbuf; /* Receive buffer is managed by SharkSSL */ |
wini | 0:3a00a9689a7e | 190 | int idleCounter=0; |
wini | 0:3a00a9689a7e | 191 | #ifdef ECHO_EX |
wini | 0:3a00a9689a7e | 192 | xprintf(("\n------\nConnected\nEnter data and press the ENTER key\n")); |
wini | 0:3a00a9689a7e | 193 | #endif |
wini | 0:3a00a9689a7e | 194 | while((rc = wscRead(&wss,sharkSslCon,&sock,&rbuf,50)) >= 0) |
wini | 0:3a00a9689a7e | 195 | { |
wini | 0:3a00a9689a7e | 196 | if(rc) /* incomming data from server */ |
wini | 0:3a00a9689a7e | 197 | { |
wini | 0:3a00a9689a7e | 198 | idleCounter=0; |
wini | 0:3a00a9689a7e | 199 | #ifdef ECHO_EX |
wini | 0:3a00a9689a7e | 200 | xprintf(("Received %d bytes from server:\n",wss.frameLen)); |
wini | 0:3a00a9689a7e | 201 | #endif |
wini | 0:3a00a9689a7e | 202 | do |
wini | 0:3a00a9689a7e | 203 | { |
wini | 0:3a00a9689a7e | 204 | int len=rc; |
wini | 0:3a00a9689a7e | 205 | while(len--) |
wini | 0:3a00a9689a7e | 206 | xprintf(("%c", *rbuf++)); |
wini | 0:3a00a9689a7e | 207 | if(wss.bytesRead == wss.frameLen) |
wini | 0:3a00a9689a7e | 208 | break; /* We are done receiving the current frame */ |
wini | 0:3a00a9689a7e | 209 | } while( (rc=wscRead(&wss,sharkSslCon,&sock,&rbuf,10000)) > 0 ); |
wini | 0:3a00a9689a7e | 210 | #ifdef ECHO_EX |
wini | 0:3a00a9689a7e | 211 | xprintf(("\nEnd WS frame.\n")); |
wini | 0:3a00a9689a7e | 212 | #endif |
wini | 0:3a00a9689a7e | 213 | if(rc <= 0) break; |
wini | 0:3a00a9689a7e | 214 | } |
wini | 0:3a00a9689a7e | 215 | else /* 50 ms timeout */ |
wini | 0:3a00a9689a7e | 216 | { |
wini | 0:3a00a9689a7e | 217 | int c; |
wini | 0:3a00a9689a7e | 218 | /* Check if we have console data i.e. if user |
wini | 0:3a00a9689a7e | 219 | * entered text into the console. */ |
wini | 0:3a00a9689a7e | 220 | while((c=pollkb())!=0) |
wini | 0:3a00a9689a7e | 221 | { |
wini | 0:3a00a9689a7e | 222 | xprintf(("%c",c)); |
wini | 0:3a00a9689a7e | 223 | sbuf[sbufIx++] = (U8)c; |
wini | 0:3a00a9689a7e | 224 | /* Flush on ENTER or if buffer is full */ |
wini | 0:3a00a9689a7e | 225 | if(c == '\n' || sbufIx == sizeof(sbuf)) |
wini | 0:3a00a9689a7e | 226 | { |
wini | 0:3a00a9689a7e | 227 | /* Send console data to server */ |
wini | 0:3a00a9689a7e | 228 | rc = wscSendBin(sharkSslCon,&sock,sbuf,sbufIx); |
wini | 0:3a00a9689a7e | 229 | sbufIx=0; |
wini | 0:3a00a9689a7e | 230 | idleCounter=0; |
wini | 0:3a00a9689a7e | 231 | if(c != '\n') |
wini | 0:3a00a9689a7e | 232 | xprintf(("\n")); |
wini | 0:3a00a9689a7e | 233 | break; |
wini | 0:3a00a9689a7e | 234 | } |
wini | 0:3a00a9689a7e | 235 | } |
wini | 0:3a00a9689a7e | 236 | if(rc < 0) break; |
wini | 0:3a00a9689a7e | 237 | } |
wini | 0:3a00a9689a7e | 238 | if(rc == 0) |
wini | 0:3a00a9689a7e | 239 | { |
wini | 0:3a00a9689a7e | 240 | if(++idleCounter == 100) /* 50ms * 100: 5 sec */ |
wini | 0:3a00a9689a7e | 241 | { |
wini | 0:3a00a9689a7e | 242 | static const U8 msg[]={"Are you still there?"}; |
wini | 0:3a00a9689a7e | 243 | idleCounter=0; |
wini | 0:3a00a9689a7e | 244 | /* There are no WS requirements for sending |
wini | 0:3a00a9689a7e | 245 | * pings. This is just an example. (Ref-Ping). Note, |
wini | 0:3a00a9689a7e | 246 | * ping payload data is not required. |
wini | 0:3a00a9689a7e | 247 | */ |
wini | 0:3a00a9689a7e | 248 | rc=wscSendCtrl( |
wini | 0:3a00a9689a7e | 249 | sharkSslCon,&sock,WSOP_Ping,msg,sizeof(msg)-1); |
wini | 0:3a00a9689a7e | 250 | if(rc < 0) break; |
wini | 0:3a00a9689a7e | 251 | } |
wini | 0:3a00a9689a7e | 252 | } |
wini | 0:3a00a9689a7e | 253 | } |
wini | 0:3a00a9689a7e | 254 | } |
wini | 0:3a00a9689a7e | 255 | /* Release resources used by sharkSslCon */ |
wini | 0:3a00a9689a7e | 256 | SharkSsl_terminateCon(&sharkSsl, sharkSslCon); |
wini | 0:3a00a9689a7e | 257 | } |
wini | 0:3a00a9689a7e | 258 | |
wini | 0:3a00a9689a7e | 259 | SharkSsl_destructor(&sharkSsl); |
wini | 0:3a00a9689a7e | 260 | se_close(&sock); |
wini | 0:3a00a9689a7e | 261 | /*! [inline doc] */ |
wini | 0:3a00a9689a7e | 262 | xprintf(("\nServer connection closed!")); |
wini | 0:3a00a9689a7e | 263 | } |
wini | 0:3a00a9689a7e | 264 |