与 Websockets 同步请求

Posted

技术标签:

【中文标题】与 Websockets 同步请求【英文标题】:Synchronous request with Websockets 【发布时间】:2012-11-16 12:53:19 【问题描述】:

我可以在 websockets 上从服务器发送和接收消息。我需要编写一个函数,它将数据发送到服务器并等待服务器的响应,然后将其作为函数的结果返回。

发送:

ws.send('my_message_to_server');

接收(这是一个事件):

ws.bind('message', function(message) 
console.log(message);
);

我的功能:

function request(message)
ws.send(message);
    //How wait for receive???
    return response;

【问题讨论】:

你不能为异步的东西创建一个同步函数。忙着等待很糟糕,javascript 不支持它。你为什么要这样做?看看承诺。 @Bergi 实际上你可以。看看我下面的答案。只需创建一个简单的队列,在该示例中无需忙于等待 :) @RonanDejhero:不,您的函数不是同步的。它确实使用异步回调,因为每个套接字侦听器都需要。 【参考方案1】:

前段时间我在一些在线游戏中使用了一种巧妙的方法,但是您还需要在服务器端进行更改!

第一个函数发送数据

var socketQueueId = 0;
var socketQueue = ;

function sendData(data, onReturnFunction)
    socketQueueId++;
    if (typeof(returnFunc) == 'function')
        // the 'i_' prefix is a good way to force string indices, believe me you'll want that in case your server side doesn't care and mixes both like php might do
        socketQueue['i_'+socketQueueId] = onReturnFunction;
    
    jsonData = JSON.stringify('cmd_id':socketQueueId, 'json_data':data);
    try
        webSocket.send(jsonData);
        console.log('Sent');
    catch(e)
        console.log('Sending failed ... .disconnected failed');
    

那么在服务器端,在处理请求的时候,你应该把cmd_id和响应一起发回给客户端

webSocket.onmessage = function(e) 

    try
        data = JSON.parse(e.data);
    catch(er)
        console.log('socket parse error: '+e.data);
    

    if (typeof(data['cmd_id']) != 'undefined' && typeof(socketQueue['i_'+data['cmd_id']]) == 'function')
        execFunc = socketQueue['i_'+data['cmd_id']];
        execFunc(data['result']);
        delete socketQueue['i_'+data['cmd_id']]; // to free up memory.. and it is IMPORTANT thanks  Le Droid for the reminder
        return;
    else
        socketRecieveData(e.data);
    

并创建一个函数来处理所有其他类型的返回:

socketRecieveData(data)
    //whatever processing you might need

所以现在只要你想为服务器发送一些数据并等待你简单的特定数据的响应:

sendData('man whats 1+1', function(data)console.log('server response:');console.log(data););

【讨论】:

哇,真是个好主意!我花了一些时间来理解,但是将回调发送到服务器并将其 jsonify 回客户端非常棒。 我一直在寻找这个确切的解决方案,懒得重新发明***。非常优雅和灵活。 'i_' 是可选的,因为字典也与 int 一起使用。您可以在 execFunc 调用后添加以下内容以释放内存:delete socketQueue['i_'+data['cmd_id']]; 如果您需要访问您将调用它的上下文/范围内的变量怎么办?据我了解,“函数(数据)”在 webSocket.onmessage 的范围之外被调用,这会造成问题【参考方案2】:

当你创建一个 websocket 时,你通常会在 socket 中添加一个 onmessage 回调函数,当客户端从服务器接收到数据时,这个回调函数就会被调用。

 var socket = new WebSocket("ws://example.com:8000/websocketapp.php");  
 socket.onmessage = function(msg)
      alert('The server says: ' + msg);
 

在服务器响应之前阻塞的函数在概念上是一个坏主意,因为这意味着脚本执行将挂起,直到服务器响应到来。当服务器繁忙且网络延迟很高时,这可能需要几秒钟。因此,最好使用回调函数(事件发生时调用的函数)。

当您可以保证一次只向服务器发送一个请求时,您只需将 socket.onmessage 函数更改为处理服务器响应并对其进行操作的函数:

 socket.onopen = function() 
    socket.send("What's your name?");
    socket.onmessage = function(message) 
        alert("The server says its name is " + message); 
        socket.send("What is the answer to life, the universe and everything?");
        socket.onmessage = function(msg)
             alert("The server says the answer is: " + msg);
        
     
 

(顺便说一句:为了更好的可读性,我在设置回复处理程序之前编写了请求。最好反过来做:先设置回复处理程序,然后发送请求。这对于不太可能的情况很重要但并非不可能的情况是服务器响应如此之快以至于尚未设置处理程序)。

但是当您有多个并行请求并且您需要找出服务器所指的请求时,您将需要更好的东西。例如,您可以有一个 onmessage 函数来识别消息,然后将每条消息转发到专门的消息处理函数。

【讨论】:

你的意思可能是socket.send() 如果服务器端发生任何事情时服务器“推送”一些信息给客户端怎么办?这段代码将如何工作?不会的! @RonanDejhero 这不是问题的范围。但是当您将其添加到上述场景中时,它将等同于上一段中描述的内容。服务器需要在每条消息前面加上类型信息,以便客户端知道如何处理它。 我的意思是,您向服务器发送了一些数据,服务器响应时间太长(一些非常长的操作)并且您需要结果,但同时您向服务器发送了另一个请求(并且也需要结果),然后服务器为您的第二个请求发送回复,一段时间后它完成长操作并将结果发送回给您,您将如何识别哪个响应对哪个请求?您的示例基本上连接,发送数据并等待响应,如果您为每个请求打开一个连接,那么 websocks 有什么用?你可以改用ajax【参考方案3】:

您应该在 WebSocket 之上使用某种 RPC 协议。我建议你看一下WAMP,这是一个添加了 RPC 和 PUB/SUB 语义的 WebSocket 子协议。它包含一个客户端库,通过为您提供基于承诺的 API,在一定程度上抽象了异步连接。

这是一个例子:

var wsuri = "ws://localhost:9000";

ab.connect(wsuri, function (session) 
    session.call("http://example.com/simple/calc#add", 23, 7).then(
        function (res) 
            console.log("got result: " + res);
        ,
        function (error, desc) 
            console.log("error: " + desc);
        
    );
);

为此有许多服务器端实现。

【讨论】:

【参考方案4】:

WebSocket 就像 TCP,只是一个传输层。你需要的是在 webSocket 之上运行一个协议。该协议负责为您创建消息之间的关联。您可能想看看 AMQP,它是用于实时异步消息传递的最广泛和最完整的开放协议。例如,请参阅 https://github.com/dansimpson/amqp-js 以获得简单的 AMQP 客户端 JS 库。

我不知道您使用的是什么类型的服务器,但 AMQP 将为您提供最大的灵活性,因为在许多平台上都支持 AMQP。还有简单的 AMQP 消息代理实现。如果您的服务器是 Java,那么您将需要查看 JMS。在这种情况下,您将需要一个更精细但更强大的 JMS 代理。

【讨论】:

TCP 实际上涉及来自另一端对您提交的每个请求的响应,这使得它更像 AJAX 而不是 WebSocket。 UDP 是您正在考虑的协议 - 您将东西扔进深渊,然后祈祷另一端正在接收它 = WebSocket。我同意您的其余评论,但 WebSocket 不像 TCP,它在 TCP 之上模拟 UDP。【参考方案5】:

我认为WebSockets-Callback这个项目会更容易使用

wscb.send(cmd: 'Hello server!',
    function(response)
        //do something...
    
)

【讨论】:

以上是关于与 Websockets 同步请求的主要内容,如果未能解决你的问题,请参考以下文章

Android 中Okhttp的同步请求与异步请求

HTTP请求中同步与异步有啥不同

servlet的同步请求异步请求以及请求转发与重定型的区别

同步与异步

关于Agax的get与post浅分析,同步请求与异步请求;

用户事务处理中同步请求与异步请求区别