2012-04-17 95 views
8

我需要使用epoll for Linux的tcp客戶端的異步連接和斷開連接。有分機。在Windows中的功能,如ConnectEx,DisconnectEx,AcceptEx等... 在tcp服務器標準接受函數正在工作,但在tcp客戶端不工作連接和斷開...所有套接字都是非阻塞的。異步連接和斷開與epoll(Linux)

我該怎麼做?

謝謝!

+0

這可以幫助你: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll – 2012-04-17 08:11:47

+0

作爲鏈接DJB頁面建議的可能替代方案,我想建議嘗試「dup '和'關閉'描述符(並使用重複)。沒有測試,但它應該工作,在我的理解。文檔聲明,不檢查'close'的返回值是嚴重的編程錯誤,因爲它可能會返回以前的錯誤。這正是你想要的(如果'close'給出錯誤,'connect'失敗)。雖然當然如果你使用'epoll',那麼你肯定會有一個操作系統,其中'getsockopt(SO_ERROR)'將會正常工作... – Damon 2012-04-17 09:08:03

+1

如果可行,最簡單的選擇是等到connect()返回之後再設置NON_BLOCK。 – delicateLatticeworkFever 2012-04-17 13:11:10

回答

29

要做到無阻塞連接(),假設插座已經作出無阻塞:

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是成功的。

+0

這是行不通的! – Matt 2012-10-25 04:04:40

+0

@Matt我知道這樣做,你可能在這裏做錯了什麼。你到底在想什麼,它在哪裏失敗?你使用fcntl將套接字置於非阻塞模式嗎? – 2012-10-25 11:51:22

+0

糟糕,沒關係。我的一個錯誤。是的,我有非阻塞的插座。檢查連接的結果時,我有一個錯誤!經典的C錯誤。 – Matt 2012-10-25 22:09:18

0

我有一個「完整」的答案在這裏以防別人正在尋找這樣的:

#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) 
    { 
    ... 
    } 
} 
+0

我相信你的錯誤檢查epoll_wait()是不正確的 - 你應該總是通過getsockopt檢查連接嘗試的結果( SO_ERROR),即使你沒有得到EPOLLERR。在手冊頁http://linux.die.net/man/2/connect中看到EINPROGRESS另外,assert()是處理嚴重錯誤的錯誤方法 - 這意味着你已經*證明*它永遠不會發生。使用exit(),即使定義了NDEBUG,也會終止程序。 – 2012-06-05 21:47:47

+0

剛剛添加了建議的編輯。未編輯的版本似乎適用於我。 – Sonny 2012-06-06 01:20:11

2

從經驗來看,檢測無阻塞連接時,epoll的是選擇和投票方式有點不同。

with epoll:

connect()調用完成後,檢查返回碼。

如果連接無法立即完成,請用epoll註冊EPOLLOUT事件。

調用epoll_wait()。

如果連接失敗,您的事件將被填充EPOLLERR或EPOLLHUP,否則EPOLLOUT將被觸發。

+0

是的,我在回答中忘記提及epoll可以返回EPOLLERR或EPOLLHUP以及EPOLLOUT。感謝提及,它已被糾正。 – 2013-10-08 12:08:36

1

我已經嘗試了桑尼的解決方案,epoll_ctl將返回無效的參數。所以我想也許這樣做是按照正確的方式:

1.創建socketfd和epollfd

2.使用epoll_ctl到socketfd和epollfd與epoll的事件相關聯。

3.do連接(socketfd,...)

4.檢查返回值或errno

5.如果錯誤號== EINPROGRESS,做epoll_wait