C#winform捕获服务器如何发送事件

C#winform捕获服务器如何发送事件,c#,winforms,server-sent-events,C#,Winforms,Server Sent Events,如何将桌面客户端订阅到服务器发送事件。因此,无论何时从服务器端推送某些内容,我的winform应用程序都应该能够捕获并显示该消息。只是一个示例代码。我得到了一些相关链接 这里我重点介绍一个示例代码,其中javascript端和ASP.NETMVC通过服务器发送事件相互对话 <input type="text" id="userid" placeholder="UserID" /><br /> <input

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

这里我重点介绍一个示例代码,其中javascript端和ASP.NETMVC通过服务器发送事件相互对话

<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);
        }
    }
}

var es=新事件源('/home/message'); e.onmessage=函数(e){ 控制台日志(如数据); }; es.onerror=函数(){ log(参数); }; $(函数(){ $('#ping')。在('click',函数(){ $.post(“/home/ping”{ UserID:$('#UserID').val()| 0 }); }); }); 使用制度; 使用System.Collections.Concurrent; 使用系统线程; 使用System.Web.Mvc; 使用Newtonsoft.Json; 命名空间EventSourceTest2.Controllers{ 公共类数据{ public int UserID{get;set;} 公共日期时间日期{get;set;}=DateTime.Now; } 公共类HomeController:控制器{ 公共行动结果索引(){ 返回视图(); } 静态ConcurrentQueue pings=新ConcurrentQueue(); 公共无效Ping(int userID){ 排队(新的PingData{UserID=UserID}); } 公共无效消息(){ Response.ContentType=“文本/事件流”; 做{ PingData下一步; if(pings.TryDequeue(out nextPing)){ Write(“数据:”+JsonConvert.SerializeObject(nextPing,Formatting.None)+“\n\n”); } Response.Flush(); 睡眠(1000); }虽然(正确); } } }
我想通过服务器发送事件在web api和winform之间进行通信。所以,请告诉我winform如何与订阅服务器发送的事件进行通信


如果再加上一个小例子……将是很大的帮助。谢谢

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

变量:

  • 主持人:127.0.0.1
  • 港口:5000
  • 方法:获取
  • Url:/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
    的其他解决方案。我添加了更多代码来解析
    文本事件流
    消息。您可以阅读更多内容以进行改进

    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内核的服务器部件(与请求的用户显示的部件非常相似)

    。。。
    私有静态ConcurrentQueue pings=新ConcurrentQueue();
    [HttpGet]
    公共无效Ping(int userID)
    {
    排队(新的PingData{UserID=UserID});
    }
    [HttpGet]
    公共无效消息()
    {
    Response.ContentType=“文本/事件流”;
    WriteAsync($“:Hello{Request.Host}\n”);
    区间常数=1000;
    做
    {
    if(pings.TryDequeue(out var nextPing))
    {
    WriteAsync($“事件:Ping\n”);
    WriteAsync($“重试:{intervalMs}\n”);
    WriteAsync($“id:{DateTime.Now.Ticks}\n”);
    WriteAsync($“数据:{JsonConvert.SerializeObject(nextPing)}\n\n”);
    }
    线程。睡眠(间隔);
    }while(Response.Body.CanWrite);
    }
    公共类数据
    {
    public int UserID{get;set;}
    公共日期时间日期{get;set;}=DateTime.Now;
    }
    ...
    
    也许您想检查
    信号器
    ()我知道我们可以使用信号器,但我很好奇,是否无法从桌面应用程序中侦听服务器发送事件。非常感谢您的示例代码,但推送消息的位置缺少服务器部分。有一件事不清楚为什么要使用TcpClient而不是httpclient?@Indi\u Rain我已经更新了我的答案。请查看。非常感谢您的第二套代码。我不知道是谁给了我负面分数,我相信我的问题不值得负面分数。你能把这个负号去掉吗?我投了你一票赞成第二套代码。谢谢,我正在使用.NETV4.5.2。作为与.net v4.5.2兼容的结果,我需要在代码中更改哪些区域。@Indi\u Rain除了
    响应.Body
    之外,所有内容都应该相同。。您需要将其更改为
    响应。编写
    ...
    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;
    }
    ...