我的TCP客户端类具有将请求发送到服务器的方法。 在某些情况下,我需要等待响应才能发出另一个请求。 当然,我可以这样写:
class MyClass {
private:
SomeResponsMsgType* some_resp_msg_type;
TCP_client tcp_client;
void handle_resp(T resp) {
//handles responses
//initializes some_resp_msg_type and others
}
public:
MyClass() : some_resp_msg_type(nullptr) {}
void init() {
tcp_client.run(this, MyClass::handle_resp);
}
SomeResponsMsgType* make_request(int v) {
Request req(v);
some_resp_msg_type = nullptr;
send_message(req);
int timeout = 10;
while(!some_resp_msg_type && !timeout) {
Sleep(100);
--timeout;
}
SomeResponsMsgType* ret = some_resp_msg_type;
some_resp_msg_type = nullptr;
return ret;
}
};
//and use it like this:
void foo() {
MyClass my_class;
my_class.init();
SomeResponsMsgType* resp = my_class.make_request(1);
if(!resp)
...
AnotherResponsMsgType* resp2 = my_class.make_another_request(resp->some_var);
}
但是此代码看起来丑陋且不安全。 请帮我举个例子。谢谢。
答案 0 :(得分:0)
确实,我会在这里查看期货,并且更喜欢让调用者决定何时等待该值:
#include <boost/asio.hpp>
#include <iostream>
namespace ba = boost::asio;
using ba::ip::tcp;
using boost::system::error_code;
using namespace std::chrono_literals;
struct Request {
Request(int v) : _v(v) {}
int _v;
friend std::ostream& operator<<(std::ostream& os, Request const& req) {
return os << req._v;
}
};
struct SomeResponseMsgType {
std::string text;
};
class MyClass {
std::future<SomeResponseMsgType> send_message(Request const& req) {
std::promise<SomeResponseMsgType> p;
try {
// This would be your async call. Here I simplify by
tcp::iostream sock(tcp::endpoint{ {}, 6767 });
sock.exceptions(std::ios::failbit | std::ios::eofbit | std::ios::badbit);
sock.expires_after(1500ms);
sock << req << "\n";
{
std::ostringstream oss;
oss << sock.rdbuf();
p.set_value(SomeResponseMsgType {oss.str()});
}
} catch (...) {
p.set_exception(std::current_exception());
}
return p.get_future();
}
public:
std::future<SomeResponseMsgType> make_request(int v) {
return send_message(Request{v});
}
};
//and use it like this:
int main() {
MyClass my_class;
auto f1 = my_class.make_request(1);
auto f2 = my_class.make_request(2);
try {
{
SomeResponseMsgType resp = f2.get();
std::cout << "Second request gave: " << resp.text << "\n";
}
{
SomeResponseMsgType resp = f1.get();
std::cout << "First request gave: " << resp.text << "\n";
}
} catch(boost::system::system_error const& e) {
std::cerr << "Whoops " << e.code().message() << "\n";
}
}
当然,在您的示例中send_message
可能会以某种异步方式使用TCP_Client
,但是原理保持不变。要实现更多自动化,请参见packaged_task<>
请注意,此处保证了请求的顺序(因为send_message
实际上是同步的)。使用异步代码,由send_message
安排的异步操作的排队将决定是否按顺序发送请求。 (然后,如果服务器碰巧响应更快,那么第二个未来可能实际上要比第一个更早完成。)
对上述内容的愚蠢测试:
socat TCP4-LISTEN:6767,reuseaddr,fork "SYSTEM:sleep 1 && /bin/date"&
./sotest
Second request gave: ma 30 jul 2018 1:14:42 CEST
First request gave: ma 30 jul 2018 1:14:41 CEST