tags:

views:

74

answers:

3

There is a flash movie which is using flash.net.Socket to connect to a server. But there could be a situation when the server is not running, hence nothing is listening on the port socket is connecting to.

When I do "telnet hostname port" I get a fast connection refused error. But flash.net.Socket does not invoke any event (see below), but silently waits for socket timeout. For me it is important to reduce time needed to detect non-existing server as much as possible to be able to reconnect to another server in the cluster.

I've tried the following events, but to no avail:

  • close
  • connect
  • ioError
  • securityError
  • socketData

None of these is invoked in such situation.

Is there a way to detect that TCP connection has been refused using flash.net.Socket?

A: 

What about catch connection errors with add <img src='http://target:port/' onerror='noconnect' /> ?

datacompboy
Is onerror supported in all browsers? Is it fired when server returns nothing?
gleber
Yes, best is server can reply with empty answer on GET / -- then, onsuccess called.http://www.w3schools.com/jsref/event_img_onerror.asp -- looks like onerror supported in all browsers. at least in opera/iceweasel/ie it works for me.
datacompboy
Thanks for a suggestion, but this looks to be only a workaround. I'll wait a bit more. Maybe there a real solution exists
gleber
A: 

what about this one:

package {
    import flash.display.Sprite;

    public class SocketExample extends Sprite {

        public function SocketExample() {
            var socket:CustomSocket = new CustomSocket("localhost", 80);
        }
    }
}

import flash.errors.*;
import flash.events.*;
import flash.net.Socket;

class CustomSocket extends Socket {
    private var response:String;

    public function CustomSocket(host:String = null, port:uint = 0) {
        super(host, port);
        this.timeout=100;
        configureListeners();
    }

    private function configureListeners():void {
        addEventListener(Event.CLOSE, closeHandler);
        addEventListener(Event.CONNECT, connectHandler);
        addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
        addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
    }

    private function writeln(str:String):void {
        str += "\n";
        try {
            writeUTFBytes(str);
        }
        catch(e:IOError) {
            trace(e);
        }
    }

    private function sendRequest():void {
        trace("sendRequest");
        response = "";
        writeln("GET /");
        flush();
    }

    private function readResponse():void {
        var str:String = readUTFBytes(bytesAvailable);
        response += str;
    }

    private function closeHandler(event:Event):void {
        trace("closeHandler: " + event);
        trace(response.toString());
    }

    private function connectHandler(event:Event):void {
        trace("connectHandler: " + event);
        sendRequest();
    }

    private function ioErrorHandler(event:IOErrorEvent):void {
        trace("ioErrorHandler: " + event);
    }

    private function securityErrorHandler(event:SecurityErrorEvent):void {
        trace("securityErrorHandler: " + event);
    }

    private function socketDataHandler(event:ProgressEvent):void {
        trace("socketDataHandler: " + event);
        readResponse();
    }
}
Eugene
It is based on a short timeout, isn't it? It is unsuitable for me, since sometimes server can be heavily loaded and timeout is too short
gleber
what if we will modify server architecture? Write external server checker, which will check server running status, and will response about cloud availability. What do you think? Like a SLA style
Eugene
This example was copied verbatim from the [Adobe doc page](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/Socket.html), and the 100ms timeout was added. The timeout property is Flash 10+ only, and it won't help in this case anyway. The problem with the example is actually the same one I describe in my answer above: the listeners don't get added until *after* the constructor initiates the connection attempt -- so quick connect errors can be missed completely. You can avoid this problem by using the no-args constructor, followed by a call to `connect(host, port)`.
Lee
@Lee if you read my previous comment, I proposed to solve it via changing architecture level, because in bigger scale it have bigger sense. Also if yo figured out how to solve this in your way, you are welcome for sharing it. Thank you. :)
Eugene
A: 

The key is to listen to the Socket object for the event IOErrorEvent.IO_ERROR. In the case you're referring to, where the connection is denied immediately, this event will be triggered immediately. In fact, it can be triggered so quickly that you will miss it unless you do things in the proper order (as shown here):

// first create the unconnected socket, and add the listener
// you *must* add the listener *before* connecting the socket
//
var mySocket = new Socket();
mySocket.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
    // called when error occurrs
    trace("ioErrorHandler: " + event);
});
mySocket.addEventListener(flash.events.Event.CONNECT, function(event:Event):void {
    // handle a successful connection here
    //...
});

// now initiate the connection to port 80 on "server.example.com"   
mySocket.connect( 'server.example.com', 80 );

NOTE: this approach is not based on a timeout. If the server returns a definite "connection refused" response then the IO Error event will happen immediately when that response is received.

The timeout only applies when the server returns nothing for an extended period of time. This can happen with certain server/firewall configurations that actually drop packets silently rather than returning "connection refused". When this happens, the client (in this case Flash), will sit waiting until the timeout expires. When the timeout does finally expire, it will result in an IOErrorEvent.IO_ERROR event as you would expect.

The default socket timeout is platform-dependent but it's always fairly long (~20-30 seconds or more). The Socket.timeout property is introduced in Flash Player version 10 and Air 1.0. As far as I know, there is no way to adjust the the socket timeout using ActionScript prior to Flash Player 10.

good luck!

--- EDIT: if it's still not working, read on ---

You should also be familiar with the way the flash player looks for (and requires) a socket policy file. Missing, or broken Socket Policy Files can sometime produce behavior that looks like a socket waiting on timeout. Quoting from this Adobe Document:

Access to socket and XML socket connections is disabled by default, even if the socket you are connecting to is in the same domain as the SWF file. You can permit socket-level access by serving a socket policy file from any of the following locations:

  • Port 843 (the location of the master policy file)
  • The same port as the main socket connection
  • A different port than the main socket connection

This means that, unless you have previously loaded a policy file using Security.loadPolicyFile(), when you initially try to connect your socket to the server, flash will first try to resolve a policy file. In some cases this can result in strange connection delays or other unexpected behavior. If you think you may be encountering this problem, you should start by adding a listener for flash.events.SecurityErrorEvent.SECURITY_ERROR.

Lee