I am bit new in CGI programming, and I trying to make an online chat API but face not few troubles:
I was looking online for solution and found Websocket for client (js) and HTTP::Daemon for perl, but I have no idea where to start to make the server listen for the connections from the browser.
Here is my JavaScript code:
ws = new WebSocket('ws://www.crazygao.com:3000'); // test
ws.onopen = function() {
alert('Connection is established!'); // test
};
ws.onclose = function() {
alert('Connection is closed');
};
ws.onmessage = function(e) {
var message = e.data;
//alert('Got new message: ' + message);
};
ws.onerror = function(e) {
//var message = e.data;
alert('Error: ' + e);
};
Here is my Perl script test code:
use HTTP::Daemon;
use HTTP::Status;
my $d = HTTP::Daemon->new(
LocalAddr => 'www.crazygao.com',
LocalPort => 3000
) || die; print "Please contact me at: <URL:", $d->url, ">\n";
while(my $c = $d->accept) {
$c->send_response("1"); # test
while (my $r = $c->get_request) {
if ($r->method eq 'GET') {
$c->send_response("...");
}
}
$c->close;
undef($c);
}
When the page loads, the connection closing immediately, and in Chrome console window I see the following error:
WebSocket connection to 'ws://198.38.89.14:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
I run the perl script manually (using simple call to http://example.com/cgi-bin/xxx.cgi) and then when I refresh the page I get:
WebSocket connection to 'ws://198.38.89.14:3000/' failed: Error during WebSocket handshake: Unexpected response code: 200
I understand that the server normally returns 200 when OK, but Websocket is waiting for 101 code as "OK".
My question is, if so, how can I achieve this?
I know this is old and I got here because I was looking for an answer myself. I ended up finding the answer myself by using Net::WebSocket::Server.
http://search.cpan.org/~topaz/Net-WebSocket-Server-0.003004/lib/Net/WebSocket/Server.pm for more details on how to use the module and example.
Basically, you'll have this perl code to match your javascript (copied and modified from the CPAN page of Net::WebSocket::Server):
use Net::WebSocket::Server;
my $origin = 'http://www.crazygao.com';
Net::WebSocket::Server->new(
listen => 3000,
on_connect => sub {
my ($serv, $conn) = #_;
$conn->on(
handshake => sub {
my ($conn, $handshake) = #_;
$conn->disconnect() unless $handshake->req->origin eq $origin;
},
utf8 => sub {
my ($conn, $msg) = #_;
$_->send_utf8($msg) for $conn->server->connections;
},
binary => sub {
my ($conn, $msg) = #_;
$_->send_binary($msg) for $conn->server->connections;
},
);
},
)->start;
Related
Hello I'm currently implementing a websocket component to my very basic site. I'm running .NET Core 3.1 HTTP Listener for serving html, I've been stumped by implementing websockets.
I've worked with TCP in C# before and understand the flow of everything but websockets are a new thing to me. Here is the C# code for accepting websockets
[Route("/socket", "GET")]
public static async Task upgrade(HttpListenerContext c)
{
if (!c.Request.IsWebSocketRequest)
{
c.Response.StatusCode = 400;
c.Response.Close();
return;
}
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
//WebSocketHandler.AddSocket(sock);
}
I've added var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None); to this function because originally I was getting the exception in my WebSocketHandler class, so I moved the code to the one function to test.
Here is the client:
<script>
let socket = new WebSocket("ws://localhost:3000/socket");
socket.onopen = function (event) {
console.log("[ OPENED ] - Opened Websocket!");
};
socket.onclose = function (event) {
console.log("[ CLOSED ] - Socket closed");
};
socket.onerror = function (error) {
console.log("[ ERROR ] - Got websocket error:");
console.error(error);
};
socket.onmessage = function (event) {
// This function will be responsible for handling events
console.log("[ MESSAGE ] - Message received: ");
const content = JSON.parse(event.data);
console.log(content);
};
</script>
Here is the output in the console for the client:
Navigated to http://127.0.0.1:5500/index.html
index.html:556 [ OPENED ] - Opened Websocket!
index.html:569 [ CLOSED ] - Socket closed
And here is the exception from the C# server:
Got exception: System.Net.WebSockets.WebSocketException (997): The remote party closed the WebSocket connection without completing the close handshake.
at System.Net.WebSockets.WebSocketBase.WebSocketOperation.Process(Nullable`1 buffer, CancellationToken cancellationToken)
at System.Net.WebSockets.WebSocketBase.ReceiveAsyncCore(ArraySegment`1 buffer, CancellationToken cancellationToken)
at SwissbotCore.HTTP.Routes.UpgradeWebsocket.upgrade(HttpListenerContext c) in C:\Users\plynch\source\repos\SwissbotCore\SwissbotCore\HTTP\Routes\UpgradeWebsocket.cs:line 29
I can provide the http requests that the client sends if need be but I am completely stumped on this, any help will be greatly appreciated.
You might want to read up on The Close Handshake in Section 1.4 in RFC 6455 and also Close the WebSocket Connection in Section 7.1.1 in RFC 6455.
Essentially, you need to let the WebSocket endpoint know you are going to close the socket, before you terminate the socket.
For your server side, you should probably be catching this exception, as this can also happen in production scenarios when network issues occur.
I'm not sure why, but, if you change the code inside try block to this:
try
{
var sock = (await c.AcceptWebSocketAsync(null)).WebSocket;
byte[] buff = new byte[1024];
await Listen(sock);
}
catch (Exception x)
{
Console.WriteLine($"Got exception: {x}");
}
private async Task Listen(WebSocket sock)
{
return new Task(async () =>
{
while(sock.State == WebSocketState.Open)
{
var r = await sock.ReceiveAsync(buff, System.Threading.CancellationToken.None);
}
});
}
it's gonna work out fine.
When I try to initialize a websocket connection to the server running on localhost with
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
in javascript, but the server hasn't completed starting up yet, I get the error
SCRIPT12029: WebSocket Error: Network Error 12029, A connection with the server could not be established
How can I prevent this? I.e. how do I check if the server has already started or how can I force the WebSocket client to wait for the server?
What about:
var webSocketFactory = {
connectionTries: 3,
connect: function(url) {
var ws = new WebSocket(url);
ws.addEventListener("error", e => {
// readyState === 3 is CLOSED
if (e.target.readyState === 3) {
this.connectionTries--;
if (this.connectionTries > 0) {
setTimeout(() => this.connect(url), 5000);
} else {
throw new Error("Maximum number of connection trials has been reached");
}
}
});
}
};
var webSocket = webSocketFactory.connect("ws://localhost:8025/myContextRoot");
When you get a connection error, you can do a limited number of trial-errors to try to re-connect. Or you can endlessly try to reach the server.
The accepted answer is perfectly fine. I just would like to extend it a little bit further with promises.
var wsFactory = { tryCount: 3,
connect : function(url){
var ctx = this,
ws = new WebSocket(url);
return new Promise(function(v,x){
ws.onerror = e => { console.log(`WS connection attempt ${4-ctx.tryCount} -> Unsuccessful`);
e.target.readyState === 3 && --ctx.tryCount;
if (ctx.tryCount > 0) setTimeout(() => v(ctx.connect(url)), 1000);
else x(new Error("3 unsuccessfull connection attempts"));
};
ws.onopen = e => { console.log(`WS connection Status: ${e.target.readyState}`);
v(ws);
};
ws.onmessage = m => console.log(m.data);
});
}
};
wsFactory.connect("ws://localhost:8025/myContextRoot")
.then(ws => ws.send("Hey..! This is my first socket message"))
.catch(console.log);
You can't prevent (or put on hold) the WebSocket from starting / establish a connection. WebSocket automatically establishes a connection with the server when its declared. What you can do is place all your code inside onopen event handler that you want to execute on successful connection. So it would be like...
var webSocket = new WebSocket("ws://localhost:8025/myContextRoot");
webSocket.onopen = function() {
// code you want to execute
};
check this article to know more about WebSocket.
Hence the protocol can't get queried by the server if it is not started, the only option is trial and error.
Or you could let the WebSocket server create a simple textfile with the timestamp of the startup in your web space directory where the javascript can retrieve it and than try to establish a connection. You can retrieve the textfile with XMLHttpRequest.
I get the message from client but in case of replying message, i tried many ways but no result .
My JavaScript Code is
var eb = new EventBus("http://localhost:8080/loginUrl");
eb.onopen = function () {
console.log("Connection Open")
};
eb.onclose = function () {
console.log("Connection Close")
};
eb.registerHandler("server-to-client", function (message) {
console.log('received a message: ' + message.body());
});
// publish a message
function sendMes(message){
console.log("Sending Message "+message);
eb.send("client-to-server",message,function(callback){
console.log("Received Message "+callback)
});
}
My Java Server Code is
Router router = Router.router(vertx);
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
SockJSHandler sockJSHandler = SockJSHandler.create(vertx);
BridgeOptions options = new BridgeOptions();
options.addInboundPermitted(new PermittedOptions().setAddress("client-to-server"));
options.addOutboundPermitted(new PermittedOptions().setAddress("server-to-client"));
sockJSHandler.bridge(options);
router.route("/loginUrl/*").handler(sockJSHandler);
EventBus eb = vertx.eventBus();
eb.consumer("client-to-server").handler(sockJSHand->{
System.out.println("Sending Message "+sockJSHand.body());//It prints the message from client
eb.send("server-to-client","Message");
});
How to reply back some message from server ?
Your code sample seems to be fine and almost looks like the chat server-client example application except that your should be using the EventBus#publish API instead of the EventBus#send API to allow the message to be dispatched among all registred handlers (all clients web browsers).
As per the Java docs:
EventBus publish(String address,
Object message)
Publish a message.
The message will be delivered to all handlers registered to the address.
An update of your server side code would be as follows:
Router router = Router.router(vertx);
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
BridgeOptions options = new BridgeOptions();
options.addInboundPermitted(new PermittedOptions().setAddress("client-to-server"));
options.addOutboundPermitted(new PermittedOptions().setAddress("server-to-client"));
SockJSHandler sockJSHandler = SockJSHandler.create(vertx).bridge(options);
router.route("/loginUrl/*").handler(sockJSHandler);
EventBus eb = vertx.eventBus();
eb.consumer("client-to-server").handler(
sockJSHand -> {
System.out.println("Sending Message "+ sockJSHand.body());//It prints the message from client
eb.publish("server-to-client", "Message");
}
);
I have opened the server.js and the address:http://localhost:8081 on my browser. But then a text "Upgrade Required" appeared at the top left conern of the website.
What is the problem of that? What else do I need to upgrade?
Here is the server.js:
var serialport = require('serialport');
var WebSocketServer = require('ws').Server;
var SERVER_PORT = 8081;
var wss = new WebSocketServer({
port: SERVER_PORT
});
var connections = new Array;
SerialPort = serialport.SerialPort,
portName = process.argv[2],
serialOptions = {
baudRate: 9600,
parser: serialport.parsers.readline('\n')
};
if (typeof portName === "undefined") {
console.log("You need to specify the serial port when you launch this script, like so:\n");
console.log(" node wsServer.js <portname>");
console.log("\n Fill in the name of your serial port in place of <portname> \n");
process.exit(1);
}
var myPort = new SerialPort(portName, serialOptions);
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);
function showPortOpen() {
console.log('port open. Data rate: ' + myPort.options.baudRate);
}
function sendSerialData(data) {
if (connections.length > 0) {
broadcast(data);
}
}
function showPortClose() {
console.log('port closed.');
}
function showError(error) {
console.log('Serial port error: ' + error);
}
function sendToSerial(data) {
console.log("sending to serial: " + data);
myPort.write(data);
}
wss.on('connection', handleConnection);
function handleConnection(client) {
console.log("New Connection");
connections.push(client);
client.on('message', sendToSerial);
client.on('close', function () {
console.log("connection closed");
var position = connections.indexOf(client);
connections.splice(position, 1);
});
}
function broadcast(data) {
for (c in connections) {
connections[c].send(data);
}
}
OK, websockets...
The "upgrade required" status marks the start of a websocket handshake. Normally your client sends this first to the WS server. The server answers in a pretty similar manner (details here : https://www.rfc-editor.org/rfc/rfc6455 ), and then proceed to pipe the actual data.
Here, you're opening a connection from your client as regular http, sending a simple GET. What you see on the screen is the server dumbly proceeding with an already corrupted handshake.
That's not how you open a WS client side connection. You don't usually open WS pages from the browser. It ought to be opened from a JavaScript call, such as new WebSocket(uri). So what you want is a regular http server on another port, that serves a page containing the necessary Javascript to open the actual WS connection and do something useful with its data. You'll find a clean example here : http://www.websocket.org/echo.html
I'm currently running into a problem you guys might be able to help me with..
I'm using websockets to connect to a custom server. Now i want to integrate a second Server IP if the first one isn't available.
How is it possible to detect, that the connection couldn't be made because the server isn't reachable? When I enter a wrong ws://url in my script, Chrome for example gives me the following error:
WebSocket connection to 'wss://1234/' failed: Error in connection establishment: net::ERR_NAME_NOT_RESOLVED
in Firefox it's something complete different. Do you guys of any method to catch this error with Javascript?
Basically when the ws:// url can't be reached, i want to change a variable with a different Server-IP and try it with this one again...
Thanks for your help!
It seems there's no way to catch the problem on instantiation, even though the magical JavaScript black box somehow seems to know the problem occurs on the new WebSocket.
To detect this error, use the following:
ws = new WebSocket(server);
ws.onerror = function (evt) {
if (ws.readyState == 3) {
//Connection closed.
}
}
thanks #orbitbot,
I'm using a framework called jwebsocket (jwebsocket.org). My Code is basically this:
serverstate = "0";
console.log(serverstate);
function logon() {
if(serverstate == "0") {
lURL = "wss://testurl-one:9797";
} else if (serverstate == "1") {
lURL = "wss://testurl-two:9797";
}
var gUsername = "user";
var lPassword = "pass";
console.log( "Connecting to " + lURL + " and console.logging in as '" + gUsername + "'..." );
var lRes = lWSC.logon( lURL, gUsername, lPassword, {
// OnOpen callback
OnOpen: function( aEvent ) {
console.log( "jWebSocket connection established." );
},
// OnMessage callback
OnMessage: function( aEvent, aToken ) {
var lDate = "";
if( aToken.date_val ) {
lDate = jws.tools.ISO2Date( aToken.date_val );
}
console.log( "jWebSocket '" + aToken.type + "' token received, full message: '" + aEvent.data + "' " + lDate + "" );
console.log(aToken);
}
},
// OnClose callback
OnClose: function( aEvent ) {
console.log( "Disconnected from Server" );
console.log("Using next server..");
serverstate = "1";
console.log(serverstate);
console.log("Trying to connect to next server");
logon();
},
// OnClose callback
OnError: function( aEvent ) {
console.log ("Some error appeared");
}
});
console.log( lWSC.resultToString( lRes ) );
}
Of course this would work so far. My Problem is that im using websockets to open a connection, get some information, and after that close the connection again.
since this code will always be fired if the server connection is closed (which in many cases i want to..) i can't use it like that...
any other ideas on this problem ?
I got it.. for everyone else who's interested:
When connection is made you receive a message from there server. So if the server is not available.. there'll be no message. So i just added a variable to the "OnMessage" Part.. and in the disconnect i check if a message was received. If not the server isn't there.. if yes, everything works fine..
Assuming your code is something like this,
var host = 'ws://a.real.websocket.url';
var socket = new WebSocket(host);
... you just need to surround the call to new WebSocket with a try/catch block, something like this
try {
var socket = new WebSocket(host);
} catch (e) {
// inspect e to understand why there was an error
// and connect to another url if necessary
}
That being said, it might be easier to work with a websockets library such as Socket.IO or SockJS (https://github.com/sockjs/sockjs-client), which then would change your reconnection code logic to whatever the libraries provide.