在前面一篇文章swoole源码解析之swoole_client流程解析(一)中我们介绍了swoole_client的构造函数和set函数的实现原理,这里我们结合第一篇中的demo流程继续解析余下的流程,首先我们从connect方法开始介绍。
//swoole_client对象的connect方法流程
static PHP_METHOD(swoole_client, connect)
{
zend_long port = 0, sock_flag = 0;//分别表示连接的端口信息和socket的属性信息
char *host = NULL;//用于连接的ip地址信息
zend_size_t host_len;
double timeout = SW_CLIENT_DEFAULT_TIMEOUT;//连接超时时间,默认为0.5s
//解析php端调用时传入的参数信息,看接口文档可以看到输入参数有ip,port,socket的属性信息和connect的超时时间
#ifdef FAST_ZPP
ZEND_PARSE_PARAMETERS_START(1, 4)
Z_PARAM_STRING(host, host_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(port)
Z_PARAM_DOUBLE(timeout)
Z_PARAM_LONG(sock_flag)
ZEND_PARSE_PARAMETERS_END();
#else
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ldl", &host, &host_len, &port, &timeout, &sock_flag) == FAILURE)
{
return;
}
#endif
if (host_len <= 0)//ip地址信息异常
{
swoole_php_fatal_error(E_WARNING, "The host is empty.");
RETURN_FALSE;
}
//读取swoole_client对应的内部扩展的对象信息,从前面分析可以知道,这里读取到的是null
swClient *cli = (swClient *) swoole_get_object(getThis());
if (cli)
{
swoole_php_fatal_error(E_WARNING, "connection to the server has already been established.");
RETURN_FALSE;
}
//创建内部扩展的对象信息,也就是客户端socket对象信息
cli = php_swoole_client_new(getThis(), host, host_len, port);
if (cli == NULL)
{
RETURN_FALSE;
}
//重新设置对应关系
swoole_set_object(getThis(), cli);
if (cli->type == SW_SOCK_TCP || cli->type == SW_SOCK_TCP6)//如果要连接的通信类型为TCP
{
if (port <= 0 || port > SW_CLIENT_MAX_PORT)//判断端口有效性
{
swoole_php_fatal_error(E_WARNING, "The port is invalid.");
RETURN_FALSE;
}
if (cli->async == 1)//如果是异步客户端
{
//for tcp: nonblock
//for udp: have udp connect
sock_flag = 1;
}
}
if (cli->keep == 1 && cli->socket->active == 1)//如果是长连接,且连接是活跃的,更新swoole_client的重用信息,这里是扣减了1次。
{
zend_update_property_bool(swoole_client_class_entry_ptr, getThis(), SW_STRL("reuse")-1, 1 TSRMLS_CC);
RETURN_TRUE;
}
else if (cli->socket->active == 1)//不是长连接,连接是活跃的,表示已经建立了连接
{
swoole_php_fatal_error(E_WARNING, "connection to the server has already been established.");
RETURN_FALSE;
}
//读取swoole_client对象的setting信息
zval *zset = sw_zend_read_property(swoole_client_class_entry_ptr, getThis(), ZEND_STRL("setting"), 1 TSRMLS_CC);
if (zset && !ZVAL_IS_NULL(zset))
{
php_swoole_client_check_setting(cli, zset TSRMLS_CC);//检查setting信息设置,这里会将swoole_client的setting里面的信息更新到cli的相应属性上
}
//异步客户端
if (cli->async)
{
//获取php侧回调函数信息,回调函数可以通过swoole_client对象的on方法设置
client_callback *cb = (client_callback *) swoole_get_property(getThis(), 0);
if (!cb)//回调函数信息为空,报错,中断流程
{
swoole_php_fatal_error(E_ERROR, "no event callback function.");
RETURN_FALSE;
}
if (swSocket_is_stream(cli->type))//判断socket类型是否为TCP类型的
{
if (!cb->onConnect)//没有设置onConnect回调函数
{
swoole_php_fatal_error(E_ERROR, "no 'onConnect' callback function.");
RETURN_FALSE;
}
if (!cb->onError)//没有设置onError回调函数
{
swoole_php_fatal_error(E_ERROR, "no 'onError' callback function.");
RETURN_FALSE;
}
if (!cb->onClose)//没有设置onClose回调函数
{
swoole_php_fatal_error(E_ERROR, "no 'onClose' callback function.");
RETURN_FALSE;
}
cli->onConnect = client_onConnect;//设置onConnect回调函数信息
cli->onClose = client_onClose;//设置onClose回调函数信息
cli->onError = client_onError;//设置onError回调函数信息
cli->onReceive = client_onReceive;//设置onReceive回调函数信息
cli->reactor_fdtype = PHP_SWOOLE_FD_STREAM_CLIENT;//客户端类型为流式协议,也就是TCP类型
if (cb->onBufferFull)
{
cli->onBufferFull = client_onBufferFull;//设置onBufferFull回调函数信息
}
if (cb->onBufferEmpty)
{
cli->onBufferEmpty = client_onBufferEmpty;//设置onBufferEmpty回调函数信息
}
}
else//如果是其他网络类型,这里只可能是UDP类型,也就是数据包协议
{
if (!cb || !cb->onReceive)//回调函数信息或者onReceive回调函数为空
{
swoole_php_fatal_error(E_ERROR, "no 'onReceive' callback function.");
RETURN_FALSE;
}
if (cb->onConnect)
{
cli->onConnect = client_onConnect;//设置onConnect回调函数信息
}
if (cb->onClose)
{
cli->onClose = client_onClose;//设置onClose回调函数信息
}
cli->onReceive = client_onReceive;//设置onReceive回调函数信息
cli->reactor_fdtype = PHP_SWOOLE_FD_DGRAM_CLIENT;//标记为数据包协议
}
zval *zobject = getThis();
cli->object = zobject;//cli的属性值object设置为swoole_client对象
sw_copy_to_stack(cli->object, cb->_object);//这里实现为空
sw_zval_add_ref(&zobject);//增加引用计数信息
}
//执行正式的连接操作,这里基于创建的客户端类型不同,实际执行的connect也不同
if (cli->connect(cli, host, port, timeout, sock_flag) < 0)//执行连接失败
{
if (errno == 0 )
{
if (SwooleG.error == SW_ERROR_DNSLOOKUP_RESOLVE_FAILED)
{
swoole_php_error(E_WARNING, "connect to server[%s:%d] failed. Error: %s[%d]", host, (int )port,
hstrerror(h_errno), h_errno);
}
//更新swoole_client的errCode属性
zend_update_property_long(swoole_client_class_entry_ptr, getThis(), SW_STRL("errCode")-1, SwooleG.error TSRMLS_CC);
}
else
{
swoole_php_sys_error(E_WARNING, "connect to server[%s:%d] failed.", host, (int )port);
//更新swoole_client的errCode属性
zend_update_property_long(swoole_client_class_entry_ptr, getThis(), SW_STRL("errCode")-1, errno TSRMLS_CC);
}
RETURN_FALSE;
}
RETURN_TRUE;
}