好久没有接触这块了,之前一直直接使用各种网络库,libevent之类的,突然发现非阻塞的connect不会了。。。

好了,不会就查个资料,然后记一下笔记,以防后续又忘了。

首先,前面的流程很简单,将socket设置为非阻塞然后进行connect,这里就不多说了。

connect后,需要判断返回值,这里有几种情况:

  • EINPROGRESS。表示连接还未完成,需要后续再次进行处理
  • 0。 已经连接成功了,一般是连接本机的,直接处理好了
  • 其余的,就是连接失败了,close掉fd即可

主要的处理流程在于EINPROGRESS上,主要是要注册一下EPOLLOUT(可写)事件,然后进行监听。

假设可写了,那么连接过程也就有结果了,这里先放上man中的文档:

EINPROGRESS              The socket is non-blocking and the connection cannot be completed immediately.   It  is  possible  to  select(2)  or poll(2)  for  completion  by  selecting the socket for writing.  After select(2) indicates writability, use getsock-opt(2) to read the SO_ERROR option at level  SOL_SOCKET  to  determine  whether  connect()  completed  successfully (SO_ERROR  is  zero)  or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).

当fd可写了之后,去读一下fd的SO_ERROR来判断连接是否成功,libevent在这里的处理是:

/* Check whether a socket on which we called connect() is done
   connecting. Return 1 for connected, 0 for not yet, -1 for error.  In the
   error case, set the current socket errno to the error that happened during
   the connect operation. */
int
evutil_socket_finished_connecting(evutil_socket_t fd)
{
	int e;
	ev_socklen_t elen = sizeof(e);

	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&e, &elen) < 0)
		return -1;

	if (e) {
		if (EVUTIL_ERR_CONNECT_RETRIABLE(e))
			return 0;
		EVUTIL_SET_SOCKET_ERROR(e);
		return -1;
	}

	return 1;
}

可以看到,在处理可写事件的时候,会先读取错误信息,假设有错,则连接失败。同时判断SO_ERROR获取的值,非零的话,假设为EINPROGRESS,则继续后续的连接状态判断,其余直接为连接失败。

共 0 条回复
暂时没有人回复哦,赶紧抢沙发
发表新回复

作者

sryan
today is a good day