Reputation: 115
I am trying to create a simple two protocol client and server for a demo. But when I specify two sub-protocols in the client initialization of the websocket I always get this message on the server side:
[1363089953:1535] ERR: Req protocol SIGN_IN_OUT_REQUEST_PROTOCOL, GET_REQUEST_PROTOCOL not supported
Client Javascript Code:
var host = 'ws://10.0.96.32:9000';
var webSocket = {};
var websocketStatus = {};
$(function() {
function webSocketSupported() {
if(window.MozWebSocket) {
window.WebSocket = window.MozWebSocket;
}
return (window.webSocket != null);
}
if(!webSocketSupported) {
$(document).simpledialog2({
mode: 'blank',
headerClose: true,
blankContent :
"<p>Websockets not supported by this browser. How about <a href='http://www.google.com/chrome'>Google Chrome</a>?</p>"+
"<a rel='close' data-role='button' href='#'>Close</a>"
});
} else {
$("#login_form").validate({
rules:{
username: {required: true, minlength: 2},
password: {required: true, minlength: 4}
},
messages:{
username: {required: "Username is required",
minlength: "Username must be atleast 2 characters"},
password: {required: "Password is required",
minlength: "Password must be atleast 4 characters"}
}
});
/** Setting up WebSocket **/
webSocket = new WebSocket(host, 'SIGN_IN_OUT_REQUEST_PROTOCOL', 'GET_REQUEST_PROTOCOL');
webSocket.binaryType = "arraybuffer";
webSocket.onopen = onopen;
webSocket.onmessage = onmessage;
webSocket.onclose = onclose;
webSocket.onerror = onerror;
}
});
function onopen() {
$('#connectionStatus').text("Connected");
$('#connectionStatus').buttonMarkup({theme: 'b'});
$("#connectionStatus").data('icon', 'check');
$("#connectionStatus .ui-icon").addClass("ui-icon-check").removeClass("ui-icon-alert");
$('#login_info').append("Sub-protocol: "+webSocket.protocol+"\n");
}
function onmessage(event) {
var data = new DataView(event.data);
if(data.getUint8(data.byteLength - 1) == 1) {
$('#login_info').append("Login Attempt Successful");
$.mobile.loadPage("mobileApp.html");
$.mobile.changePage("mobileApp.html")
}
else {
$('#login_info').append("Login Attempt Unsuccessful");
$('#username').val("");
$('#password').val("");
}
}
function onclose() {
$('#connectionStatus').text("Connection Closed");
$('#connectionStatus').buttonMarkup({theme: 'a'});
$("#connectionStatus").data('icon', 'alert');
$("#connectionStatus .ui-icon").addClass("ui-icon-alert").removeClass("ui-icon-alert");
}
function onerror() {
$('#connectionStatus').text("Connection Error");
$('#connectionStatus').buttonMarkup({theme: 'a'});
$("#connectionStatus").data('icon', 'alert');
$("#connectionStatus .ui-icon").addClass("ui-icon-alert").removeClass("ui-icon-alert");
}
function initialize() {
$('#login_info').val("");
$('#username').val("");
$('#password').val("");
$('#connectionStatus').text("Connection Closed");
$('#connectionStatus').buttonMarkup({theme: 'a'});
$("#connectionStatus").data('icon', 'alert');
$("#connectionStatus .ui-icon").addClass("ui-icon-alert").removeClass("ui-icon-alert");
}
function sendLoginRequest(event) {
var i = 0;
event.preventDefault();
if($("#login_form").valid()) {
var username = $("#username").val();
var password = $("#password").val();
var loginRequest = new Uint8Array(calculateBufferLength());
loginRequest[i++] = 0x13; //User Accounts IP
loginRequest[i++] = 0x13; //Sign In/Sign Out Request
loginRequest[i++] = 0x07; //Sign In
loginRequest[i++] = username.length;
loginRequest[i++] = password.length;
//Pushing username
for(var j=0; j < username.length; j++) {
loginRequest[i++] = username.charCodeAt(j);
}
//Pushing password
for(var j=0; j < password.length; j++) {
loginRequest[i++] = password.charCodeAt(j);
}
webSocket.send(loginRequest.buffer);
$("#login_info").append("Login request sent with\nUsername: "+username+"\nPassword: "+password+"\n");
}
}
function calculateBufferLength() {
var opIPLength = 1;
var modeLength = 1;
var subModeLength = 1;
var UserNamePasswordlengths = 2;
var userNameLength = $("#username").val().length;
var passwordLength = $("#password").val().length;
return (opIPLength + modeLength + subModeLength + UserNamePasswordlengths + userNameLength + passwordLength);
}
Server C/C++ code (Libwebsockets)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <libwebsockets.h>
using namespace std;
typedef enum {
USER_ACCOUNTS_IP_REQUEST,
MODE,
SUB_MODE,
USER_NAME_LENGTH,
PASSWORD_LENGTH
}user_accounts_ip_request_t;
typedef enum {
USER_ACCOUNTS_IP_RESPONSE,
RESPONSE_IP
}user_accounts_ip_response_t;
int Hex_to_Dec(unsigned char *array, int size);
void sendLoginResponse(struct libwebsocket *wsi, void *in_buffer, size_t bufferlen, struct per_session_data__binary *out_buffer);
void printSocketInfo(struct libwebsocket *wsi, struct libwebsocket_context *currentContext);
struct per_session_data__binary
{
unsigned int len;
unsigned char buf[sizeof(LWS_SEND_BUFFER_PRE_PADDING) + 1024 + sizeof(LWS_SEND_BUFFER_POST_PADDING)];
};
static int callback_http(struct libwebsocket_context *currentContext,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len)
{
return 0;
}
static int callback_sign_in_out_request(struct libwebsocket_context *currentContext,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__binary *psb = (per_session_data__binary *)user; ////////
switch (reason) {
case LWS_CALLBACK_ESTABLISHED: // just log message that someone is connecting
printf("connection established\n");
break;
case LWS_CALLBACK_PROTOCOL_INIT:
printf("LWS_CALLBACK_PROTOCOL_INIT\n");
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
printf("LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n");
break;
case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH:
printf("LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH\n");
break;
case LWS_CALLBACK_RECEIVE: {
printf("Binary frame received decoding packet...\n");
sendLoginResponse(wsi, in, len, psb);
}
break;
default:
break;
}
return 0;
}
static int callback_get_request(struct libwebsocket_context *currentContext,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
return 0;
}
static struct libwebsocket_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", // name
callback_http, // callback
0 // per_session_data_size
},
{
"SIGN_IN_OUT_REQUEST_PROTOCOL", // protocol name - very important!
callback_sign_in_out_request, // callback
sizeof(struct per_session_data__binary) // we don't use any per session data
},
{
"GET_REQUEST_PROTOCOL",
callback_get_request,
sizeof(struct per_session_data__binary)
},
{
NULL, NULL, 0 /* End of list */
}
};
int main(void) {
// server url will be http://localhost:9000
int port = 9000;
const char *interface = NULL;
struct libwebsocket_context *context;
struct lws_context_creation_info info;
// we're not using ssl
const char *cert_path = NULL;
const char *key_path = NULL;
// no special options
int opts = 0;
info.port = port;
info.iface = interface;
info.protocols = protocols;
info.extensions = libwebsocket_get_internal_extensions();
info.ssl_cert_filepath = cert_path;
info.ssl_private_key_filepath = key_path;
info.ssl_ca_filepath = NULL;
info.gid = -1;
info.uid = -1;
info.options = opts;
info.user = NULL;
//info.ka_time = 0;
//info.ka_probes = 100;
//info.ka_interval= 60;
context = libwebsocket_create_context(&info);
if (context == NULL) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
printf("starting server...\n");
// infinite loop, to end this server send SIGTERM. (CTRL+C)
while (1) {
libwebsocket_service(context, 50);
//printf("after service\n");
// libwebsocket_service will process all waiting events with their
// callback functions and then wait 50 ms.
// (this is a single threaded webserver and this will keep our server
// from generating load while there are not requests to process)
}
libwebsocket_context_destroy(context);
return 0;
}
void sendLoginResponse(struct libwebsocket *wsi, void *in_buffer, size_t bufferlen, struct per_session_data__binary *out_buffer)
{
vector<string> userNames, passwords;
string userName, password;
bool loginValid = false;
int usernameLength, passwordLength;
int usernamePos, passwordPos;
userNames.push_back("Nautel");
passwords.push_back("password");
//Print Login Request Data
char* loginRequest = (char*) in_buffer;
usernameLength = loginRequest[USER_NAME_LENGTH];
passwordLength = loginRequest[PASSWORD_LENGTH];
usernamePos = PASSWORD_LENGTH + 1;
passwordPos = usernamePos + usernameLength;
//Get Username
for(int i=usernamePos; i < (usernamePos+usernameLength); i++) {
userName.push_back(loginRequest[i]);
}
//Get Password
for(int i=passwordPos; i < (passwordPos + passwordLength); i++) {
password.push_back(loginRequest[i]);
}
printf("Username: %s Password: %s\n", userName.c_str(), password.c_str());
for(int i=0; i < userNames.size(); i++) {
loginValid = (!userName.compare(userNames[i]) && !password.compare(passwords[i]));
if(loginValid) break;
}
out_buffer->buf[USER_ACCOUNTS_IP_RESPONSE] = 0x13;
out_buffer->buf[RESPONSE_IP] = (loginValid) ? 0x01 : 0x00;
out_buffer->len = 2;
libwebsocket_write(wsi, out_buffer->buf, out_buffer->len, LWS_WRITE_BINARY);
}
void printSocketInfo(struct libwebsocket *wsi, struct libwebsocket_context *currentContext){
char name[128];
int name_len=sizeof(name);
char rip[16];
int rip_len= sizeof(rip);
int fd;
fd = libwebsocket_get_socket_fd(wsi);
libwebsockets_get_peer_addresses (currentContext, wsi, fd, name, name_len, rip, rip_len);
printf("Name: %s, IP: %s\n", name, rip);
}
Is it possible to have a client with multiple subprotocols supported by a single server or vice-versa. Or would I need multiple websocket connections from the client to the server for different protocols.
Regards, Shreyas
Upvotes: 2
Views: 2999
Reputation: 158
During the handshake, a list of supported subprotocol server side is send to the client. The client send its list too. They must have at least one subprotocol in common.
You can implement your own subprotocol or use the existing ones.
For instance, in your case for a websocket server using the libwebsockets library (C/C++) and a javascript websocket client, if you implement your own subprotocol :
Server side :
static struct libwebsocket_protocols protocols[] = {
{
"http-only",
callback_http,
0
},
{
"MySubProtocol",
dedicated_callback_function,
sizeof(struct per_session_data__binary)
}
}
Client side :
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", "MySubProtocol");
Upvotes: 0
Reputation: 22051
A WebSocket client can announce support for multiple WebSocket subprotocols, but a server MUST choose ONE subprotocol (it supports) from the list announced by the client.
Upvotes: 1