我需要在Linux中使用epoll为tcp客户端提供异步连接和断开功能。在Windows中有一些外部函数,如ConnectEx、DisconnectEx、AcceptEx等等...
在tcp服务器中,标准的accept函数可以工作,但在tcp客户端中,连接和断开都无法工作... 所有的套接字都是非阻塞的。
我该怎么做呢?
谢谢!
我该怎么做呢?
谢谢!
为了执行非阻塞 connect(),假设套接字已经被设置成非阻塞模式:
int res = connect(fd, ...);
if (res < 0 && errno != EINPROGRESS) {
// error, fail somehow, close socket
return;
}
if (res == 0) {
// connection has succeeded immediately
} else {
// connection attempt is in progress
}
对于第二种情况,当connect() 函数返回EINPROGRESS错误时(仅适用于此情况),您需要等待套接字可写,例如在epoll中指定您正在等待此套接字的EPOLLOUT事件。一旦您收到通知它已经可写(使用epoll时,还要期望收到EPOLLERR或EPOLLHUP事件),检查连接尝试的结果:
int result;
socklen_t result_len = sizeof(result);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) {
// error, fail somehow, close socket
return;
}
if (result != 0) {
// connection failed; error code is in 'result'
return;
}
// socket is ready for read()/write()
根据我的经验,在Linux上,connect()函数从来不会立即成功,你总是需要等待可写事件。然而,例如在FreeBSD上,我曾经看到非阻塞的connect()函数对本地主机的连接能够立即成功。
从经验上看,当检测到非阻塞连接时,epoll 与 select 和 poll 有一些不同。
使用 epoll:
在进行 connect() 调用后,检查返回代码。
如果连接不能立即完成,则使用 epoll 注册 EPOLLOUT 事件。
调用 epoll_wait()。
如果连接失败,则您的事件将填充 EPOLLERR 或 EPOLLHUP,否则将触发 EPOLLOUT。
#include <sys/epoll.h>
#include <errno.h>
....
....
int retVal = -1;
socklen_t retValLen = sizeof (retVal);
int status = connect(socketFD, ...);
if (status == 0)
{
// OK -- socket is ready for IO
}
else if (errno == EINPROGRESS)
{
struct epoll_event newPeerConnectionEvent;
int epollFD = -1;
struct epoll_event processableEvents;
unsigned int numEvents = -1;
if ((epollFD = epoll_create (1)) == -1)
{
printf ("Could not create the epoll FD list. Aborting!");
exit (2);
}
newPeerConnectionEvent.data.fd = socketFD;
newPeerConnectionEvent.events = EPOLLOUT | EPOLLIN | EPOLLERR;
if (epoll_ctl (epollFD, EPOLL_CTL_ADD, socketFD, &newPeerConnectionEvent) == -1)
{
printf ("Could not add the socket FD to the epoll FD list. Aborting!");
exit (2);
}
numEvents = epoll_wait (epollFD, &processableEvents, 1, -1);
if (numEvents < 0)
{
printf ("Serious error in epoll setup: epoll_wait () returned < 0 status!");
exit (2);
}
if (getsockopt (socketFD, SOL_SOCKET, SO_ERROR, &retVal, &retValLen) < 0)
{
// ERROR, fail somehow, close socket
}
if (retVal != 0)
{
// ERROR: connect did not "go through"
}
}
else
{
// ERROR: connect did not "go through" for other non-recoverable reasons.
switch (errno)
{
...
}
}
dup
和close
描述符(并使用副本)。在我的理解中,虽然没有经过测试,但应该可以工作。文档指出,不检查close
的返回值是一个严重的编程错误,因为它可能返回先前的错误。这正是您想要的(如果close
出现错误,则connect
失败)。当然,如果您使用epoll
,则保证具有getsockopt(SO_ERROR)
将正常工作的操作系统... - Damongetattrinfo_a
也会在内部执行此操作)。因此,当你在工作线程中阻塞时,你也可以在连接上阻塞... - Damon