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

如何使用WebSocketSharp发出类似同步的websocket请求?

谢诚
2023-03-14

我在做C#项目。我想通过websocketsharp库以某种同步的方式发送API请求。

我一直试着按照以下方式去做:

>

  • 在发送任何WS请求之前,我们使用唯一的ID创建新的SynchronousRequest()对象,并将新创建的对象添加到某种类型的等待列表中

    我们在响应中发送WS请求,向有效负载添加唯一的ID-服务器将返回相同的ID。

    我们开始等待事件发出信号(一旦我们收到响应就会发出信号)

    在响应处理程序上:

    问题是第3步,一旦我在事件上使用WaitOne(),整个websocket客户机就挂起了,并且不会收到进一步的响应--导致完全死锁。

    我如何在单独的线程中执行某种WaitOne()调用,或者可能有完全更好的解决方案来解决我的问题,从而使整个客户端不会挂起,并且我们匹配上下文?

    public class SynchronousRequest
    {
        public long requestId;
        public ManualResetEvent resetEvent = new ManualResetEvent(false);
        public dynamic response;
    
        public SynchronousRequest()
        {
            var random = new Random();
            requestId = random.Next();
        }
    
    }
    
    
    public class APIWebSocket: BaseAPIWebSocket
    {
    
    
        private List<SynchronousRequest> waitingSyncRequests = new List<SynchronousRequest>();
    
    
        public APIWebSocket()
        {
            ws = new WebSocket("wss://www.someserver.com");
    
            registerConnectionEvents(); //Registers onOpen(), onMessage() handlers and similar
        }
    
    
    
        public void SendSyncTest()
        {
            var sr = new SynchronousRequest();
            waitingSyncRequests.Add(sr);
    
            //some data to send
            var msg = new
            {
                jsonrpc = "2.0",
                method = "public/ticker",
                id = sr.requestId, //Response should contain the same ID
                @params = new
                {
                    instrument_name = "ETH"
                }
            };
    
            ws.Send(JsonConvert.SerializeObject(msg));
    
            //Below WaitOne() causes the entire websocket connection/thread to block
            // No further messages will be received by HandleMessage() once we call WaitOne()
    
            sr.resetEvent.WaitOne(); //Wait until we receive notification that response has been received
    
            //do some processing on response here... 
    
           //Synchronous request completed, remove it from list
           waitingSyncRequests.Remove(sr);
        }
    
    
    
    
        protected override void OnReceivedMessage(System.Object sender, WebSocketSharp.MessageEventArgs e)
        {
            dynamic message = JsonConvert.DeserializeObject(e.Data);
    
            if (message.id != null )
            {
                //Find a resetEvent for given message.id
                var matchingSyncRequest = waitingSyncRequests.First(r => r.requestId == message.id);
                if (matchingSyncRequest != null)
                {
                    matchingSyncRequest.response = message;
                    matchingSyncRequest.resetEvent.Set(); //Notify that response has been received
                }
            }
    
        }
    
    }
    
  • 共有1个答案

    陆晓博
    2023-03-14

    根据我的理解,对于将在将来某个时间设置的某个事件,您需要await。您可以考虑使用TaskCompletionSource

    >

  • 1.每当您发送需要其结果的消息时,您可以创建TaskCompletionSource(tcs),将其添加到Dictionary并通过套接字发送消息。

    2.您在TCS等待tcs.tasktcs.task.result

    3.在另一个线程/任务上,您可以处理传入的响应(在您的情况下,您有您的received message Handler。每当您接收到目标类型id的响应时,您可以根据id从字典中提取TCSTask.setResult(response)。这时,调用方(您正在等待的调用方)将被解除阻止。

     public class Message
     {
       public string ID{get;set;}
     }
     public class Response
     {
         public string Id{get;set;}
     }
    
     public void MyClass
     {
        private ConcurrentDictionary<string,TaskCompletionSource<Response>>map=new ConcurrentDictionary<string,TaskCompletionSource<Response>>();
        private Websocket socket;
        public void SomeEventTrigger()
        {
            var msg=new Message{ Id="somespecialID" };
            var tcs=new TaskCompletionSource<Response>();
            ws.Send(JsonConvert.SerializeObject(msg));
            if(!this.map.TryAdd(msg.Id,tcs))
            {
               return;
            }
            var result=tcs.Result; //this gets blocked until you use `tcs.SetResult`- >  that would happen in your OnReceivedMessage  
            this.map.TryRemove(msg.Id,out TaskCompletionSource<Response>resp);
    
        }
        protected override void OnReceivedMessage(System.Object sender, WebSocketSharp.MessageEventArgs e)
        {
             Response message = JsonConvert.DeserializeObject<Response>(e.Data);
    
             if (message.id != null )
             {
    
                 if(this.map.TryGetValue(message.id),out TaskCompletionSource<Response> tcs)
                 {
                    tcs.SetResult(message); //this unblocks the method that wrote the message to the socket   (above)
                 }
    
    
             }
          }
       }
    

    p.s您需要确保在正确的TCS上使用Task.SetResult,以便对SomeEventTrigger方法的正确调用停止等待。

  •  类似资料:
    • 问题内容: 我正在尝试使用进行同步请求。我查看了Stackoverflow并发现了这个问题:使异步alamofire请求成为sync 我看到接受的答案用于使请求同步,但是我无法使其正常工作。这是我的简化代码: 有了这段代码,我在尝试制作时会出错。我收到的错误如下: 无法调用非功能类型“布尔”的值 我尝试使用许多使用完成的示例来同步获取值(因为我需要先检索数据才能将其显示在表上,并同时获取该表的行数

    • Spring留档声明,即使要执行同步超文本传输协议调用,我们也必须从RestTemboard切换到。 目前,我有以下代码: 当然,我可以在这里使用CountdownLatch,但它看起来像是API滥用。 如何执行同步请求?

    • 问题内容: 如何使Ajax请求同步? 我有一个需要提交的表格。但是只有在用户输入正确的密码时才需要提交。 这是表单代码: 用于发送和检查密码的jQuery代码是这样的: 但是,无论ajax请求返回的值如何,该表单始终提交。我检查了其他所有内容。输入正确的密码后,arr的值将显示为“成功”,反之亦然。 如何使此请求同步?据我可以调试,请求是异步的,因此在请求完成之前先提交表单。 checkpass.

    • 问题内容: 我读了一些将jsons发布到服务器的示例。 有人说: OkHttp是Java提供的HttpUrlConnection接口的实现。它提供用于编写​​内容的输入流,并且不知道(或不在乎)内容的格式。 现在,我想用名称和密码的参数对URL进行常规发布。 这意味着我需要自己将名称和值对编码为流? 问题答案: 当前接受的答案已过期。现在,如果您想创建一个发布请求并向其中添加参数,则应该使用Mul

    • 我阅读了一些将jsons发布到服务器的示例。 有人说: OkHttp是Java提供的HttpUrlConnection接口的实现。它提供了一个用于写入内容的输入流,而不知道(或关心)该内容是什么格式。 现在我想用name和password参数对URL做一个普通的post。 这意味着我需要自己将名称和值对编码成流?

    • 本文向大家介绍vue如何使用async、await实现同步请求,包括了vue如何使用async、await实现同步请求的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了vue如何使用async、await实现同步请求,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 以下是vue method的demo: 其中方法需要用async修饰, 然