How can server interrupt client in browser?

A

Anil

I have a Javascript program which runs in the browser and has
functions work(), and stop().
It listens to commands from the server to work() and can be
interrupted by the server to stop().
I am using XmlHttpRequest to talk to the server.
So I use http GET to send a command "ready" to the server, which
replies at some point in time by sending "work" which is invoked by
the callback.

As per my understanding, the browser client is single threaded. How to
get the server to interrupt the browser client to say "stop"?

I modified Flanagan's example code here:

var HTTP = {

// This is a list of XMLHttpRequest creation factory functions to try
_factories : [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
],

// When we find a factory that works, store it here
_factory : null,

// Create and return a new XMLHttpRequest object.
//
// The first time we're called, try the list of factory functions
until
// we find one that returns a nonnull value and does not throw an
// exception. Once we find a working factory, remember it for later
use.
//
newRequest : function() {
if (HTTP._factory != null) return HTTP._factory();

for(var i = 0; i < HTTP._factories.length; i++) {
try {
var factory = HTTP._factories;
var request = factory();
if (request != null) {
HTTP._factory = factory;
return request;
}
}
catch(e) {
continue;
}
}

// If we get here, none of the factory candidates succeeded,
// so throw an exception now and for all future calls.
HTTP._factory = function() {
throw new Error("XMLHttpRequest not supported");
}
HTTP._factory(); // Throw an error
},

/**
* Use XMLHttpRequest to fetch the contents of the specified URL
using
* an HTTP GET request. When the response arrives, pass it (as plain
* text) to the specified callback function.
*
* This function does not block and has no return value.
*/
toServer : function(url) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
MyProxy.fromServer(request.responseText);
}
request.open("GET", url, true); // 3rd param implies asynchronous
console.log("sending " + url);
request.send(null);
},

};

......
// MyProxy
....
fromServer: function(command) {
console.log("fromServer: " + command);
eval(command);
},
thanks,
Anil
 
S

sasuke

As per my understanding, the browser client is single threaded. How to
get the server to interrupt the browser client to say "stop"?

The communication has to be initiated by the client. Either keep
polling the server periodically[Ajax with polling] or use something
along the lines of Comet technology [Web Server push enabled by the
use of long held http connections].
<URL: http://en.wikipedia.org/wiki/Comet_(programming)>

/sasuke
 
A

Anil

As per my understanding, the browser client is single threaded. How to
get the server to interrupt the browser client to say "stop"?

The communication has to be initiated by the client. Either keep
polling the server periodically[Ajax with polling] or use something
along the lines of Comet technology [Web Server push enabled by the
use of long held http connections].
<URL:http://en.wikipedia.org/wiki/Comet_(programming)>

/sasuke

Thanks for replying. I was wondering if a trick like this would work,
but it doesn't seem to. Is it a bug in my implementation or is it
conceptually faulty?
In MyProxy.fromServer(), issue another toServer("url").
Thus there will always be a GET request pending. Since it is
asynchronous, it will return immediately. But the callback will occur
while the client is executing.
so what happens here - what will the browser do - will it simply
block, or a new thread be created? My understanding is stuck here.
 
S

sasuke

The communication has to be initiated by the client. Either keep
polling the server periodically[Ajax with polling] or use something
along the lines of Comet technology [Web Server push enabled by the
use of long held http connections].
<URL:http://en.wikipedia.org/wiki/Comet_(programming)>

Thanks for replying. I was wondering if a trick like this would work,
but it doesn't seem to. Is it a bug in my implementation or is it
conceptually faulty?
In MyProxy.fromServer(), issue another toServer("url").
Thus there will always be a GET request pending. Since it is
asynchronous, it will return immediately. But the callback will occur
while the client is executing.
so what happens here - what will the browser do - will it simply
block, or a new thread be created? My understanding is stuck here.

Javascript implementations don't support threading. When making asyn
requests, the browser never blocks; instead as soon as a response is
returned from the server, the callback function is executed which
performs the required processing.

Like I mentioned before, just keep polling the server for commands
using setInterval and make provision for a NO-OP command. Thoroughly
read the Comet article in case you want to avoid polling. Modern web
servers like Tomcat come with in-built Comet support.

/sasuke
 
T

Thomas 'PointedEars' Lahn

Anil said:
I have a Javascript program which runs in the browser and has
functions work(), and stop().
It listens to commands from the server to work() and can be
interrupted by the server to stop().
I am using XmlHttpRequest to talk to the server.
So I use http GET to send a command "ready" to the server, which
replies at some point in time by sending "work" which is invoked by
the callback.

As per my understanding, the browser client is single threaded.

That is correct, however it is rather irrelevant here because the point
in using asynchronous request-response handling (as you do) is to work
around that.
How to get the server to interrupt the browser client to say "stop"?

One possibility is the obvious:

fromServer: function(command) {
console.log("fromServer: " + command);

if (command != "stop")
{
eval(command);
}
},

That is not interrupting anything, though, as nothing needs to be
interrupted in the first place.

However, the reference implementation and other implementations also support
a way to cancel the current HTTP request while it is in progress, the
abort() method:

<http://msdn.microsoft.com/en-us/library/ms760305(VS.85).aspx>
<https://developer.mozilla.org/En/XMLHttpRequest>
<http://www.opera.com/docs/specs/xhr/index.dml>
<http://developer.kde.org/documentation/library/3.4-api/khtml/html/xmlhttprequest_8cpp-source.html>
I modified Flanagan's example code here:

You should not use Flanagan's code, and particularly not this code as it is
error-prone.


PointedEars
 
A

Anil

Can you please tell me why it is error prone? Is there better code
that will do the same thing... if so, can you please point me to it?

Also, "stop" is not for canceling the http request. stop() does a
specific task.
thanks,
Anil
 
T

Thomas 'PointedEars' Lahn

Anil said:
Can you please tell me why it is error prone?

It uses exception handling without fallback to begin with.
Is there better code that will do the same thing...
Yes.

if so, can you please point me to it?

It is located in your /dev/brain.


Please don't top-post. Reply below parts of trimmed quotes instead.


PointedEars
 
R

Richard Maher

Hi Anil,
As per my understanding, the browser client is single threaded. How to
get the server to interrupt the browser client to say "stop"?

On the off-chance that you're not like every other xenophobic little-HTTPer
here, and you won't let any personal prejudices stop you from thinking
outside the Ajax-box for a moment, let me offer you a Java Applet
alternative. (If/when Flash or Silverlight get around to supporting UDP you
can probably do it with them as well.)

If you'd like to see an example of a threaded Java Applet that receives UDP
messages from an abitrary server(s), then please: -

1) Click on the following link and read the instructions:
http://manson.vistech.net/~tier3/tier3pager.html

2) Telnet to manson.vistech.net. (If you don't already have an account on
the Deathrow cluster then please use Username: DEMO Password: USER) and
then:

$set term/width=132
$run sys$users:[users.tier3.web]demo_udp_msg

3) Enter the IP address of your client node. Your "stock-monitor" from step
1 should now spring into life with random stock-prices generated at 2sec
intervals. (NATed clients will find this bit problematic :)

4) Enter any adhoc messages that you wish to appear in the seperate Java
Frame on the client.

OPCOM messages to web-subscribers? CHAT conferences? Stock-Watching? Alarm
Monitoring? - It's all good!

The rationale here is not to use up a TCP/IP connection and a server
process/thread for ad-hoc, asynchronous, broadcasts, and most definitely do
use something other than a connectionless, context-devoid,
session-hijackable, pile of http-pooh as your middleware backbone.

Cheers Richard Maher

PS. The code for Tier3Pager.java aqnd DEMO_UDP_MSG.COB are below, but all
can be found at SYS$USERS:[USERS.TIER3.WEB]

PPS. If this is for the Intranet then you might wish to look at Broadcast or
even Multicast functionality as well, and eliminate the "subscribe" step
altogether.

Tier3Pager.java
===========

/**
* Copyight Tier3 Software. All rights reserved.
*
* Author: Richard Maher
*
**/

import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.IOException;
import netscape.javascript.JSObject;
import netscape.javascript.JSException;

public class Tier3Pager extends Applet
{
private String hostName;
private JSObject browser;
private static MessageThread socketThread;
private static Tier3Talk chat;

public class MessageThread extends Thread
{
private DatagramSocket socket;
private DatagramPacket packet;
private String threadData;

public MessageThread(String name, String txt) throws Exception
{
super(name);

byte[] buffer;
threadData = txt;

String port = getParameter("PORT");
String maxBuf = getParameter("MAXBUF");
try
{
if (port == null)
socket = new DatagramSocket();
else
socket = new DatagramSocket(Integer.parseInt(port));

if (maxBuf == null)
buffer = new byte[512];
else
buffer = new byte[Integer.parseInt(maxBuf)];

packet = new DatagramPacket(buffer, buffer.length);
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Unable to create UDP Socket");
throw new Exception("Message thread could not be created");
}

setDaemon(true);
start();
}

public void shutdown()
{
socket.close();
}

public int getLocalPort()
{
return socket.getLocalPort();
}

public void run()
{
System.out.println("Started Message thread. ThreadData = "
+threadData);
String args[] = {"Started Message Thread " + threadData};
browser.call("alert", args);
boolean stopThread = false;

readLoop:
while (!stopThread)
{
try
{
socket.receive(packet);
String received = new String(packet.getData(),
0,packet.getLength());
processMessage(received);
}
catch (SocketException e)
{
System.out.println("Shutting up shop");
stopThread = true;
continue readLoop;
}
catch (IOException e)
{
e.printStackTrace();
System.out.println("Unable to retrieve UDP message");
}
}

System.out.println("Thread run() unit terminating");
}

public void processMessage(String msgText)
{
int msgType = Integer.parseInt(msgText.substring(0,2));
switch (msgType){
case 1:
chat.append(msgText.substring(2));
break;
case 2:
String args[] = {msgText.substring(2)};
try {browser.call("priceUpdate", args);}
catch (JSException e)
{
System.out.println("Error when calling
JSpriceUpdate()");
}
break;
default:
System.out.println("Unknown rec type"+msgText);
}
}
}

public void init()
{
System.out.println("Initializing. . .");
hostName = getCodeBase().getHost();

chat = new Tier3Talk("Tier3 Messages");
requestFocus();

browser = JSObject.getWindow(this);

if (socketThread == null)
{
try
{
socketThread = new MessageThread("MsgDaemon", "SomeData");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Could not init Tier3Pager");
}
}
}

public void alert(String alertText)
{
String args[] = {alertText};
browser.call("alert", args);
}

public void destroy()
{
if (chat != null)
chat.dispose();

boolean stillDying;

if (socketThread != null){
socketThread.shutdown();
do
{
stillDying = false;
System.out.println("Joining MessageThread");
try {socketThread.join();}
catch (InterruptedException e){
System.out.println("Interrupted Join");
stillDying = true;
}
} while (stillDying);

socketThread = null;
}

System.out.println("Tier3Pager Applet Rundown complete");
super.destroy();
}
}


Anil said:
I have a Javascript program which runs in the browser and has
functions work(), and stop().
It listens to commands from the server to work() and can be
interrupted by the server to stop().
I am using XmlHttpRequest to talk to the server.
So I use http GET to send a command "ready" to the server, which
replies at some point in time by sending "work" which is invoked by
the callback.

As per my understanding, the browser client is single threaded. How to
get the server to interrupt the browser client to say "stop"?

I modified Flanagan's example code here:

var HTTP = {

// This is a list of XMLHttpRequest creation factory functions to try
_factories : [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
],

// When we find a factory that works, store it here
_factory : null,

// Create and return a new XMLHttpRequest object.
//
// The first time we're called, try the list of factory functions
until
// we find one that returns a nonnull value and does not throw an
// exception. Once we find a working factory, remember it for later
use.
//
newRequest : function() {
if (HTTP._factory != null) return HTTP._factory();

for(var i = 0; i < HTTP._factories.length; i++) {
try {
var factory = HTTP._factories;
var request = factory();
if (request != null) {
HTTP._factory = factory;
return request;
}
}
catch(e) {
continue;
}
}

// If we get here, none of the factory candidates succeeded,
// so throw an exception now and for all future calls.
HTTP._factory = function() {
throw new Error("XMLHttpRequest not supported");
}
HTTP._factory(); // Throw an error
},

/**
* Use XMLHttpRequest to fetch the contents of the specified URL
using
* an HTTP GET request. When the response arrives, pass it (as plain
* text) to the specified callback function.
*
* This function does not block and has no return value.
*/
toServer : function(url) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
MyProxy.fromServer(request.responseText);
}
request.open("GET", url, true); // 3rd param implies asynchronous
console.log("sending " + url);
request.send(null);
},

};

.....
// MyProxy
...
fromServer: function(command) {
console.log("fromServer: " + command);
eval(command);
},
thanks,
Anil
 
J

Jorge

(...)
In MyProxy.fromServer(), issue another toServer("url").
Thus there will always be a GET request pending. Since it is
asynchronous, it will return immediately. But the callback will occur
while the client is executing.

so what happens here - what will the browser do - will it simply
block, or a new thread be created? My understanding is stuck here.


There's just a single thread. No new threads will be created, no,
never.
IOW, callback() won't be called until after work() completes.

Consider this work() function: ("stop" being a global) :

function work () {
while (!stop) {
//keep working
}
}

and this callback():

function onReadyStateChangeCallBack () {
if ((xhr.readyState > 3) && (xhr.status === 200)) {
stop= (xhr.responseText === "stop");
}
}

The above code won't work. The reason is that in order to give a
chance to the JS interpreter to call onReadyStateChangeCallBack(),
work() has to complete first, but it won't unless the callBack gets
called. Therefore it's a deadlock.

Consider this work() function instead:

function work () {
doJustALittleBitOfWorkAtATime();
if (!stop) {
setTimeout(arguments.callee, 0)
}
}

Now, work() isn't locked in an endless loop that prevents
onReadyStateChangeCallBack() to be called: every time work() is re-
scheduled to run again a.s.a.p. by the setTimeout(work, 0), the
interpreter has the chance to service any pending callbacks (and it
will do so before returning to "work").

HTH,
 
A

Anil

There's just a single thread. No new threads will be created, no,
never.
IOW, callback() won't be called until after work() completes.

Consider this work() function: ("stop" being a global) :

function work () {
while (!stop) {
//keep working
}

}

and this callback():

function onReadyStateChangeCallBack () {
if ((xhr.readyState > 3) && (xhr.status === 200)) {
stop= (xhr.responseText === "stop");
}

}

The above code won't work. The reason is that in order to give a
chance to the JS interpreter to call onReadyStateChangeCallBack(),
work() has to complete first, but it won't unless the callBack gets
called. Therefore it's a deadlock.

Consider this work() function instead:

function work () {
doJustALittleBitOfWorkAtATime();
if (!stop) {
setTimeout(arguments.callee, 0)
}

}

Now, work() isn't locked in an endless loop that prevents
onReadyStateChangeCallBack() to be called: every time work() is re-
scheduled to run again a.s.a.p. by the setTimeout(work, 0), the
interpreter has the chance to service any pending callbacks (and it
will do so before returning to "work").

HTH,

Thanks Jorge, I shall try your solution doJustALittleBitOfWorkAtATime
();
It seemed to me that an embedded media player in the browser creates a
new thread so that things won't block.
-
Anil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top