我们在zeromq源代码分析6中分析zeromq所有类型的socket,主要集中分析send和recv函数。
今天我们先分析一下所有socket中base class------socket_base:
先看send(2)函数:
int zmq::socket_base_t::send (::zmq_msg_t *msg_, int flags_)
{
// Check whether the library haven't been shut down yet.
if (unlikely (ctx_terminated)) {
errno = ETERM;
return -1;
}
// Check whether message passed to the function is valid.
if (unlikely ((msg_->flags | ZMQ_MSG_MASK) != 0xff)) {
errno = EFAULT;
return -1;
}
// Process pending commands, if any.
int rc = process_commands (false, true); // 先处理pending的消息,不过是非阻塞模式去处理的
if (unlikely (rc != 0))
return -1;
// At this point we impose the MORE flag on the message.
if (flags_ & ZMQ_SNDMORE) // 如果设置ZMQ_SNDMORE,说明是multipart message,将msg的flags增加ZMQ_MSG_MORE标志
msg_->flags |= ZMQ_MSG_MORE;
// Try to send the message.
rc = xsend (msg_, flags_); // 尝试发消息
if (rc == 0)
return 0;
// In case of non-blocking send we'll simply propagate
// the error - including EAGAIN - upwards.
if (flags_ & ZMQ_NOBLOCK) // 非阻塞模式
return -1;
// Oops, we couldn't send the message. Wait for the next
// command, process it and try to send the message again.
while (rc != 0) { // 阻塞模式如果发消息失败,会等待writer重新被激活并且重发消息
if (errno != EAGAIN)
return -1;
if (unlikely (process_commands (true, false) != 0))
return -1;
rc = xsend (msg_, flags_);
}
return 0;
}
1. 第二个参数表明消息的flag,而目前支持的flag主要有ZMQ_MSG_MORE和ZMQ_NOBLOCK,前者在分析multipart消息的时候我们曾经讲过,后者是表明使用非阻塞模式,默认是阻塞模式的。
2. 阻塞模式下会不断地尝试发消息。失败返回EAGAIN的话就说明当前管道满了(到达HWM,并且swap文件也容不下新消息了),因此这时候socket中的writer就会non-active,所以我们就会调用process_commands(2)来等到激活writer的消息,然后再重发。其它错误的话就直接返回-1。
3. 非阻塞模式就直接返回-1。
而recv(2)函数也是类似,也是默认是阻塞模式,能够设置成非阻塞模式。
int zmq::socket_base_t::recv (::zmq_msg_t *msg_, int flags_)
{
// Check whether the library haven't been shut down yet.
if (unlikely (ctx_terminated)) {
errno = ETERM;
return -1;
}
// Check whether message passed to the function is valid.
if (unlikely ((msg_->flags | ZMQ_MSG_MASK) != 0xff)) {
errno = EFAULT;
return -1;
}
// Get the message.
int rc = xrecv (msg_, flags_); // 先从管道中收消息
int err = errno;
// Once every inbound_poll_rate messages check for signals and process
// incoming commands. This happens only if we are not polling altogether
// because there are messages available all the time. If poll occurs,
// ticks is set to zero and thus we avoid this code.
//
// Note that 'recv' uses different command throttling algorithm (the one
// described above) from the one used by 'send'. This is because counting
// ticks is more efficient than doing RDTSC all the time.
if (++ticks == inbound_poll_rate) {
if (unlikely (process_commands (false, false) != 0))
return -1;
ticks = 0;
}
// If we have the message, return immediately.
if (rc == 0) { // 获得了消息
rcvmore = msg_->flags & ZMQ_MSG_MORE; // 后面还有子消息。。。
if (rcvmore)
msg_->flags &= ~ZMQ_MSG_MORE;
return 0;
}
// If we don't have the message, restore the original cause of the problem.
errno = err;
// If the message cannot be fetched immediately, there are two scenarios.
// For non-blocking recv, commands are processed in case there's an
// activate_reader command already waiting int a command pipe.
// If it's not, return EAGAIN.
if (flags_ & ZMQ_NOBLOCK) { // 非阻塞模式
if (errno != EAGAIN)
return -1;
if (unlikely (process_commands (false, false) != 0)) // 探测一下reader是否被重新激活,第一个参数表明non-block,木有的话就返回-1
return -1;
ticks = 0;
rc = xrecv (msg_, flags_);
if (rc == 0) {
rcvmore = msg_->flags & ZMQ_MSG_MORE;
if (rcvmore)
msg_->flags &= ~ZMQ_MSG_MORE;
}
return rc;
}
// In blocking scenario, commands are processed over and over again until
// we are able to fetch a message.
bool block = (ticks != 0);
while (rc != 0) { // 阻塞模式则不断地去取消息,不断地去等待reader被重新激活的command消息
if (errno != EAGAIN)
return -1;
if (unlikely (process_commands (block, false) != 0))
return -1;
rc = xrecv (msg_, flags_);
ticks = 0;
block = true;
}
rcvmore = msg_->flags & ZMQ_MSG_MORE;
if (rcvmore)
msg_->flags &= ~ZMQ_MSG_MORE;
return 0;
}
这边也是和write(2)差不多的工作流程。。。
其他函数目前来看不是我们分析过程中的重点,可能最后会有一章会分析这些函数。现在你已经迫不及待地准备去看各种socket的代码了吧!
6-2我们就会讲一下req和rep这一对socket,会拿个最简单的tutorial代码并结合源码分析。敬请期待!希望有兴趣的朋友可以和我联系,一起学习。 kaka11.chen@gmail.com