关于TcpConnection的send函数

sayliu
$len = @fwrite($this->_socket, $send_buffer);
// send successful.
if ($len === strlen($send_buffer)) {
    return true;
}
// Send only part of the data.
if ($len > 0) {
    $this->_sendBuffer = substr($send_buffer, $len);
} else {
    // Connection closed?
    if (!is_resource($this->_socket) || feof($this->_socket)) {
        self::$statistics++;
        if ($this->onError) {
            try {
                call_user_func($this->onError, $this, WORKERMAN_SEND_FAIL, 'client closed');
            } catch (\Exception $e) {
                Worker::log($e);
                exit(250);
            } catch (\Error $e) {
                Worker::log($e);
                exit(250);
            }
        }
        $this->destroy();
        return false;
    }
    $this->_sendBuffer = $send_buffer;
}
Worker::$globalEvent->add($this->_socket, EventInterface::EV_WRITE, array($this, 'baseWrite'));

这段代码中 当$len != strlen($send_buffer)的时候,为什么不继续 fwrite将剩余的数据发送出去,而要去监听
baseWrite函数, 在baseWrite里将剩余数据发送出去

3816 1 0
1个回答

walkor

$len != strlen($send_buffer) 说明缓冲区满了,暂时无法再次发送数据了。
再次调用fwrite很大的几率也是发送不了,所以要等待socket缓冲区的数据被对方接收socket缓冲区可写才能再次调用fwrite发送。

类似一个物流公司,有一个人(进程)负责发货(发送数据),这个人(进程)有很多仓库(链接)。一个仓库(链接)装满了(socket写缓冲区满),再往里放货物放不下了(fwrite返回0),所以要等待仓库的货物全部或者部分发出去(操作系统把socket缓冲区的数据发给对方),仓库有空地儿才能再次向里面放货物。但是这个人(进程)又不能在那里傻等(阻塞等待),万一货物永远都发不出去呢(进程永久阻塞等待)?岂不是不是很亏,这个时间这个人(进程)可以给其它仓库装货物呢(给其它链接发送数据)。所以这个人设置了一个机制(io复用机制,类似epoll),当货物发出仓库又有地方可以放新货物的时候通知他(socket可写事件),这个人继续向里面放货物(调用baseWrite)。这样这个人通过这种类似通知机制(epoll)大大提高了整个物流系统的吞吐量。

  • 暂无评论
年代过于久远,无法发表回答
🔝