树莓派问题现象:
- 使用curl库,http访问公网域名,连接超时;
- 使用websocketpp库,ws访问公网域名,连接超时
- 使用命令行ping域名,也要15s以上
Android上问题现象:
- 使用curl库,http访问公网域名,正常;
- 使用websocketpp库,ws访问公网域名,连接超时
- 使用命令行ping域名,正常
相关异常打印:
con->get_ec().message()返回 Timer Expired
get_local_close_code 返回 1006
ws端分析过程:
libwebsocketpp库连接流程分析:
3rd/libwebsocketpp/0.8.2/include/websocketpp/roles/client_endpoint.hpp
transport_type::async_connect
3rd/libwebsocketpp/0.8.2/include/websocketpp/transport/asio/endpoint.hpp
void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb)
m_resolver->async_resolve
3rd/libasio/1.18.1/include/asio/detail/resolver_service.hpp
void async_resolve
start_resolve_op(p.p)
3rd/libasio/1.18.1/include/asio/detail/resolve_query_op.hpp
static void do_complete
socket_ops::background_getaddrinfo
3rd/libasio/1.18.1/include/asio/detail/impl/socket_ops.ipp
asio::error_code background_getaddrinfo
socket_ops::getaddrinfo(host, service, hints, result, ec);
asio::error_code getaddrinfo
int error = ::getaddrinfo(host, service, &hints, result);
getaddrinfo是系统函数
树莓派上直接demo直接调用getaddrinfo解析ai.cvte.com也会超时
Android上直接调用getaddrinfo也会超时
原因是:getaddrinfo(host, service, &hints, result);的hints.ai_family=AF_UNSPEC
看介绍:
ai_family参数指定调用者期待返回的套接口地址结构的类型。
它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。如果指定AF_INET,那么函数九不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回
赋值位置:3rd\libasio\1.18.1\include\asio\ip\basic_resolver_query.hpp hints_.ai_family = ASIO_OS_DEF(AF_UNSPEC);
修改为 hints_.ai_family = ASIO_OS_DEF(AF_INET); 测试速度很快。
可是这个地方是asio库写死的,直接改这里不妥,以后更新库可能会被覆盖
3rd\libasio\1.18.1\include\asio\ip\basic_resolver_query.hpp
不仅有:
basic_resolver_query(const std::string& host, const std::string& service,
resolver_query_base::flags resolve_flags = address_configured)
(此处是写死 hints_.ai_family = ASIO_OS_DEF(AF_UNSPEC);)
还有:
basic_resolver_query(const protocol_type& protocol,
const std::string& service,
resolver_query_base::flags resolve_flags = passive | address_configured)
(此处是用hints_.ai_family = protocol.family();)
所以要分析如何使其调用后者
3rd\libasio\1.18.1\include\asio\ip\basic_resolver.hpp
有:
results_type resolve(ASIO_STRING_VIEW_PARAM host,ASIO_STRING_VIEW_PARAM service, resolver_base::flags resolve_flags)
{
basic_resolver_query<protocol_type> q(static_cast<std::string>(host),static_cast<std::string>(service), resolve_flags);
也有:
results_type resolve(const protocol_type& protocol,ASIO_STRING_VIEW_PARAM host, ASIO_STRING_VIEW_PARAM service,resolver_base::flags resolve_flags)
{
basic_resolver_query<protocol_type> q(protocol, static_cast<std::string>(host),static_cast<std::string>(service), resolve_flags);
3rd\libwebsocketpp\0.8.2\include\websocketpp\transport\asio\endpoint.hpp
tcp::resolver::query query(host,port);
此处调用了上面的 basic_resolver_query(const std::string& host
改成:tcp::resolver::query query(tcp::v4(),host,port);就可以跑入basic_resolver_query(const protocol_type& protocol
树莓派上不仅ws会超时,http也会超时,所以curl库也要指明只解析ipv4
curl_easy_setopt(curl, CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
总结:
原因是,服务端没有配置ipv6域名,dns解析的时候同时解析ipv4和ipv6,阻塞等待结果超时。
修改方法是,网络库指明只解析ipv4地址