C++ zeromq:重置请求/代表套接字状态
当您使用简单的ZeroMQ REQ/REP模式时,您依赖于一个固定的send()->recv()/recv()->send()序列。 正如文章所描述的,当参与者在请求的中间断开连接时,会遇到麻烦,因为您不能只从另一个连接接收下一个请求开始,但状态机会强迫您向断开连接的请求发送请求。 自从写了上述文章以来,有没有出现一种更优雅的方法来解决这个问题C++ zeromq:重置请求/代表套接字状态,c++,sockets,zeromq,C++,Sockets,Zeromq,当您使用简单的ZeroMQ REQ/REP模式时,您依赖于一个固定的send()->recv()/recv()->send()序列。 正如文章所描述的,当参与者在请求的中间断开连接时,会遇到麻烦,因为您不能只从另一个连接接收下一个请求开始,但状态机会强迫您向断开连接的请求发送请求。 自从写了上述文章以来,有没有出现一种更优雅的方法来解决这个问题 重新连接是否是解决此问题的唯一方法(除了不使用REQ/REP,而是使用另一种模式)有一种解决方案,即为所有调用添加超时。由于ZeroMQ本身并不真正提供
重新连接是否是解决此问题的唯一方法(除了不使用REQ/REP,而是使用另一种模式)有一种解决方案,即为所有调用添加超时。由于ZeroMQ本身并不真正提供简单的超时功能,因此我建议使用ZeroMQ套接字的子类,为所有重要调用添加超时参数
因此,您可以调用s.recv(timeout=5.0),而不是调用s.recv(),如果响应没有在5秒钟内返回,它将返回None并停止阻塞。当我遇到这个问题时,我做了一次徒劳的尝试 我现在正在研究这个问题,因为我正在重新安装一个遗留系统 我经常遇到“需要”了解连接状态的代码。然而,问题是我想转到库所提倡的消息传递范式 我发现了以下功能: 它所做的是监视传递给它的套接字并生成事件,然后将这些事件传递给“inproc”端点——此时,您可以添加处理代码来实际执行某些操作 这里还有一个示例(实际上是测试代码): 目前(可能在周末)我还没有给出任何具体的代码,但我的目的是响应连接和断开连接,以便我能够实际执行所需的任何逻辑重置 希望这能有所帮助,尽管引用了4.2文档,但我使用的是4.0.4,它似乎有这个功能 还有
注意到我注意到上面提到的Python,但是问题是C++的标记,所以我的答案来自……/P> < P>。好消息是,对于ZMQ 3和以后(现代时代),你可以在套接字上设置超时。正如其他人在其他地方所指出的,您必须在创建套接字之后但在连接之前执行以下操作:
zmq_req_socket.setsockopt(zmq.RCVTIMEO,500)#毫秒
然后,当您实际尝试接收回复时(在向REP套接字发送消息之后),您可以捕获将在超过超时时断言的错误:
try:
send( message, 0 )
send_failed = False
except zmq.Again:
logging.warning( "Image send failed." )
send_failed = True
然而!当这种情况发生时,正如其他地方所观察到的,您的套接字将处于一种有趣的状态,因为它仍然期待响应。在这一点上,我找不到任何可靠的工作,除了重新启动套接字。请注意,如果断开()套接字,然后重新连接(),它仍将处于此不良状态。因此,你需要
def reset_my_socket:
zmq_req_socket.close()
zmq_req_socket = zmq_context.socket( zmq.REQ )
zmq_req_socket.setsockopt( zmq.RCVTIMEO, 500 ) # milliseconds
zmq_req_socket.connect( zmq_endpoint )
您还将注意到,由于我关闭了套接字,因此接收超时选项“丢失”,因此在新套接字上设置该选项非常重要
我希望这有帮助。我希望这不是这个问题的最佳答案 由于被接受的答案让我感到非常难过,我做了一些研究,发现我们需要的所有东西实际上都在文档中 带有正确参数的
.setsockopt()
可以帮助您重置套接字状态机,而无需粗暴地破坏它,并在前一个死尸的基础上重建另一个套接字状态机
(是的,我喜欢这张照片)
ZMQ\u请求相关:
将回复与请求匹配REQ
sockets的默认行为是依靠消息的顺序来匹配请求和响应,这通常就足够了。当此选项设置为1
时,REQ
套接字将在传出消息前面加上一个包含请求id的额外帧。这意味着完整消息是(请求id
,0
,用户帧…
)。REQ
套接字将丢弃所有不以这两个帧开头的传入消息。选项值类型
int
选项值单位
0
,1
默认值
0
适用的插座类型
ZMQ\u REQUE\u REQUE\u REQUE\u REQUE:
放松请求和回复之间的严格交替默认情况下,
REQ
套接字不允许启动带有zmq\u send(3)
的新请求,直到收到前一个请求的回复。当设置为1
时,允许发送另一条消息,并具有断开与预期应答的对等方的基础连接的效果,从而在支持该消息的传输上触发重新连接尝试。请求-应答状态机被重置,新请求被发送到下一个可用对等方。如果设置为
1
,还应启用ZMQ\u REQ\u CORRELATE
,以确保请求和回复的正确匹配。否则,对中止请求的延迟回复可以报告为对替代请求的回复。选项值类型
int
选项值单位
0
,1
默认值
0
适用的插座类型
更新:我正在用这个优秀的资源更新这个答案:套接字编程很复杂,所以请查看本文中的参考资料 这里的答案似乎都不准确或有用。OP不是在寻找有关BSD套接字编程的信息。他试图找出如何在REP socket上的ZMQ中稳健地处理accept()ed客户端套接字故障
void zmq_setup(zmq::context_t** context, zmq::socket_t** socket, const char* endpoint)
{
// Free old references.
if(*socket != NULL)
{
(**socket).close();
(**socket).~socket_t();
}
if(*context != NULL)
{
// Shutdown all previous server client-sockets.
zmq_ctx_destroy((*context));
(**context).~context_t();
}
*context = new zmq::context_t(1);
*socket = new zmq::socket_t(**context, ZMQ_REP);
// Enable TCP keep alive.
int is_tcp_keep_alive = 1;
(**socket).setsockopt(ZMQ_TCP_KEEPALIVE, &is_tcp_keep_alive, sizeof(is_tcp_keep_alive));
// Only send 2 probes to check if client is still alive.
int tcp_probe_no = 2;
(**socket).setsockopt(ZMQ_TCP_KEEPALIVE_CNT, &tcp_probe_no, sizeof(tcp_probe_no));
// How long does a con need to be "idle" for in seconds.
int tcp_idle_timeout = 1;
(**socket).setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, &tcp_idle_timeout, sizeof(tcp_idle_timeout));
// Time in seconds between individual keep alive probes.
int tcp_probe_interval = 1;
(**socket).setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, &tcp_probe_interval, sizeof(tcp_probe_interval));
// Discard pending messages in buf on close.
int is_linger = 0;
(**socket).setsockopt(ZMQ_LINGER, &is_linger, sizeof(is_linger));
// TCP user timeout on unacknowledged send buffer
int is_user_timeout = 2;
(**socket).setsockopt(ZMQ_TCP_MAXRT, &is_user_timeout, sizeof(is_user_timeout));
// Start internal enclave event server.
printf("Host: Starting enclave event server\n");
(**socket).bind(endpoint);
}
#include <signal.h>
#include <zmq.hpp>
// zmq_setup def here [...]
int main(int argc, char** argv)
{
// Ignore SIGPIPE signals.
signal(SIGPIPE, SIG_IGN);
// ... rest of your code after
// (Could potentially also restart the server
// sock on N SIGPIPEs if you're paranoid.)
// Start server socket.
const char* endpoint = "tcp://127.0.0.1:47357";
zmq::context_t* context;
zmq::socket_t* socket;
zmq_setup(&context, &socket, endpoint);
// Message buffers.
zmq::message_t request;
zmq::message_t reply;
// ... rest of your socket code here
}
// E.g. skip broken accepted sockets (pseudo-code.)
while (1):
{
try
{
if ((*socket).recv(&request)) == -1)
throw -1;
}
catch (...)
{
// Prevent any endless error loops killing CPU.
sleep(1)
// Reset ZMQ state machine.
try
{
zmq::message_t blank_reply = zmq::message_t();
(*socket).send (blank_reply);
}
catch (...)
{
1;
}
continue;
}
import asyncio
import zmq
import zmq.asyncio
@asyncio.coroutine
def req(endpoint):
ms = 2000 # In milliseconds.
sock = ctx.socket(zmq.REQ)
sock.setsockopt(zmq.SNDTIMEO, ms)
sock.setsockopt(zmq.RCVTIMEO, ms)
sock.setsockopt(zmq.LINGER, ms) # Discard pending buffered socket messages on close().
sock.setsockopt(zmq.CONNECT_TIMEOUT, ms)
# Connect the socket.
# Connections don't strictly happen here.
# ZMQ waits until the socket is used (which is confusing, I know.)
sock.connect(endpoint)
# Send some bytes.
yield from sock.send(b"some bytes")
# Recv bytes and convert to unicode.
msg = yield from sock.recv()
msg = msg.decode(u"utf-8")