【问题标题】:C# How a winform capture server sent eventC# winform 捕获服务器如何发送事件
【发布时间】:2020-07-11 21:06:28
【问题描述】:

如何将桌面客户端订阅到服务器发送的事件。因此,每当从服务器端推送某些内容时,我的 winform 应用程序应该能够捕获并显示该消息。只是在寻找示例代码。我有一些相关的链接。

这里我强调一个示例代码,其中 javascript 端和 ASP.Net MVC 通过服务器发送的事件相互交谈。

<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />

<script>
    var es = new EventSource('/home/message');
    es.onmessage = function (e) {
        console.log(e.data);
    };
    es.onerror = function () {
        console.log(arguments);
    };

    $(function () {
        $('#ping').on('click', function () {
            $.post('/home/ping', {
                UserID: $('#userid').val() || 0
            });
        });
    });
</script>

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace EventSourceTest2.Controllers {
    public class PingData {
        public int UserID { get; set; }
        public DateTime Date { get; set; } = DateTime.Now;
    }

    public class HomeController : Controller {
        public ActionResult Index() {
            return View();
        }

        static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();

        public void Ping(int userID) {
            pings.Enqueue(new PingData { UserID = userID });
        }

        public void Message() {
            Response.ContentType = "text/event-stream";
            do {
                PingData nextPing;
                if (pings.TryDequeue(out nextPing)) {
                    Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
                }
                Response.Flush();
                Thread.Sleep(1000);
            } while (true);
        }
    }
}

我想通过服务器发送的事件在 web api 和 winform 之间进行通信。那么请告诉我一个winform如何与服务器发送的事件进行通信?

如果要添加任何小示例....将有很大帮助。谢谢

【问题讨论】:

标签: c# winforms server-sent-events


【解决方案1】:

我使用TcpClient 编写了一个基本实现。

变量:

  1. 主机:127.0.0.1
  2. 端口:5000
  3. 方法:GET
  4. 网址:/home/message

听众

private static async Task ConnectEventStreamAsync(CancellationToken token)
{
    var client = new TcpClient();
    try
    {
        await client.ConnectAsync("127.0.0.1", 5000);
        if (!client.Connected)
            throw new Exception("Unable to connect the host");

        var encoding = Encoding.UTF8;
        var stream = client.GetStream();
        var connectBytes = encoding.GetBytes(
            "GET /home/message HTTP/1.1\r\n" +
            "Host: 127.0.0.1\r\n" +
            "Content-Length: 0\r\n\r\n"
        );
        await stream.WriteAsync(connectBytes, 0, connectBytes.Length, token);
        var buffer = new byte[4096];
        while (!token.IsCancellationRequested)
        {
            var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
            if (readCount > 0)
            {
                var message = encoding.GetString(buffer, 0, readCount);
                using (var stringReader = new StringReader(message))
                {
                    string line;
                    while ((line = await stringReader.ReadLineAsync()) != null)
                    {
                        //--try to read the next chunk
                        if (!int.TryParse(line, NumberStyles.HexNumber, null, out var bytes))
                            continue;
                        var data = await stringReader.ReadLineAsync();
                        Console.WriteLine($">>New Event ({bytes} bytes)\r\n{data}");
                    }
                }
            }
            await Task.Delay(500, token);
        }
    }
    finally
    {
        client.Dispose();
    }
}

编辑:这是使用HttpClient 的其他解决方案。我添加了更多代码来解析text-eventstream 消息。您可以阅读更多here 进行改进。

private static async Task ConnectEventStreamAsync(CancellationToken token)
{
    var client = new HttpClient
    {
        Timeout = Timeout.InfiniteTimeSpan
    };
    try
    {
        var response = await client.GetAsync(
            "http://127.0.0.1:5000/home/message",
            HttpCompletionOption.ResponseHeadersRead,
            token
        );
        if (!response.IsSuccessStatusCode)
            throw new Exception("Unable to connect the stream");

        var isTextEventStream = response.Content.Headers.ContentType.MediaType == "text/event-stream";

        if (!isTextEventStream)
            throw new InvalidOperationException("Invalid resource content type");

        var stream = await response.Content.ReadAsStreamAsync();
        var buffer = new byte[4096];
        while (!token.IsCancellationRequested)
        {
            var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
            if (readCount > 0)
            {
                var data = Encoding.UTF8.GetString(buffer, 0, readCount);
                await ParseDataAsync(data);
            }

            await Task.Delay(500, token);
        }
    }
    finally
    {
        client.Dispose();
    }

    async Task ParseDataAsync(string data)
    {
        using (var stringReader = new StringReader(data))
        {
            string line;
            while ((line = await stringReader.ReadLineAsync()) != null)
            {
                if (line.StartsWith("event:"))
                {
                    var eventText = line.Substring("event:".Length);
                    Console.WriteLine($">>Event ({eventText.Length} bytes)\r\n{eventText}");
                    continue;
                }

                if (line.StartsWith("data:"))
                {
                    var dataText = line.Substring("data:".Length);
                    Console.WriteLine($">>Data ({dataText.Length} bytes)\r\n{dataText}");
                    continue;
                }

                if (line.StartsWith("id:"))
                {
                    var eventId = line.Substring("id:".Length);
                    Console.WriteLine($">>Event ID ({eventId.Length} bytes)\r\n{eventId}");
                    continue;
                }

                if (line.StartsWith("retry:"))
                {
                    var retryValue = line.Substring("retry:".Length);
                    Console.WriteLine($">>Retry ({retryValue.Length} bytes)\r\n{retryValue}");
                    continue;
                }

                if (line.StartsWith(":"))
                {
                    Console.WriteLine($">>Comment ({line.Length - 1} bytes)\r\n{line.Substring(1)}");
                }
            }
        }
    }
}

使用 Asp.Net Core 的服务器部分(与询问的用户显示的非常相似)。

...
private static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();

[HttpGet]
public void Ping(int userID)
{
    pings.Enqueue(new PingData { UserID = userID });
}

[HttpGet]
public void Message()
{
    Response.ContentType = "text/event-stream";
    Response.WriteAsync($":Hello {Request.Host}\n");

    const int intervalMs = 1000;
    do
    {
        if (pings.TryDequeue(out var nextPing))
        {
            Response.WriteAsync($"event:Ping\n");
            Response.WriteAsync($"retry:{intervalMs}\n");
            Response.WriteAsync($"id:{DateTime.Now.Ticks}\n");
            Response.WriteAsync($"data:{JsonConvert.SerializeObject(nextPing)}\n\n");
        }

        Thread.Sleep(intervalMs);
    } while (Response.Body.CanWrite);
}

public class PingData
{
    public int UserID { get; set; }
    public DateTime Date { get; set; } = DateTime.Now;
}
...

【讨论】:

  • 非常感谢您提供的示例代码,但推送消息的位置缺少服务器部分。一件事不清楚你为什么使用 TcpClient 而不是 httpclient?
  • @Indi_Rain 我已经更新了我的答案。请检查一下。
  • 非常感谢您提供的第二组代码。我不知道谁给了我负分,我相信我的问题不值得负分。你能把这个负面标记去掉吗?我对第二组代码投了赞成票。谢谢
  • 我正在使用 .net v4.5.2。我需要在代码中更改哪些区域,因此它应该与 .net v4.5.2 兼容。
  • @Indi_Rain 除了Response.Body.. 你需要把它改成Response.Write
猜你喜欢
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 2015-10-24
  • 2018-09-24
  • 1970-01-01
  • 2015-09-24
  • 2015-06-16
  • 2013-08-20
相关资源
最近更新 更多