2015-03-25 64 views
-1

我必須爲我的IT學校製作一個簡單的IRC客戶端/服務器程序。該主題要求我們使用select(2)進行套接字輪詢,但禁止我們使用O_NONBLOCK套接字。我們可以用阻塞套接字做一個非阻塞的服務器嗎?

  • 您的服務器將接受多個同時連接。
    注意,禁止使用fork。所以你應該使用select
  • 你的服務器不能被阻塞。
    這有什麼做與非阻塞套接字,這是禁止的(所以不要使用fcntl(s, O_NONBLOCK)

我想知道如果它甚至可以設計一個無阻塞服務器(哪些不叉)與阻塞插座,甚至使用select(2)

下面是一個簡單的例子:假設我們有一個簡單的文本協議,每行一個命令。每個客戶都有一個緩衝區。當select(2)告訴我們一個客戶端準備好了read(2)時,我們閱讀,直到我們在客戶端緩衝區中找到一個\n,因此我們處理該命令。使用非阻塞套接字,我們將讀取到EAGAIN

現在想象一下,我們正在使用阻塞套接字,並且惡意客戶端發送沒有換行符的文本。 select(2)告訴我們數據可用,然後我們在客戶端上read(2)。但我們永遠不會讀取預期的\n。系統調用不會返回EAGAIN,而是無限期地阻塞。這是一項拒絕服務攻擊。

是否真的有可能設計一個無阻塞的服務器與阻塞插座和select(2)(否fork(2))?

回答

1

是的,你從套接字讀取一次,select告訴你已經準備好了。如果read包含\n,則處理該行。否則,存儲收到的任何數據,並立即返回select

當然,這意味着對於每個打開的套接字,您必須保持狀態信息以及到目前爲止讀取的數據緩衝區。這允許代碼獨立處理每個read,而無需在返回select之前完成整行。

+0

好的,這是有道理的;那麼你只需要做一個'read'調用,對吧? – 2015-03-25 02:53:21

+2

當'select()'退出時,一個或多個套接字可能是可讀的,具體取決於您等待的套接字數量。你必須在每個可讀的套接字上調用'read()',根據需要緩存接收到的數據,然後在返回到'select()'前處理緩衝區。 – 2015-03-25 03:09:10

+0

這隻考慮閱讀。你還需要考慮寫作。 – EJP 2015-03-25 03:22:48

0

這是不可能的。

  1. select()塊,因此任何調用它的程序也是如此。
  2. Posix在阻塞模式下爲send()定義的行爲是阻塞,直到提供的所有數據都已傳輸到套接字發送緩衝區。除非你打算鑽研低水位標記等,否則不可能事先知道對於任何給定的send(),在套接字發送緩衝區中是否有足夠的空間完成而沒有阻塞,因此不可能用於任何調用的程序send()不要阻止。

    請注意,select()不會幫助你。它可以告訴你什麼時候有一些房間,但是當沒有足夠的

+0

「阻止'選擇'調用」!=「阻塞套接字」。 Upvote for'發送'警告雖然(這似乎也適用於'寫') – immibis 2015-03-25 03:29:23

+0

@immibis引用的任務說'你的*服務器*一定不能阻塞'。措辭不佳。警告適用於'send(),sendto(),sendmsg(),write(),...' – EJP 2015-03-25 03:34:56

+0

是的,措辭不佳;然而,很顯然他們希望你在'select'上阻塞,但不是很明顯你可以阻止'send' /'write'。 – immibis 2015-03-25 03:37:09