Javascript - Arduino - POST request - WiFi101 - Connection issue - javascript

In my code I am trying to send a POST request to the IFTTT service webhooks (maker).
I'm using a couple of libraries, mainly WiFi101
I am using an Arduino MKR1000.
I have updated the firmware, and added a certificate for https://maker.ifttt.com:443.
When in the following code I call sslClient.connect(host, 443); It fails to make the connection. I have tried bypassing this and just trying to print data to the host, however this also didn't work.
It takes about 10-20 seconds for the function to return as false, if I change the host to an incorrect variable, then it returns as false immediately. I'm assuming this is a good sign since the arduino is trying to connect?
wifiSetup() Runs well, connection is established reasonably quickly.
The code I am refering to is below:
Globally defined
//WiFi router setup
char ssid[] = "-----"; //network SSID (aka WiFi name)
char pass[] = "-----"; //network password
int status = WL_IDLE_STATUS;
const char* host = "https://maker.ifttt.com";
WiFiSSLClient sslClient;
Wifi setup procedure: This runs without problems
void wifiSetup() {
// Check for the presence of the shield
Serial.print("WiFi101 shield: ");
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("NOT PRESENT");
return; // don't continue
}
Serial.println("DETECTED");
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
printWifiStatus(); // you're connected now, so print out the status
}
The code below is the one causing problems
void sendMessage() {
if (sslClient.connect(host, 443)) {
//change this to your Maker setting from https://ifttt.com/services/maker/settings
String data = "randomdata";
sslClient.println("POST /trigger/tank_empty/with/key/bxa");
sslClient.println("Host: https://maker.ifttt.com");
sslClient.println("Content-Type: application/json");
sslClient.print("Content-Length: ");
sslClient.println(data.length());
sslClient.println();
sslClient.print(data);
sslClient.stop();
Serial.println("IFTTT request Sucessful");
}
else {
Serial.println("IFTTT request failed");
}
delay(20000000);
}
Does anyone have any solutions, or things to troubleshoot?
Thanks for your help all,
Let me know if you need any extra information.

https://maker.ifttt.com is not a valid host. A valid host is either an IP address or a domain. https:// is not a part of the domain, but an URL.
You are also missing the HTTP protocol version (HTTP/1.1), which could potentially cause problems.
const char* host = "maker.ifttt.com";
sslClient.println("POST /trigger/tank_empty/with/key/bxa HTTP/1.1");
sslClient.print("Host: ");
sslClient.println(host); // non hardcoded host header
sslClient.println("Content-Type: application/json");
sslClient.print("Content-Length: ");
sslClient.println(data.length());
sslClient.println();
sslClient.print(data);
sslClient.stop();

Related

Why does Node.js HTTP server not respond to a request from Python?

I've got a working HTTP node.js server.
I then created a program on python that uses the socket module to connect to the above server
Please for the time being do not mind the try and except statements. The code's connectTO() function simply connects to a server like any other code, with the exception that it handles some errors. Then the program send the message "hello". Next in the while loop it repeatedly waits for an answer and when it receives one, it prints it.
When I connect to the Node.js http server from python, I do get the message:
"You have just succesfully connected to the node.js server"
Which if you look at my code means that the s.connect(()) command was successful. My problem is that when a request is send to the server, it's supposed to output a message back, but it doesn't.
I also tried sending a message to the server, in which case the server sends back the following message:
HTTP/1.1 400 Bad Request
So why is the server not responding to the requests? Why is it rejecting them?
Python Client:
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
import threading, socket, time, sys
s = socket.socket(AF_INET,SOCK_STREAM)
def connectTO(host,port):
connect = False
count = 0
totalCount = 0
while connect!= True:
try:
s.connect((host,port))
connect = True
print("You have just succesfully connected to the node.js server")
except OSError:
count += 1
totalCount += 1
if totalCount == 40 and count == 4:
print("Error: 404. Connection failed repeatedly")
sys.exit(0)
elif count == 4:
print("Connection failed, retrying...")
count = 0
else:
pass
connectTO("IP_OF_NODE.jS_SERVER_GOES_HERE",777)
message = "hello"
s.send(message.encode("utf-8"))
while True:
try:
data, addr = s.recvfrom(1024)
if data == "":
pass
else:
print(data.decode())
except ConnectionResetError:
print("it seems like we can't reach the server anymore..")
print("This could be due to a change in your internet connection or the server.")
s.close()
Node.js HTTP server:
function onRequest(req, res) {
var postData = "";
var pathname = url.parse(req.url).pathname;
//Inform console of event recievent
console.log("Request for "+pathanme+" received.");
//Set the encoding to equivelant one used in html
req.setEncoding("utf8");
//add a listener for whenever info comes in and output full result
req.addListener("data", function(postDataChunk) {
postData += postDataChunk;
console.log("Received POST data chunk: '"+postDataChunk+"'");
});
req.addListener("end", function() {
route(handle, pathname, res, frontPage, postData);
});
};
http.createServer(onRequest).listen(port,ip);
console.log("Server has started.");
Some of my Research
I should also note that after some research, it seems that an HTTP server accepts HTTP requests, but I don't understand most of what's on Wikipedia. Is this the reason why the server is not responding? And how do I fix that while still using the socket module.
Also there are a lot of similar questions on Stack Overflow, but none help me solve my problem. One of them describes my issue, and the only answer is about "handshakes". Google is also pointless here, but from what I understand it is simply a reaction between the server and the client which defines what the protocol will be. Could this be what I'm missing, and how do I implement it?
Some of these questions also use modules that I'm not ready to use yet like websocket. Either that or they describe a way in which the server connects to the client, which can be done by directly calling python code or connecting to it from Node.js express. I want the client to be the one connecting to an HTTP server, by the means of the socket module in python. For the sake of future visitors who are looking for something like this, here are some of these question:
How to connect node.js app with python script?
Python Client to nodeJS Server with Socket.IO
Python connecting to an HTTP server
A blog that also does something similar to what is described above: https://www.sohamkamani.com/blog/2015/08/21/python-nodejs-comm/
Here is an answer that doesn't actually seem that obvious, but also solves the issue with only the relevant code. People who don't yet no much about servers in general will have probably missed it:
how to use socket fetch webpage use python
You will need to construct an HTTP request.
Example: GET / HTTP/1.1\n\n
Try this:
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
import threading, socket, time, sys
s = socket.socket(AF_INET,SOCK_STREAM)
def connectTO(host,port):
connect = False
count = 0
totalCount = 0
while connect!= True:
try:
s.connect((host,port))
connect = True
print("You have just succesfully connected to the node.js server")
except OSError:
count += 1
totalCount += 1
if totalCount == 40 and count == 4:
print("Error: 404. Connection failed repeatedly")
sys.exit(0)
elif count == 4:
print("Connection failed, retrying...")
count = 0
else:
pass
connectTO("IP_OF_NODE.jS_SERVER_GOES_HERE",777)
message = "GET / HTTP/1.1\n\n"
s.send(message.encode("utf-8"))
while True:
try:
data, addr = s.recvfrom(1024)
if data == "":
pass
else:
print(data.decode())
except ConnectionResetError:
print("it seems like we can't reach the server anymore..")
print("This could be due to a change in your internet connection or the server.")
s.close()
Read this to learn more about HTTP.
Now, I would recommend using this python lib to do what you're trying to do. It makes things much easier. However, if you are 100% set on using raw sockets, then you should make the node server use raw sockets as well. (Assuming you will only be connecting via python). Here is an excellent tutorial

signalr - with different clients

I am trying to setup a signalR system.
I have the sample code working, using two browsers and the same hub. messages are sent and received.
Now, when I created a different page, and try to send messages to the hub, it appears to be kinda working, meaning it doesn't blow up, but nothing gets transmitted to the other clients.
I thought I was accessing the same message hub, from all the clients, but maybe I am missing something.
Is it possible to connect different web sites to the same message hub?
Begin Edit
As requested.... here is the code i am using on my second client...
var connection = $.hubConnection('http://xxxxxxxxx.azurewebsites.net/');
var contosoChatHubProxy = connection.createHubProxy('MessagePump');
// contosoChatHubProxy.on('Send', function (name, message) {console.log(name + ' ' + message);});
$.connection.hub.start()
.done(function () {
console.log('Now connected, connection ID=' + $.connection.hub.id); // returns an ID
// $.connection.hub.send('testing', 'this is a test from the client');
// contosoChatHubProxy.send("testing");
// contosoChatHubProxy.invoke('testing', 'this is a test for the client 1');
// contosoChatHubProxy.invoke('say', 'this is a test for the client 2');
// contosoChatHubProxy.invoke('Send', 'This is a test for client 3');
// $.connection.hub.send('testing', 'this is a test from the client 4');
contosoChatHubProxy.invoke('messagePump', 'user', 'this is a test message for 5');
})
.fail(function(){ console.log('Could not Connect!'); });
This is what i am seeing in firebug
From what i can make of the code, the proxy appears to be loading locally, and not even seeing the remote system hub...
My console application(s) that only connect to the remote system hub are able to send and receive messages.
btw - i have tried upper can lower case (MessagePump, messagePump)
but it has not changed the result.
var connection = $.hubConnection('http://xxxxxxxxx.azurewebsites.net/');
You are trying to connect a different website. This http://xxxxxxxxx.azurewebsites.net/ should let cross domain requests.Otherwise you can't connect. If you can manage http://xxxxxxxxx.azurewebsites.net/, you should configure signalr like:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Branch the pipeline here for requests that start with "/signalr"
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
}

AuthorizedHandler Blocked wrong request! url: /socket.io/

I'm using mrniko/netty-socketio (Java) to start a websocket server like this:
config = new Configuration();
config.setHostname("localhost");
config.setPort(8001);
server = new SocketIOServer(config);
server.addListeners(serviceClass);
server.start();
Then I'm using (the recommended) socketio/socket.io-client (JavaScript) to try to connect to the websocket server like this (all on the same server):
var socket = io("http://localhost:8001");
The connection is "blocked" at the server with the server printing:
8239 [nioEventLoopGroup-5-1] WARN com.corundumstudio.socketio.handler.AuthorizeHandler - Blocked wrong request! url: /socket.io/, ip: /127.0.0.1:48915
28889 [nioEventLoopGroup-5-2] WARN com.corundumstudio.socketio.handler.AuthorizeHandler - Blocked wrong request! url: /socket.io/, ip: /127.0.0.1:48916
Which occurs endlessly, as the client continues to retry the connection.
I can't seem to get the server to accept the connection. I've tried:
var socket = io("ws://localhost:8001");
But that gives the same outcome. I've also tried putting a trailing slash after the URL for both cases - makes no difference. I've also tried all combinations of using "localhost" or "127.0.0.1" at both the server and client, and so on.
The JavaScript page itself is being served up from a http server on localhost:8000. This does not appear to be a cross site issue as that gives an entirely different error at the browser.
Does anyone know what is going wrong and how to fix it?
In my case network monitoring accesses that port every 10 seconds. I had temporarily changed log4j.properties to ERROR level logging, but wanted to provide networking a path to use that would not cause excessive warn logging. Not sure if this was the best approach, but this is what I ended up doing.
config.setAllowCustomRequests(true);
By allowing custom requests the piece of code displaying the warning was bypassed in Authorizehandler.
I created a custom pipeline, that allowed me to switch out the wrongUrlHandler with a custom one to allow a safe path to use for monitoring.
public class CustomSocketIOChannelInitializer extends SocketIOChannelInitializer {
CustomWrongUrlHandler customWrongUrlHandler = null;
public CustomSocketIOChannelInitializer(Configuration configuration) {
customWrongUrlHandler = new CustomWrongUrlHandler(configuration);
}
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
addSslHandler(pipeline);
addSocketioHandlers(pipeline);
// Replace wrong url handler with our custom one to allow network monitoring without logging warnings.
pipeline.replace(SocketIOChannelInitializer.WRONG_URL_HANDLER, "CUSTOM_WRONG_URL_HANDLER", customWrongUrlHandler);
}
This is my custom handler:
#Sharable
public class CustomWrongUrlHandler extends ChannelInboundHandlerAdapter {
private final Logger log = LoggerFactory.getLogger(getClass());
Configuration configuration = null;
/**
* #param configuration
*/
public CustomWrongUrlHandler(Configuration configuration) {
this.configuration = configuration;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
Channel channel = ctx.channel();
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri());
// Don't log when port is pinged for monitoring. Must use context that starts with /ping.
if (configuration.isAllowCustomRequests() && queryDecoder.path().startsWith("/ping")) {
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
channel.writeAndFlush(res).addListener(ChannelFutureListener.CLOSE);
req.release();
//log.info("Blocked wrong request! url: {}, ip: {}", queryDecoder.path(), channel.remoteAddress());
return;
}
// This is the last channel handler in the pipe so if it is not ping then log warning.
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
ChannelFuture f = channel.writeAndFlush(res);
f.addListener(ChannelFutureListener.CLOSE);
req.release();
log.warn("Blocked wrong socket.io-context request! url: {}, params: {}, ip: {}", channel.remoteAddress() + " " + queryDecoder.path(), queryDecoder.parameters());
}
}
}
CustomSocketIOChannelInitializer customSocketIOChannelInitializer = new CustomSocketIOChannelInitializer(config);
server.setPipelineFactory(customSocketIOChannelInitializer);

Connecting to websocket using C# (I can connect using JavaScript, but C# gives Status code 200 error)

I am new in the area of websocket.
I can connect to websocket server using JavaScript using this code:
var webSocket = new WebSocket(url);
But for my application, I need to connect to the same server using c#. The code I am using is:
ClientWebSocket webSocket = null;
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
3rd line of the code results following error:
"Server returned status code 200 when status code 101 was expected"
After little bit of survey, I realised that somehow server can't switch http protocol to websocket protocol during connection process.
Am I doing anything stupid in my C# code or there is something going wrong with the server. I don't have any access to the server, as the url I am using is a third party one .
Could you please give me any suggestion regarding the issue?
TL; DR:
Use ReceiveAsync() in loop until Close frame is received or CancellationToken is canceled. That's how you get your messages. Sending is straightworward, just SendAsync(). Do not use CloseAsync() before CloseOutputAsync() - because you want to stop your receiving loop first. Otherwise - either the CloseAsync() would hang, or if you use CancellationToken to quit ReceiveAsync() - the CloseAsync() would throw.
I learned a lot from https://mcguirev10.com/2019/08/17/how-to-close-websocket-correctly.html .
Full answer:
Use Dotnet client, here, have an example cut out from my real life code, that illustrate how the handshaking is made. The most important thing most people don't understand about how the thing operates is that there is no magic event when a message is received. You create it yourself. How?
You just perform ReceiveAsync() in a loop that ends, when a special Close frame is received. So when you want to disconnect you have to tell the server you close with CloseOutputAsync, so it would reply with a similar Close frame to your client, so it would be able to end receiving.
My code example illustrates only the most basic, outer transmission mechanism. So you send and receive raw binary messages. At this point you cannot tell the specific server response is related to the specific request you've sent. You have to match them yourself after coding / decoding messages. Use any serialization tool for that, but many crypto currency markets use Protocol Buffers from Google. The name says it all ;)
For matching any unique random data can be used. You need tokens, in C# I use Guid class for that.
Then I use request / response matching to make request work without dependency on events. The SendRequest() methods awaits until matching response arrives, or... the connection is closed. Very handy and allows to make way more readable code than in event-based approach. Of course you can still invoke events on messages received, just make sure they are not matched to any requests that require response.
Oh, and for waiting in my async method I use SemaphoreSlim. Each request puts its own semaphore in a special dictionary, when I get the response, I find the entry by the response token, release the semaphore, dispose it, remove from the dictionary. Seems complicated, but it's actually pretty simple.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace Example {
public class WsClient : IDisposable {
public int ReceiveBufferSize { get; set; } = 8192;
public async Task ConnectAsync(string url) {
if (WS != null) {
if (WS.State == WebSocketState.Open) return;
else WS.Dispose();
}
WS = new ClientWebSocket();
if (CTS != null) CTS.Dispose();
CTS = new CancellationTokenSource();
await WS.ConnectAsync(new Uri(url), CTS.Token);
await Task.Factory.StartNew(ReceiveLoop, CTS.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public async Task DisconnectAsync() {
if (WS is null) return;
// TODO: requests cleanup code, sub-protocol dependent.
if (WS.State == WebSocketState.Open) {
CTS.CancelAfter(TimeSpan.FromSeconds(2));
await WS.CloseOutputAsync(WebSocketCloseStatus.Empty, "", CancellationToken.None);
await WS.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
}
WS.Dispose();
WS = null;
CTS.Dispose();
CTS = null;
}
private async Task ReceiveLoop() {
var loopToken = CTS.Token;
MemoryStream outputStream = null;
WebSocketReceiveResult receiveResult = null;
var buffer = new byte[ReceiveBufferSize];
try {
while (!loopToken.IsCancellationRequested) {
outputStream = new MemoryStream(ReceiveBufferSize);
do {
receiveResult = await WS.ReceiveAsync(buffer, CTS.Token);
if (receiveResult.MessageType != WebSocketMessageType.Close)
outputStream.Write(buffer, 0, receiveResult.Count);
}
while (!receiveResult.EndOfMessage);
if (receiveResult.MessageType == WebSocketMessageType.Close) break;
outputStream.Position = 0;
ResponseReceived(outputStream);
}
}
catch (TaskCanceledException) { }
finally {
outputStream?.Dispose();
}
}
private async Task<ResponseType> SendMessageAsync<RequestType>(RequestType message) {
// TODO: handle serializing requests and deserializing responses, handle matching responses to the requests.
}
private void ResponseReceived(Stream inputStream) {
// TODO: handle deserializing responses and matching them to the requests.
// IMPORTANT: DON'T FORGET TO DISPOSE THE inputStream!
}
public void Dispose() => DisconnectAsync().Wait();
private ClientWebSocket WS;
private CancellationTokenSource CTS;
}
}
BTW, why use other libraries than the .NET built in? I can't find any reason other than maybe poor documentation of the Microsoft's classes. Maybe - if for some really weird reason you would want to use modern WebSocket transport with an ancient .NET Framework ;)
Oh, and I haven't tested the example. It's taken from the tested code, but all inner protocol parts were removed to leave only the transport part.
Since WebsocketSharp is not .NET Core compatible I suggest using websocket-client instead.
Here's some sample code
static async Task Main(string[] args)
{
var url = new Uri("wss://echo.websocket.org");
var exitEvent = new ManualResetEvent(false);
using (var client = new WebsocketClient(url))
{
client.MessageReceived.Subscribe(msg => Console.WriteLine($"Message: {msg}"));
await client.Start();
await client.Send("Echo");
exitEvent.WaitOne();
}
Console.ReadLine();
}
Be sure to use ManualResetEvent. Otherwise it doesn't work.
If you connect with a WebSocket client and you get an HTTP 200 as response, means that probably you are connecting to the wrong place (host, path and/or port).
Basically, you are connecting to a normal HTTP endpoint that is not understanding your WebSocket requirement, and it is just returning the "OK" response (HTTP 200). Probably the WebSocket server runs in another port or path in the same server.
Check your URL.
Not quite sure what happened to WebSocketSharp nuget package, however I noticed that now WebSocket# is showing up as most relevant result in nuget repo. It took me some time before I realized that Connect() is now returning Task, hopefully this example will be useful to someone:
using System;
using System.Threading.Tasks;
using WebSocketSharp;
namespace Example
{
class Program
{
private static void Main(string[] args)
{
using (var ws = new WebSocket(url: "ws://localhost:1337", onMessage: OnMessage, onError: OnError))
{
ws.Connect().Wait();
ws.Send("Hey, Server!").Wait();
Console.ReadKey(true);
}
}
private static Task OnError(ErrorEventArgs errorEventArgs)
{
Console.Write("Error: {0}, Exception: {1}", errorEventArgs.Message, errorEventArgs.Exception);
return Task.FromResult(0);
}
private static Task OnMessage(MessageEventArgs messageEventArgs)
{
Console.Write("Message received: {0}", messageEventArgs.Text.ReadToEnd());
return Task.FromResult(0);
}
}
}
All the libraries mentioned above are Wrappers. The .Net Frameworks class doing this is System.Net.WebSockets.ClientWebSocket
Websocket URLs should start with ws:// or wss:// where the latter is secure websocket.

Sending message to a specific connected users using webSocket?

I wrote a code for broadcasting a message to all users:
// websocket and http servers
var webSocketServer = require('websocket').server;
...
...
var clients = [ ];
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
...
});
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server.
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
...
var connection = request.accept(null, request.origin);
var index = clients.push(connection) - 1;
...
Please notice:
I don't have any user reference but only a connection .
All users connection are stored in an array.
Goal: Let's say that the Node.js server wants to send a message to a specific client (John). How would the NodeJs server know which connection John has? The Node.js server doesn't even know John. all it sees is the connections.
So, I believe that now, I shouldn't store users only by their connection, instead, I need to store an object, which will contain the userId and the connection object.
Idea:
When the page finishes loading (DOM ready) - establish a connection to the Node.js server.
When the Node.js server accept a connection - generate a unique string and send it to the client browser. Store the user connection and the unique string in an object. e.g. {UserID:"6", value: {connectionObject}}
At client side, when this message arrives - store it in a hidden field or cookie. (for future requests to the NodeJs server )
When the server wants to send a message to John:
Find john's UserID in the dictionary and send a message by the corresponding connection.
please notice there is no asp.net server code invloced here (in the message mechanism). only NodeJs .*
Question:
Is this the right way to go?
This is not only the right way to go, but the only way. Basically each connection needs a unique ID. Otherwise you won't be able to identify them, it's as simple as that.
Now how you will represent it it's a different thing. Making an object with id and connection properties is a good way to do that ( I would definitely go for it ). You could also attach the id directly to connection object.
Also remember that if you want communication between users, then you have to send target user's ID as well, i.e. when user A wants to send a message to user B, then obviously A has to know the ID of B.
Here's a simple chat server private/direct messaging.
package.json
{
"name": "chat-server",
"version": "0.0.1",
"description": "WebSocket chat server",
"dependencies": {
"ws": "0.4.x"
}
}
server.js
var webSocketServer = new (require('ws')).Server({port: (process.env.PORT || 5000)}),
webSockets = {} // userID: webSocket
// CONNECT /:userID
// wscat -c ws://localhost:5000/1
webSocketServer.on('connection', function (webSocket) {
var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
webSockets[userID] = webSocket
console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(webSockets))
// Forward Message
//
// Receive Example
// [toUserID, text] [2, "Hello, World!"]
//
// Send Example
// [fromUserID, text] [1, "Hello, World!"]
webSocket.on('message', function(message) {
console.log('received from ' + userID + ': ' + message)
var messageArray = JSON.parse(message)
var toUserWebSocket = webSockets[messageArray[0]]
if (toUserWebSocket) {
console.log('sent to ' + messageArray[0] + ': ' + JSON.stringify(messageArray))
messageArray[0] = userID
toUserWebSocket.send(JSON.stringify(messageArray))
}
})
webSocket.on('close', function () {
delete webSockets[userID]
console.log('deleted: ' + userID)
})
})
Instructions
To test it out, run npm install to install ws. Then, to start the chat server, run node server.js (or npm start) in one Terminal tab. Then, in another Terminal tab, run wscat -c ws://localhost:5000/1, where 1 is the connecting user's user ID. Then, in a third Terminal tab, run wscat -c ws://localhost:5000/2, and then, to send a message from user 2 to 1, enter ["1", "Hello, World!"].
Shortcomings
This chat server is very simple.
Persistence
It doesn't store messages to a database, such as PostgreSQL. So, the user you're sending a message to must be connected to the server to receive it. Otherwise, the message is lost.
Security
It is insecure.
If I know the server's URL and Alice's user ID, then I can impersonate Alice, ie, connect to the server as her, allowing me to receive her new incoming messages and send messages from her to any user whose user ID I also know. To make it more secure, modify the server to accept your access token (instead of your user ID) when connecting. Then, the server can get your user ID from your access token and authenticate you.
I'm not sure if it supports a WebSocket Secure (wss://) connection since I've only tested it on localhost, and I'm not sure how to connect securely from localhost.
For people using ws version 3 or above. If you want to use the answer provided by #ma11hew28, simply change this block as following.
webSocketServer.on('connection', function (webSocket) {
var userID = parseInt(webSocket.upgradeReq.url.substr(1), 10)
webSocketServer.on('connection', function (webSocket, req) {
var userID = parseInt(req.url.substr(1), 10)
ws package has moved upgradeReq to request object and you can check the following link for further detail.
Reference: https://github.com/websockets/ws/issues/1114
I would like to share what I have done. Hope it doesn't waste your time.
I created database table holding field ID, IP, username, logintime and logouttime. When a user logs in logintime will be currect unixtimestamp unix. And when connection is started in websocket database checks for largest logintime. It will be come user logged in.
And for when user logs out it will store currect logouttime. The user will become who left the app.
Whenever there is new message, Websocket ID and IP are compared and related username will be displayed. Following are sample code...
// when a client connects
function wsOnOpen($clientID) {
global $Server;
$ip = long2ip( $Server->wsClients[$clientID][6] );
require_once('config.php');
require_once CLASSES . 'class.db.php';
require_once CLASSES . 'class.log.php';
$db = new database();
$loga = new log($db);
//Getting the last login person time and username
$conditions = "WHERE which = 'login' ORDER BY id DESC LIMIT 0, 1";
$logs = $loga->get_logs($conditions);
foreach($logs as $rows) {
$destination = $rows["user"];
$idh = md5("$rows[user]".md5($rows["time"]));
if ( $clientID > $rows["what"]) {
$conditions = "ip = '$ip', clientID = '$clientID'
WHERE logintime = '$rows[time]'";
$loga->update_log($conditions);
}
}
...//rest of the things
}
interesting post (similar to what I am doing).
We are making an API (in C#) to connect dispensers with WebSockets, for each dispenser we create a ConcurrentDictionary that stores the WebSocket and the DispenserId making it easy for each Dispenser to create a WebSocket and use it afterwards without thread problems (invoking specific functions on the WebSocket like GetSettings or RequestTicket).
The difference for you example is the use of ConcurrentDictionary instead of an array to isolate each element (never attempted to do such in javascript).
Best regards,

Categories

Resources