我需要使用epoll for Linux的tcp客戶端的異步連接和斷開連接。有分機。在Windows中的功能,如ConnectEx,DisconnectEx,AcceptEx等... 在tcp服務器標準接受函數正在工作,但在tcp客戶端不工作連接和斷開...所有套接字都是非阻塞的。異步連接和斷開與epoll(Linux)
我該怎麼做?
謝謝!
我需要使用epoll for Linux的tcp客戶端的異步連接和斷開連接。有分機。在Windows中的功能,如ConnectEx,DisconnectEx,AcceptEx等... 在tcp服務器標準接受函數正在工作,但在tcp客戶端不工作連接和斷開...所有套接字都是非阻塞的。異步連接和斷開與epoll(Linux)
我該怎麼做?
謝謝!
要做到無阻塞連接(),假設插座已經作出無阻塞:
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
}
對於第二種情況,其中連接()與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上,連接()從不立即成功,你總是需要等待可寫性。然而,例如,在FreeBSD上,我發現無阻塞connect()到localhost是成功的。
我有一個「完整」的答案在這裏以防別人正在尋找這樣的:
#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)
{
...
}
}
我相信你的錯誤檢查epoll_wait()是不正確的 - 你應該總是通過getsockopt檢查連接嘗試的結果( SO_ERROR),即使你沒有得到EPOLLERR。在手冊頁http://linux.die.net/man/2/connect中看到EINPROGRESS另外,assert()是處理嚴重錯誤的錯誤方法 - 這意味着你已經*證明*它永遠不會發生。使用exit(),即使定義了NDEBUG,也會終止程序。 – 2012-06-05 21:47:47
剛剛添加了建議的編輯。未編輯的版本似乎適用於我。 – Sonny 2012-06-06 01:20:11
從經驗來看,檢測無阻塞連接時,epoll的是選擇和投票方式有點不同。
with epoll:
connect()調用完成後,檢查返回碼。
如果連接無法立即完成,請用epoll註冊EPOLLOUT事件。
調用epoll_wait()。
如果連接失敗,您的事件將被填充EPOLLERR或EPOLLHUP,否則EPOLLOUT將被觸發。
是的,我在回答中忘記提及epoll可以返回EPOLLERR或EPOLLHUP以及EPOLLOUT。感謝提及,它已被糾正。 – 2013-10-08 12:08:36
我已經嘗試了桑尼的解決方案,epoll_ctl將返回無效的參數。所以我想也許這樣做是按照正確的方式:
1.創建socketfd和epollfd
2.使用epoll_ctl到socketfd和epollfd與epoll的事件相關聯。
3.do連接(socketfd,...)
4.檢查返回值或errno
5.如果錯誤號== EINPROGRESS,做epoll_wait
這可以幫助你: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll – 2012-04-17 08:11:47
作爲鏈接DJB頁面建議的可能替代方案,我想建議嘗試「dup '和'關閉'描述符(並使用重複)。沒有測試,但它應該工作,在我的理解。文檔聲明,不檢查'close'的返回值是嚴重的編程錯誤,因爲它可能會返回以前的錯誤。這正是你想要的(如果'close'給出錯誤,'connect'失敗)。雖然當然如果你使用'epoll',那麼你肯定會有一個操作系統,其中'getsockopt(SO_ERROR)'將會正常工作... – Damon 2012-04-17 09:08:03
如果可行,最簡單的選擇是等到connect()返回之後再設置NON_BLOCK。 – delicateLatticeworkFever 2012-04-17 13:11:10