如何在没有陷阱的情况下实现简单的请求-响应技术

Posted

技术标签:

【中文标题】如何在没有陷阱的情况下实现简单的请求-响应技术【英文标题】:How to implement simple request-reponse techique without pitfalls 【发布时间】:2015-09-23 12:32:43 【问题描述】:

我想创建简单的客户端和服务器,应该像这样按照请求-响应原则工作:

客户端发送以下字符串:“do_some_stuff” 服务器执行适当的操作并发送以下字符串作为响应:“成功” 客户端读取响应并做一些日志记录 一段时间后客户端发送新命令,所有操作再次重复

听起来很简单?很遗憾,我有以下问题:

我应该使用哪种技术来阅读回复?我应该在发生某些特定字符序列之前读取数据还是应该在某种while true 循环中调用read 函数并等待连接关闭(我将在响应发送后关闭服务器端的套接字)? 我不明白为什么有这么多示例只是读取一些字节(例如,1024)并希望将答案完全读取?如果我们说我们应该获得 1024 个字节,为什么库会决定在“成功”之后没有更多字节?因为服务器端的连接关闭? 如果read_until 永远无法获得终止字符序列(例如,由于互联网连接)怎么办?它会无限期地等待吗?

总而言之,这样可以吗?

客户

#include <boost/asio.hpp>

#include <iostream>

int main()

  try
  
    boost::asio::io_service io_service;

    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), "127.0.0.1", "5013");
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

    boost::asio::ip::tcp::socket s(io_service);
    boost::asio::connect(s, iterator);

    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    request_stream << "do_some_stuff\n";

    boost::asio::write(s, request);

    boost::asio::streambuf b;
    boost::asio::read_until(s, b, '\n');
    std::istream is(&b);
    std::string line;
    std::getline(is, line);

    std::cout << "Reply is: " << line << std::endl;
  
  catch (std::exception& e)
  
    std::cerr << "Exception: " << e.what() << "\n";
  

服务器

import socket
import threading

def main():
    listener = socket.socket()
    listener.bind(('127.0.0.1', 5013))
    listener.listen(5)
    while True:
        client, _ = listener.accept()
        worker_thread = threading.Thread(target=worker, args=(client,))
        worker_thread.setDaemon(True)
        worker_thread.start()

def worker(client):
    data = ""
    while True:
        packet = client.recv(1024)
        if not packet:
            break

        data += packet

        if '\n' in data:
            line, data = data.split('\n', 1)
            print line
            client.sendall('success\n')

if __name__ == '__main__':
    main()

这个解决方案有问题吗?顺便说一句,为什么client.recv(1024) 在从套接字接收到 1024 个字节之前就完成了?它怎么知道后面没有数据?它实际上是如何工作的?

提前致谢。

【问题讨论】:

【参考方案1】:

最常用的方法是使用指定长度的标头并读取 直到收到所有字节。使用消息分隔符也是 用了很多。无连接可以等待连接结束 通信,这意味着,建立连接,发送消息 (和回复)并关闭每条消息,效率较低。

使用一次读取来获取 tcp 消息的所有字节是一种常见的做法 通常总是适用于短消息的错误。错误 是tcp是流协议,不是消息协议。 但是,tcp 主要用于传递消息。长度 参数应该是缓冲区的大小,而不是预期的数字 字节数。读者知道什么时候可以返回,因为发件人 发送数据包。

当连接丢失而没有机会通知时(例如电源 关闭,电缆断开),这可能导致无休止的等待。一个可能的 解决方案是tcp_keepalive。

【讨论】:

非常感谢您的回答!所以,我的代码应该在所有情况下都能正常工作,对吗? 未经测试无法保证,但对我来说已经足够好了。 谢谢!以及client.recv(1024) 如何决定何时结束读取数据? 基于通过 tcp 发送的数据包 这取决于 TCP 层。它可以根据不完整的mtu数据包或下一个数据包的超时来决定。

以上是关于如何在没有陷阱的情况下实现简单的请求-响应技术的主要内容,如果未能解决你的问题,请参考以下文章

GMail Chat 如何能够在没有客户端交互的情况下发出 AJAX 请求?

在没有任何插件的情况下,在chrome dev工具中从POST请求中提取请求主体的最简单方法

如何在不发送请求的情况下拦截 Moya 请求并返回失败响应

Python请求库遇到重试限制时如何访问服务器响应

如何在没有任何 vue 库的情况下在 vue 回调中获取 http 响应标头(axios)

如何在没有 iphone 的情况下测试响应式设计字体