非阻塞connect的正确使用方式

问题

在使用非阻塞connect时,发现在服务端没有运行时,连接成功,发现是对于connect返回为-1时,对错误码没有做正确的处理,只是对于errno为EINTR做了处理,其它错误码时使用select判断是否可写,可写就将套接字添加到reactor中,这种处理方式是有问题的,应该是在errno为EINPROGRESS时,才使用select判断是否可写,由于出错时,套接字也会变为可写,所以在判断为可写时,需要调用getsockopt(s, SOL_SOCKET, SO_ERROR,…)判断没有出错,才认为是连接成功的。

asio的异步连接处理

linux的处理

首先创建reactive_socket_connect_op操作,调用start_connect_op,在套接字为非阻塞,或者将套接字设置为非阻塞,调用connect发起连接,如果错误码为in_progress或者would_block,将reactive_socket_connect_op操作添加到事件循环中,在套接字变为可写时,会执行reactive_socket_connect_op_base基类的do_perform方法,其会调用socket_ops#non_blocking_connect方法

template <typename Handler>void async_connect(implementation_type& impl,const endpoint_type& peer_endpoint, Handler& handler){bool is_continuation =boost_asio_handler_cont_helpers::is_continuation(handler);// Allocate and construct an operation to wrap the handler.typedef reactive_socket_connect_op<Handler> op;typename op::ptr p = { boost::asio::detail::addressof(handler),boost_asio_handler_alloc_helpers::allocate(sizeof(op), handler), 0 };p.p = new (p.v) op(impl.socket_, handler);BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_connect"));start_connect_op(impl, p.p, is_continuation,peer_endpoint.data(), peer_endpoint.size());p.v = p.p = 0;}
};```cpp
void reactive_socket_service_base::start_connect_op(reactive_socket_service_base::base_implementation_type& impl,reactor_op* op, bool is_continuation,const socket_addr_type* addr, size_t addrlen)
{if ((impl.state_ & socket_ops::non_blocking)|| socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, true, op->ec_)){if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0){if (op->ec_ == boost::asio::error::in_progress|| op->ec_ == boost::asio::error::would_block){op->ec_ = boost::system::error_code();reactor_.start_op(reactor::connect_op, impl.socket_,impl.reactor_data_, op, is_continuation, false);return;}}}reactor_.post_immediate_completion(op, is_continuation);
}class reactive_socket_connect_op_base : public reactor_op
{
public:reactive_socket_connect_op_base(socket_type socket, func_type complete_func): reactor_op(&reactive_socket_connect_op_base::do_perform, complete_func),socket_(socket){}static bool do_perform(reactor_op* base){reactive_socket_connect_op_base* o(static_cast<reactive_socket_connect_op_base*>(base));return socket_ops::non_blocking_connect(o->socket_, o->ec_);}private:socket_type socket_;
};

windows的处理

template <typename Handler>void async_connect(implementation_type& impl,const endpoint_type& peer_endpoint, Handler& handler){// Allocate and construct an operation to wrap the handler.typedef win_iocp_socket_connect_op<Handler> op;typename op::ptr p = { boost::asio::detail::addressof(handler),boost_asio_handler_alloc_helpers::allocate(sizeof(op), handler), 0 };p.p = new (p.v) op(impl.socket_, handler);BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_connect"));start_connect_op(impl, impl.protocol_.family(), impl.protocol_.type(),peer_endpoint.data(), static_cast<int>(peer_endpoint.size()), p.p);p.v = p.p = 0;}
};void win_iocp_socket_service_base::start_connect_op(win_iocp_socket_service_base::base_implementation_type& impl,int family, int type, const socket_addr_type* addr,std::size_t addrlen, win_iocp_socket_connect_op_base* op)
{// If ConnectEx is available, use that.if (family == BOOST_ASIO_OS_DEF(AF_INET)|| family == BOOST_ASIO_OS_DEF(AF_INET6)){if (connect_ex_fn connect_ex = get_connect_ex(impl, type)){union address_union{socket_addr_type base;sockaddr_in4_type v4;sockaddr_in6_type v6;} a;using namespace std; // For memset.memset(&a, 0, sizeof(a));a.base.sa_family = family;socket_ops::bind(impl.socket_, &a.base,family == BOOST_ASIO_OS_DEF(AF_INET)? sizeof(a.v4) : sizeof(a.v6), op->ec_);if (op->ec_ && op->ec_ != boost::asio::error::invalid_argument){iocp_service_.post_immediate_completion(op, false);return;}op->connect_ex_ = true;update_cancellation_thread_id(impl);iocp_service_.work_started();BOOL result = connect_ex(impl.socket_,addr, static_cast<int>(addrlen), 0, 0, 0, op);DWORD last_error = ::WSAGetLastError();if (!result && last_error != WSA_IO_PENDING)iocp_service_.on_completion(op, last_error);elseiocp_service_.on_pending(op);return;}}// Otherwise, fall back to a reactor-based implementation.reactor& r = get_reactor();update_cancellation_thread_id(impl);if ((impl.state_ & socket_ops::non_blocking) != 0|| socket_ops::set_internal_non_blocking(impl.socket_, impl.state_, true, op->ec_)){if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0){if (op->ec_ == boost::asio::error::in_progress|| op->ec_ == boost::asio::error::would_block){op->ec_ = boost::system::error_code();r.start_op(reactor::connect_op, impl.socket_,impl.reactor_data_, op, false, false);return;}}}r.post_immediate_completion(op, false);
}class win_iocp_socket_connect_op_base : public reactor_op
{
public:win_iocp_socket_connect_op_base(socket_type socket, func_type complete_func): reactor_op(&win_iocp_socket_connect_op_base::do_perform, complete_func),socket_(socket),connect_ex_(false){}static bool do_perform(reactor_op* base){win_iocp_socket_connect_op_base* o(static_cast<win_iocp_socket_connect_op_base*>(base));return socket_ops::non_blocking_connect(o->socket_, o->ec_);}socket_type socket_;bool connect_ex_;
};

其中win_iocp_socket_connect_op_base 和reactive_socket_connect_op_base 都会调用non_blocking_connect,其内部对window和linux作了区分处理,window是调用select,其设置的超时时间为0,将套接字添加到可写以及异常集合中上。linux是调用poll,添加POLLOUT事件,超时时间设置为0。两个都是立即返回。如果返回的事件数为0,说明还在尝试连接中。否则调用getsockopt判断是否出错,没有出错说明连接成功了

bool non_blocking_connect(socket_type s, boost::system::error_code& ec)
{// Check if the connect operation has finished. This is required since we may// get spurious readiness notifications from the reactor.
#if defined(BOOST_ASIO_WINDOWS) \|| defined(__CYGWIN__) \|| defined(__SYMBIAN32__)fd_set write_fds;FD_ZERO(&write_fds);FD_SET(s, &write_fds);fd_set except_fds;FD_ZERO(&except_fds);FD_SET(s, &except_fds);timeval zero_timeout;zero_timeout.tv_sec = 0;zero_timeout.tv_usec = 0;int ready = ::select(s + 1, 0, &write_fds, &except_fds, &zero_timeout);
#else // defined(BOOST_ASIO_WINDOWS)// || defined(__CYGWIN__)// || defined(__SYMBIAN32__)pollfd fds;fds.fd = s;fds.events = POLLOUT;fds.revents = 0;int ready = ::poll(&fds, 1, 0);
#endif // defined(BOOST_ASIO_WINDOWS)// || defined(__CYGWIN__)// || defined(__SYMBIAN32__)if (ready == 0){// The asynchronous connect operation is still in progress.return false;}// Get the error code from the connect operation.int connect_error = 0;size_t connect_error_len = sizeof(connect_error);if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR,&connect_error, &connect_error_len, ec) == 0){if (connect_error){ec = boost::system::error_code(connect_error,boost::asio::error::get_system_category());}elseec = boost::system::error_code();}return true;
}

qt的非阻塞连接处理

是通过QAbstractSocket::waitForConnected
首先开启QElapsedTimer定时器,调用QAbstractSocketPrivate::_q_startConnecting发起连接,内部调用QAbstractSocketPrivate::_q_connectToNextAddress,调用QNativeSocketEngine::connectToHost,通过调用QNativeSocketEnginePrivate::nativeConnect即connect来发起连接 ,在连接没有成功时,会设置套接字的可写socketEngine->setWriteNotificationEnabled(true);会为套接字添加写处理器QWriteNotifier,当事件触发并且当前连接状态为ConnectingState时,再次发起连接,如果errno为EISCONN则更新状态为ConnectedState。同时会调用waitForWrite等待可写事件
对于windows系统,如果返回的事件为不为0,则表示连接成功,否则调用getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize)获取错误原因。
对于 linux系统,当事件触发并且当前连接状态为ConnectingState时,再次发起连接,如果errno为EISCONN则更新状态为ConnectedState。waitForWrite时,如果没有超时,并且当前状态为ConnectingState,则发起重连,同时调用QAbstractSocketPrivate::_q_testConnection判断连接状态。

bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
{Q_D(QNativeSocketEngine);Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(),QAbstractSocket::UnconnectedState, false);if (timedOut)*timedOut = false;int ret = d->nativeSelect(msecs, false);// On Windows, the socket is in connected state if a call to// select(writable) is successful. In this case we should not// issue a second call to WSAConnect()
#if defined (Q_OS_WIN)if (state() == QAbstractSocket::ConnectingState) {if (ret > 0) {setState(QAbstractSocket::ConnectedState);d_func()->fetchConnectionParameters();return true;} else {int value = 0;int valueSize = sizeof(value);if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {if (value == WSAECONNREFUSED) {d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);d->socketState = QAbstractSocket::UnconnectedState;return false;} else if (value == WSAETIMEDOUT) {d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);d->socketState = QAbstractSocket::UnconnectedState;return false;} else if (value == WSAEHOSTUNREACH) {d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);d->socketState = QAbstractSocket::UnconnectedState;return false;}}}}
#endifif (ret == 0) {if (timedOut)*timedOut = true;d->setError(QAbstractSocket::SocketTimeoutError,QNativeSocketEnginePrivate::TimeOutErrorString);d->hasSetSocketError = false; // A timeout error is temporary in waitFor functionsreturn false;} else if (state() == QAbstractSocket::ConnectingState || (state() == QAbstractSocket::BoundState && d->socketDescriptor != -1)) {connectToHost(d->peerAddress, d->peerPort);}return ret > 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791282.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字符分类函数

字符分类函数 C语言中有⼀系列的函数是专门做字符分类的&#xff0c;也就是⼀个字符是属于什么类型的字符的。这些函数的使用都需要包含⼀个头文件是 ctype.h 这些函数的使用方法非常类似&#xff0c;我们就讲解⼀个函数的事情&#xff0c;其他的非常类似&#xff1a; int i…

图的应用解析

01&#xff0e;任何一个无向连通图的最小生成树(B )。 A.有一棵或多棵 B.只有一棵 C.一定有多棵 D.可能不存在 02.用Prim算法和Kruskal算法构造图的最小生成树&#xff0c…

内存和网卡压力测试

1.内存压力测试 1.1测试目的 内存压力测试的目的是评估开发板中的内存子系统性能和稳定性&#xff0c;以确保它能够满足特定的应用需求。开发板通常用于嵌入式系统、物联网设备、嵌入式智能家居等场景&#xff0c;这些场景对内存的要求通常比较高。 其内存压力测试的主要目的…

开发板通过Ethernet(以太网口)与笔记本相连,并通过笔记本wifi连接手机热点来访问外网

文章目录 1、连接开发板与笔记本&#xff1a;2、配置笔记本的网络共享&#xff1a;3、配置开发板的网络设置&#xff1a;4、验证网络连接&#xff1a; 1、连接开发板与笔记本&#xff1a; 使用一根网线将开发板的Ethernet接口与笔记本的以太网口相连。 2、配置笔记本的网络共…

【深度学习】sdwebui的token_counter,update_token_counter,如何超出77个token的限制?对提示词加权的底层实现

文章目录 前言关于token_counter关于class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing)如何超出77个token的限制&#xff1f;对提示词加权的底层实现Overcoming the 77 token limit in diffusers方法1 手动拼方法2 compel 问询、帮助请看&#xff1a; 前言 …

Linux系统切割文件和合并文件介绍

split 和 cat 是 Linux 系统中用于文件切割和合并的命令。它们可以用于将大文件分割成多个小文件&#xff0c;或者将多个小文件合并成一个大文件。 1. split 命令用于将一个大文件切割成多个小文件。它的基本语法如下&#xff1a; split [-a num][-d][-l num][file][prefix] 其…

JS 数组方法 slice 和 splice 的区别

1. slice 概述 slice 方法用于提取数组中的一部分&#xff0c;返回提取结果&#xff0c;不会改变原数组 语法 array.slice(start, end)start 起始位置 end 可选参数&#xff0c;结束位置返回值: 起始位置到结束位置之间的数据&#xff0c;包含起始位置&#xff0c;但是不包含…

动规训练2

一、最小路径和 1、题目解析 就是一个人从左上往做下走&#xff0c;每次只能往右或者往下&#xff0c;求他到终点时&#xff0c;路径上数字和最小&#xff0c;返回最小值 2、算法原理 a状态表示方程 小技巧&#xff1a;经验题目要求 用一个二维数组表示&#xff0c;创建一个…

Flask Python:数据库多条件查询,flask中模型关联

前言 在上一篇Flask Python:模糊查询filter和filter_by&#xff0c;数据库多条件查询中&#xff0c;已经分享了几种常用的数据库操作&#xff0c;这次就来看看模型的关联关系是怎么定义的&#xff0c;先说基础的关联哈。在分享之前&#xff0c;先分享官方文档,点击查看 从文档…

蓝桥杯算法基础(36)动态规划dp经典问题详解

动态规划 -动态规划方法方法代表了这一类问题&#xff08;最优子结构or子问题最优性&#xff09;的有一半解法&#xff0c;是设计方法或者策略&#xff0c;不是具体算法 -本质是递推&#xff0c;核心是找到状态转移的方式&#xff0c;写出dp方程 -形式:记忆性递归递推01背包问题…

FreeRTOS中断管理以及实验

FreeRTOS中断管理以及实验 继续记录学习FreeRTOS的博客&#xff0c;参照正点原子FreeRTOS的视频。 ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级&#xff0c;这个寄存器就是中断优先级配置寄存器 &#xff0c; STM32寄存器中并且这个寄存器只使用[7:4]&#xff0c…

Rust 中的字符串类型:`str` 和 `String`

Rust 中的字符串类型&#xff1a;&str 和 String 文章目录 Rust 中的字符串类型&#xff1a;&str 和 String1. &str&#xff1a;不可变的字符串引用2. String&#xff1a;可变的字符串3、字符串使用综合案例代码执行结果 在 Rust 编程语言中&#xff0c;有两种主要…

RTX RTOS 操作实例分析之---线程(thread)

0 Preface/Foreword 1 线程&#xff08;thread&#xff09; 1.1 线程定义 1.1.1 USE_BASIC_THREADS&#xff08;宏定义&#xff09; 经过以上步骤&#xff08;makefile包含&#xff09;&#xff0c;USE_BASIC_THREADS在编译阶段被定义到相应的模块中。 1.1.2 定义线程ID变量…

安装Pillow库的方法最终解答!_Python第三方库

安装Python第三方库Pillow 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 pillow库 Pillow库是一个非常强大的图像处理库。它提供了广泛的图像处理功能&#xff0c;让我们可以轻松地读取和保存图像、创建缩略图和合并到…

Java学习day5-面向对象2

构建标准JavaBean快捷方式 快捷键&#xff1a;altinsert(Fn)->构造函数->全选构造带参方法&#xff0c;不选择则空参构造 插件&#xff1a;ptg,安装后右键单击&#xff0c;倒数第三个则为一键构造所有set/get函数 数据类型 基本数据类型&#xff1a;数据值存储在自己的…

欧拉路径欧拉回路

欧拉回路&#xff0c;指遍历图时通过图中每条边且仅通过一次&#xff0c;最终回到起点的一条闭合回路&#xff0c;适用于有向图与无向图&#xff0c;如果不强制要求回到起点&#xff0c;则被称为欧拉路径。 欧拉图&#xff1a;具备欧拉回路的图 无向图&#xff1a;图的所有顶…

代码随想录 Day37 738.单调递增的数字 968.监控二叉树

738.单调递增的数字 class Solution { public:int monotoneIncreasingDigits(int N) {string strNum to_string(N);// flag用来标记赋值9从哪里开始// 设置为这个默认值&#xff0c;为了防止第二个for循环在flag没有被赋值的情况下执行int flag strNum.size();for (int i s…

37-巩固练习(一)

37-1 if语句等 1、问&#xff1a;输出结果 int main() {int i 0;for (i 0; i < 10; i){if (i 5){printf("%d\n", i);}return 0;} } 答&#xff1a;一直输出5&#xff0c;死循环 解析&#xff1a;i5是赋值语句&#xff0c;不是判断语句&#xff0c;每一次循…

路径规划——曲线拟合详解(一):多项式轨迹与QP优化(minimum-snap算法核心部分)

前言 历经一个多星期时间&#xff0c;我们在路径规划——搜索算法部分讲解了7种常见的路径搜索算法&#xff0c;每一种算法的链接放在下面了&#xff0c;有需要的朋友点击跳转即可&#xff1a; 路径规划——搜索算法详解&#xff08;一&#xff09;&#xff1a;Dijkstra算法详…

由平行公设的不同而来三种几何学浅谈

由平行公设的不同而来三种几何学浅谈 欧几里德的《几何原本》 欧几里德的《几何原本》一开始就给出了23个定义&#xff0c;5个公设&#xff0c;5个公理。 23个定义(部分)&#xff1a; 点是没有部分的东西。 线是没有宽度的长度。 线的端点是点。 直线是各点都在同一方向上…