HTML5 Server-Sent Events prototyping - ambiguous error and repeated polling?

前端 未结 8 1487
野趣味
野趣味 2020-12-03 14:44

I\'m trying to get to grips with Server-Side Events as they fit my requirements perfectly and seem like they should be simple to implement, however I can\'t get past a vague

相关标签:
8条回答
  • 2020-12-03 15:05

    I'm trying the same thing. With varying degrees of success.

    1. Had the same problem with Firefox, running the same js code as mentioned. Using the Nginx server and some PHP that exited(ie no continual loop), I could get messages back to a "Request" from firefox only once the PHP exited. Run the PHP as a script in PHP.exe and all is good on the concole, stings are printed when flushed. However, Nginx doesn't send the data until the PHP has completed. Tried adding extra \r\n\r\n and flush() or ob_flush() did not help. There is no pushing of data, as shown in Wireshark logs, just a delayed response packet to the GET.

    Read that I need a "push" module for Nginx that requires a re-build from source.

    So this is definitely an Nginx problem.

    1. Using a socket in 'C' I was able to push data to Firefox as expected, and the socket was kept open, and no messages were missed. However this has the disadvantage that I need to server the page.html and the events/stream from the same socket or firefox will not connect due to Cross Site Url problems. There are some ways around this in certain situations, but not for a iframe in a menu system. This approach did prove the point that the SSE does work with firefox and there are pushed packets in the wireshark log. Where option 1 only had request/reply packets.

    All this said, I still don't have a solution. I've tried to remove the buffering on the PHP and Nginx. But still nothing until PHP finishes. Tried different header options, eg chunks didn't help either. I don't feel like writing a full blown http server in 'C' but this seems to be the only option that is working for me at the moment. I'm about to try Apache, but most write ups suggest that this is worse than Nginx at this job.

    0 讨论(0)
  • 2020-12-03 15:08

    According to the Spec, the 3 second reconnection is by design when the connection is closed. PHP with a loop should theoretically stop this but the PHP script will be running indefinitely and wasting resources. You should try to avoid using apache and php for SSE because of this issue.

    The standard http response should close a connection once the response is sent. You can change this with the header "connection: keep-alive" which should tell the browser that the connection is meant to stay open although this can cause problems if you're using proxies.

    node.js or something similar is a better engine to use for SSE rather than apache/php and since it's basically JavaScript, its pretty easy to get to grips with.

    0 讨论(0)
  • 2020-12-03 15:14

    Server Sent Event as name suggests the data should be traveling from server to client if it has to reconnect every three seconds to retrieve data from server then it is no different than other polling mechanisms.The purpose of SSE is to alert client as soon as there is new data which client is unaware of.Since server closes connection even if header is keep-alive there is no other way than to run php script in infinite loop but with considerable thread sleep to prevent burden on server.Till now i don't see any other way out and its better than spamming server every 3 seconds for new data.

    0 讨论(0)
  • 2020-12-03 15:16

    There is no actual issue with the code, that I can see. The answer selected as correct, is then, incorrect.

    This sums up the behavior mentioned in the question (http://www.w3.org/TR/2009/WD-html5-20090212/comms.html):

    "If such a resource (with the correct MIME type) completes loading (i.e. the entire HTTP response body is received or the connection itself closes), the user agent should request the event source resource again after a delay equal to the reconnection time of the event source. This doesn't apply for the error cases that are listed below."

    The problem lies with the stream. I've successfully kept a single EventStream open before in perl; just send the appropriate HTTP headers, and start sending stream data; never shutdown the stream server side. The issue is that it seems most HTTP libraries attempt to close the stream after its been opened. This will cause the client to attempt to reconnect to the server, which is fully standard compliant.

    This means that it will appear that the problem is solved by running a while loop, for a couple of reasons:

    A) The code will continue to send data, as if it were pushing out a large file B) The code (php server) will never have the chance to attempt to close the connection

    However, the problem here is obvious: to keep the stream alive, a constant stream of data must be sent. This results in wasteful utilization of resources, and negates any benefits the SSE stream is supposed to provide.

    I'm not enough of a php guru to know, but I'd imagine that something in the php server/later in the code is prematurely closing the stream; I had to manipulate the stream at Socket level with Perl to keep it open, since HTTP::Response was closing the connection, and causing the client browser to attempt to re-open the connection. In Mojolicious (another Perl web framework), this can be done by opening a Stream object and setting the timeout to zero, so that the stream never times out.

    So, the proper solution here is not to use a while loop; it is to call the appropriate php functions for opening, and keeping open, a php stream.

    0 讨论(0)
  • 2020-12-03 15:19

    The problem is not a server side issue, this all happens on the client and is part of the spec (I know it sounds weird).

    http://dev.w3.org/html5/eventsource/

    "When a user agent is to reestablish the connection, the user agent must run the following steps. These steps are run asynchronously, not as part of a task. (The tasks that it queues, of course, are run like normal tasks and not asynchronously.)"

    1. Queue a task to run the following steps:
      1. If the readyState attribute is set to CLOSED, abort the task.
      2. Set the readyState attribute to CONNECTING.
      3. Fire a simple event named error at the EventSource object.

    I can't see any need to have an error here, so I have modified your Init function to filter out the error event fired whilst connecting.

    function init() {
                    var CONNECTING = 0;
                    var source;
                    if (!!window.EventSource) {
                        source = new EventSource('events.php');
                        source.addEventListener('message', function (e) {
                            document.getElementById('output').innerHTML += e.data + '
    '; }, false); source.addEventListener('open', function (e) { document.getElementById('output').innerHTML += 'connection opened
    '; }, false); source.addEventListener('error', function (e) { if (source.readyState != CONNECTING) { document.getElementById('output').innerHTML += 'error
    '; } }, false); } else { alert("Browser doesn't support Server-Sent Events"); } }
    0 讨论(0)
  • 2020-12-03 15:25

    The problem is your php.

    With the way your php script is written, only one message is sent per execution. That's how it works if you access the php file directly, and that's how it works if you access the file with an EventSource. So in order to make your php script send multiple messages, you need a loop.

    <?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    
    function sendMsg($id, $msg) {
      echo "id: $id" . PHP_EOL;
      echo "data: $msg" . PHP_EOL;
      echo PHP_EOL;
      ob_flush();
      flush();
    }
    while(true) {
      $serverTime = time();
      sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
      sleep(1);
    }
    ?>
    

    I have altered your code to include an infinite loop that waits 1 second after every message sent (following an example found here: Using server-sent events).

    This type of loop is what I'm currently using and it eliminated the constant connection drop and reconnect every 3 seconds. However (and I've only tested this in chrome), the connections are now only kept alive for 30 seconds. I will be continuing to figure out why this is the case and I'll post a solution when I find one, but until then this should at least get you closer to your goal.

    Hope that helps,

    Edit:

    In order to keep the connection open for ridiculously long times with php, you need to set the max_execution_time (Thanks to tomfumb for this). This can be accomplished in at least three ways:

    1. If you can alter your php.ini, change the value for "max_execution_time." This will allow all of your scripts to run for the time you specify though.
    2. In the script you wish to run for a long time, use the function ini_set(key, value), where key is 'max_execution_time' and value is the time in seconds you wish your script to run for.
    3. In the script you wish to run for a long time, use the function set_time_limit(n) where n is the number of seconds that you wish your script to run.
    0 讨论(0)
提交回复
热议问题