当前位置: 首页 > 知识库问答 >
问题:

流媒体服务器在F中发送事件(SSE)#

慕高格
2023-03-14

使用系统将“服务器发送事件(SSE)样式”事件流式传输到F#中的前端的轻量级方式是什么。网Http库?我了解事件流格式(例如,这个PHP示例代码),但我正在寻求一些指导,以便在服务器端F#应用程序(我在.Net Framework 4.8上)中实现流部分。

共有2个答案

太叔京
2023-03-14

以下是最简单的基本代码(操作系统:Windows 10,浏览器:Google Chrome 92.0.4515,.Net Framework 4.8):

F#客户端代码:

module SSE0 =
    open System
    open System.IO
    open System.Net

let pipeUTF8 (data: string) (sink: Stream) : Async<unit> = async {
    let bytes = System.Text.Encoding.UTF8.GetBytes data
    use src = new MemoryStream(bytes)
    do! src.CopyToAsync(sink) |> Async.AwaitTask }

let private (=>=) data sink = pipeUTF8 data sink

type Msg = { id: string; event: string; data: string } with
    member this.send (sink: Stream) : Async<unit> = async { 
        do! (sprintf "id:%s\n" this.id) =>= sink
        do! (sprintf "event:%s\n" this.event) =>= sink
        do! (sprintf "data:%s\n\n" this.data) =>= sink // Only works for single-line data payloads (won't work if eol included)
        do! " \n" =>= sink
        do! Async.Sleep 1000 // only for this basic example
        Console.WriteLine(sprintf "id: %s, event: %s, data: %s" this.id this.event this.data)
        do! sink.FlushAsync() |> Async.AwaitTask}

let sse_count (ctx : HttpListenerContext) : Async<unit> =
    let output  = ctx.Response.OutputStream
    let message (i: int) : Msg = { id = sprintf "id#%02d" i; event = "message"; data = sprintf "data#%02d" i }
    let msgs = seq { for i in 0 .. 59 -> let msg = message i in async { do! msg.send output } }
    msgs |> Async.Sequential |> Async.Ignore

let startServer (url: string) (handler: HttpListenerContext -> Async<unit>) (cts: Threading.CancellationTokenSource) : Threading.CancellationTokenSource =
    let task = async {
        use listener = new HttpListener()
        listener.Prefixes.Add(url)
        listener.Start()
        while true do
            let! context = listener.GetContextAsync() |> Async.AwaitTask
            let resp = context.Response
            [ ("Content-Type", "text/event-stream; charset=utf-8") 
            ; ("Cache-Control", "no-cache")
            ; ("Access-Control-Allow-Origin", "*") ] // or Access-Control-Allow-Origin: http://localhost:3000
            |> List.iter(fun (k, v) -> resp.AddHeader(k, v))
            Async.Start (handler context, cts.Token)
        }
    Async.Start (task, cts.Token)
    cts

[<EntryPoint>]
let main argv =
    let cts' = defaultArg None <| new Threading.CancellationTokenSource()

    Console.WriteLine("Press return to start.")
    Console.ReadLine() |> ignore

    Console.WriteLine("Running...")
    let cts = startServer "http://localhost:8080/events/" sse_count cts'
    
    Console.WriteLine("Press return to exit.")
    Console.ReadLine() |> ignore
    cts.Cancel()
    0

html文档:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>SSE test</title>
    </head>

    <body>
        <button id="btn">Close the connection</button>
        <ul id="msglist"></ul>

        <script>
            var es = new EventSource("http://localhost:8080/events/");
            
            es.onopen = function() {
                console.log("Connection to server opened.");
            };
            
            var msgList = document.getElementById("msglist");
            es.onmessage = function(e) {
                console.log("type: " + e.type +  ", id: " + e.lastEventId + ", data: " + e.data);
                
                var newElement = document.createElement("li");
                newElement.textContent = "type: " + e.type +  ", id: " + e.lastEventId + ", data: " + e.data;
                msgList.appendChild(newElement);
            };

            var btn = document.getElementById("btn");
            btn.onclick = function() {
                console.log("Connection closed");
                es.close();
            }
            
            es.onerror = function(e) {
                console.log("Error found.");
            };
        </script>
    </body>

以下资源有助于实现这一目标:

https://github.com/mdn/dom-examples/tree/master/server-sent-events

https://github.com/haf/FSharp.EventSource

璩俊雅
2023-03-14

你可以用Suave。下面的示例每秒钟发送一条消息(使用SSE)我没有在. net 47中尝试过(我在. net 5中尝试过),但它应该可以工作。

open Suave
open Suave.Sockets
open Suave.Sockets.Control
open Suave.EventSource
open Suave.Operators
open Suave.Filters

let write i out =
    socket {
      let msg = { id = string i; data = string i; ``type`` = None }
      do! msg |> send out
      return! SocketOp.ofAsync (Async.Sleep 1000)
    }
    
    
let app =
  choose [
          GET  >=> request (fun _ -> 
            handShake (fun out ->
              socket {
                let actions  = 
                    Seq.initInfinite (fun n -> n + 1)
                    |> Seq.map (fun i -> write i out)

                for a in actions do
                    do! a
                   
                return out
              }))
    
    
[<EntryPoint>]
let main _ =
     startWebServer defaultConfig app
     0
 类似资料:
  • 我有一个后端服务器,它将事件作为服务器发送的事件发送给客户端。我还没有找到一个好的库来在Android上处理这项技术,所以我一直在使用一种回退方法,定期检查服务器(通过访问事件endpoint)中的新事件。 后台服务每10秒执行一次。不用说,这不是最好的方法。如果没有任何开源库可用于此场景,那么在内存使用和电池消耗方面,定期检查服务器后端是否有新事件的最佳方法是什么?与在Android中管理开放式

  • 概述 客户端代码 概述 建立连接 open事件 message事件 error事件 自定义事件 close方法 数据格式 概述 data:数据栏 id:数据标识符 event栏:自定义信息类型 retry:最大间隔时间 服务器代码 参考链接 概述 传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据。比如,每当收到新的电子邮件,服务器就向浏览器发送一个“

  • 概述 客户端代码 概述 建立连接 open事件 message事件 error事件 自定义事件 close方法 数据格式 概述 data:数据栏 id:数据标识符 event栏:自定义信息类型 retry:最大间隔时间 服务器代码 参考链接 概述 传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据。比如,每当收到新的电子邮件,服务器就向浏览器发送一个“

  • 我试图让服务器发送的事件与Mozilla Firefox一起工作。给定一个Spring Boot的网络服务 使用Chrome浏览器或Edge(始终是最新版本)可以正常工作。我可以在网络分析器选项卡中看到未完成的请求,并且每秒都会显示一个新的时间戳。 然而,当我使用Firefox(84.0.2或更早版本)时,请求也会显示在网络选项卡中,但不会显示响应头或流数据。当我终止Spring后端时,Firef

  • 当前,我已经在Express API上为服务器发送的事件设置了一个endpoint,当我从cmd调用endpoint时,使用http://localhost:3000/v1/devices,我得到了流,但当我使用新事件源从Vue应用程序调用endpoint时('http://localhost:3000/v1/devices“)但它只会继续加载,而不会流式传输任何数据。是否有我目前没有做的事情要做

  • 我有一个ASP。net core 3.1服务器项目,它有一个非常简单的API发送单向服务器发送事件(SSE),如下所示: 现在,我想通过C#UWP客户端接收这些事件。很遗憾,我只收到第一个事件: 如何在UWP中创建行为以始终监听该连接并接收我可以进一步处理的事件?