i'm new to websockets. Ive just completed my first multipurpose socket server, which serves 3 types of messages in the same socket. This server's intended usage is as follows:
chat messages
real time market data
user specific alerts
The first two functions work great and i'm totally happy, however i am at a total loss when it comes to identifying and linking peers in the connection to specific accounts, so that i can serve pending alert notices to specific users.
basically, i store the alert messages in a database table like so:
Table: uc_notifications
account(varchar50) | message(varchar255) | id(primary,AI) | seen(default 0)
i want to store the peers in a table like so:
uc_notification_peers
account(varchar50) | peer_id(unique,varchar100) | id(primary,AI)
Basically, the problem i'm having is storing the peer's info in the db, and retrieving it so i can send the message to the select peer while reading the notification table. Everything else in the script worked.
The first thing i tried, was passing the account name to the websocket using onopen. this crashes the websocket.
The next thing i tried was accessing the user session through globals pertaining to the ip address. didn't work either, and resulted in messages being received by all accounts on same ip( i was testing on localhost in all 3 browsers).
The last thing i tried, was the first method. i set a timeout of 2 seconds on the on open event, then sent the account message. i was able to finally store a peer in the database! but, to my chagrin when i checked the database, it said ("resource id#6). i tried casting the $clients array to globals but its empty everytime. i googled around a bit, then i tried serializing the array. it all works until i get to the part where i send the message to the unique peer.
here's the function for sending the message
function send_message_single_client($msg,$client)
{
@socket_write($client,$msg,strlen($msg));
return true;
}
here's an example of where i use this function to push the last 100 messages to the chatbox of the new peer.
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket);
$clients[] = $socket_new;
$header = socket_read($socket_new, 1024);
perform_handshaking($header, $socket_new, $host, $port);
socket_getpeername($socket_new, $ip);
$found_socket = array_search($socket, $changed);
//need to push last 100 messages to new peer.
$query = $mysqli->query("SELECT * FROM (SELECT * FROM uc_chat_msg WHERE `hidden`='0' ORDER BY `id` DESC LIMIT 100) as last100 ORDER BY id");
while($row = $query->fetch_assoc()) {
$response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>security($row["username"]), 'message'=>security($row["message"]), 'color'=>security($row["color"]))));
send_message_single_client($response_text, $socket_new);//send old messages to the new client
}
$query->close();
unset($changed[$found_socket]);
}
That was a few weeks ago, and i put the project on the backburner so i could move on and focus on other things. Since, i've moved to a development vps to accomadate the growing size/complexity of the application. instead of php, i'm using facebooks HHVM, not relevant but thought i would mention it, since it has a few quirks that forced me to alter my code a bit.
I have no idea about PHP, but I think this is more an approach issue rather than coding issue. So I hope these thoughts from my experience with push architectures bring some light.
WebSockets use a HTTP handshake that will send the cookies for that origin. You could attach an temporary ID (eg: GUID) to a special session cookie, that will be resend every time the user reconnects in the same session. You can deploy the cookie from the website as long you are honouring the Same Origin Policy, or in the HTTP handshake response itself.
When the WebSocket connect, you can send from the server an initial message with a token that contains a temporary ID. That token can be resend on reconnection if the connection drops, but will be forgot if the user reload the page unless it is saved in local storage or any other kind of browser storage.
How that temporary ID maps to a particular user or account, is an implementation detail you DO NOT WANT to leak to the web. So do not use that information to identify your WS connections.
You should keep a "session" registry that maps temporary IDs to accounts, that maybe from a in memory dictionary, to a table on the DB. Up to you, your requirements and your service. This element must keep reference to the WebSocket connection, so you can retrieve it by temporary ID when needed.
Never leak internal details, specially account ids (because they do not change).
When receiving a message from a WebSocket, you must create a message object that contains the sender account using the "session" information you are keeping, and also the actual message content. Then, pass this message object to your business logic that will decide what to do.
In order to send messages, you must define first several elements:
Which accounts are online? Merge your "session" storage with the account table. When a connection drops, you should remove the drop user session.
Which accounts belong to a particular group or signal? You must design this according to your requirements. Some groups could be predefined and indicated in the "account" entities, some other may be dynamic like he belonging to a chat room. In any case, you need another element that maps "groups" to "accounts"
When your business logic decides it wants to send a message, either as response to an incoming message, as a timed action, own initiative or whatever... it must decide to which user account, user accounts or user group must be sent.
Then, using the session information that you keep, gather the list of temporary IDs that must receive that message, then the lists of WebSockets where that message must be forwarded to, and finally send the message to each of the WS of that list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With