前面我们分析了客户端swoole_client的connect过程,从里面代码可以看出,swClient在connect过程中设置了各种其他动作的回调函数,这里我们接着分析余下的流程,同时把回调函数实现也关注下。
//swoole_client的send函数实现
static PHP_METHOD(swoole_client, send)
{
char *data;
zend_size_t data_len;
zend_long flags = 0;
//解析输入参数信息,这里解析出了data,data长度信息data_len,flag信息,解析也分新的扩展方式和旧的扩展方式
#ifdef FAST_ZPP
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STRING(data, data_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(flags)
ZEND_PARSE_PARAMETERS_END();
#else
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &flags) == FAILURE)
{
return;
}
#endif
if (data_len <= 0) //数据长度小于等于0
{
swoole_php_fatal_error(E_WARNING, "data to send is empty.");
RETURN_FALSE;
}
swClient *cli = client_get_ptr(getThis() TSRMLS_CC);//基于当前php对象swoole_client获取到swoole内部的封装对象信息swClient
if (!cli)//对象信息为空
{
RETURN_FALSE;
}
//clear errno
SwooleG.error = 0;
int ret = cli->send(cli, data, data_len, flags);//调用swClient的send函数发送数据
if (ret < 0) //发送失败
{
swoole_php_sys_error(E_WARNING, "failed to send(%d) %zd bytes.", cli->socket->fd, data_len);
zend_update_property_long(swoole_client_class_entry_ptr, getThis(), SW_STRL("errCode")-1, SwooleG.error TSRMLS_CC);//更新swoole_client的errCode属性信息
RETVAL_FALSE;
}
else
{
RETURN_LONG(ret);
}
}
如开篇所说,我们在connect过程中设置了swClient的send回调函数,我们继续分析这个回调函数的实现,客户端在创建时,基于同步和异步区别,在执行send时,流程是不同的,不同点在于,如果是同步的,则多次发送数据直到发送数据完毕,如果是异步的,通过多路复用的方式处理,这里其实是模拟了异步,不是真正的异步,我们重点看看同步的,异步的代码也贴出来,不做实际分析。
cli->connect = swClient_tcp_connect_sync;//同步回调函数设置
cli->send = swClient_tcp_send_async;//异步回调函数设置
//标准的tcp数据发送,多次发送直到数据发送完成,中间需要判断send返回值做处理
static int swClient_tcp_send_sync(swClient *cli, char *data, int length, int flags)
{
int written = 0;
int n;
assert(length > 0);
assert(data != NULL);
while (written < length)
{
n = swConnection_send(cli->socket, data, length - written, flags);
if (n < 0)
{
if (errno == EINTR)
{
continue;
}
else if (errno == EAGAIN)
{
swSocket_wait(cli->socket->fd, 1000, SW_EVENT_WRITE);
continue;
}
else
{
SwooleG.error = errno;
return SW_ERR;
}
}
written += n;
data += n;
}
return written;
}
static sw_inline int swConnection_send(swConnection *conn, void *__buf, size_t __n, int __flags)
{
int retval;
#ifdef SW_USE_OPENSSL
if (conn->ssl)
{
retval = swSSL_send(conn, __buf, __n);
}
else
{
retval = send(conn->fd, __buf, __n, __flags);
}
#else
retval = send(conn->fd, __buf, __n, __flags);//调用linux函数send做发送动作
#endif
#ifdef SW_DEBUG
if (retval > 0)
{
conn->total_send_bytes += retval;//统计实际发送的字节数
}
#endif
return retval;
}
异步发送过程代码如下,不做解析,这里重点是调用了reactor的接口去发送,而reactor在做处理时,会用多路复用。
static int swClient_tcp_send_async(swClient *cli, char *data, int length, int flags)
{
int n = length;
if (cli->reactor->write(cli->reactor, cli->socket->fd, data, length) < 0)
{
if (SwooleG.error == SW_ERROR_OUTPUT_BUFFER_OVERFLOW)
{
n = -1;
cli->socket->high_watermark = 1;
}
else
{
return SW_ERR;
}
}
if (cli->onBufferFull && cli->socket->out_buffer && cli->socket->high_watermark == 0
&& cli->socket->out_buffer->length >= cli->buffer_high_watermark)
{
cli->socket->high_watermark = 1;
cli->onBufferFull(cli);
}
return n;
}