2017-06-19 171 views
-1

在我的TCP服務器,我想連接(活動)插座有:非阻塞PASV襪子和阻塞

  1. 非阻塞被動插座有無阻塞接受();

  2. 接受連接後,我想喜歡覈實客戶提供的ID和密碼進行一些認證。所以我有明確的協議,我想通過連接套接字阻止TCP服務器和客戶端之間的recv()/ send()對話。

  3. 客戶端身份驗證後,我想有無阻塞連接插座,以便從外部線程服務器關閉。

問題是,當我第一次設置非阻塞PASSIVE套接字然後接受CONNECTION套接字也是非阻塞?他們爲什麼不分開套接字?

fcntl(ps_fd, F_SETFL, O_NONBLOCK); 

我通過連接插座做認證:

if((n_recv = recv(sock_fd, buf, sizeof(buf) - 1, 0)) <= 0) { ... } 

但這裏recv()不會阻止和客戶不能提供

我已經使用這個代碼被動套接字設置爲非阻塞模式它是EAGAIN錯誤之前的身份驗證標識和密碼。

我可以恢復連接插座在阻斷模式又和被動插座左無阻塞?

+0

這聽起來非常錯誤的...不要在一個客戶端上阻止你的整個服務!相反,使用一些上下文和一個狀態機。同時建議'選擇()'(或什麼特定平臺的像'的epoll()'),而不是'O_NONBLOCK' –

+0

啊,但他們似乎從被動套接字繼承狀態,並不會阻止:( –

+1

所以把它們放到阻塞模式是什麼阻止你? – EJP

回答

1

阻塞狀態不是由Linux的接受的套接字繼承。在BSD衍生的系統(如MacOS和可能的Windows(雖然我無法找到指定的任何東西))無阻塞被繼承。

一個解決方案當然是讓接受的套接字再次被阻塞,然後一旦完成驗證階段就不會阻塞。如果您是單線程的,這將阻止程序的其餘部分,這意味着當一個用戶進行身份驗證時,您無法接受其他連接。

另一種可能的解決方案是使用線程,或甚至過程,以處理連接。

或者你可以使用一些投票站像selectpoll或者如果你在Linux上使用epoll(或相應的變種BSD系統),知道什麼時候有被接受的套接字上接收到的數據。對於這個工作,你可以使用一個簡單的狀態機,在那裏你有國家如WAITING_FOR_USERNAMEWAITING_FOR_PASSWORDLOGGED_IN

+0

一切都被繼承。阻塞/非阻塞模式,接收緩衝區大小,... – EJP

+0

@EJP你說得對。該規範的實施BSD非可以阻斷遺傳。答案更新,保持可行的解決方案。 –

1

我已經試過這樣的解決方案,它似乎工作。

對我來說,從被動套接字上的accept()接收到的OS X連接套接字將繼承其非阻塞模式。所以在接受後,我將連接套接字模式更改爲再次阻止連接套接字。

示例代碼

fcntl(ps_fd, F_SETFL, O_NONBLOCK); 

    int cs_fd = accept(ps_fd); 
    // revert connection socket to non-blocking 
    int opts = fcntl(cs_fd, F_GETFL); 
    opts = opts & (~O_NONBLOCK); 
    fcntl(cs_fd, F_SETFL, opts); 

    // then authentication via cs_fd 

    // after authentication change it to non-blocking again 
    fcntl(cs_fd, F_SETFL, O_NONBLOCK);