1. 程式人生 > >WinSock和非同步io

WinSock和非同步io

        一般Socket本身可以設定屬性,阻塞或者非阻塞,阻塞模式下accept,connect,send,recv,sendto,recvfrom操作都會等待socket的訊號,send要等待操作完成後才會返回,等待的時間也是在等socket的狀態變為可寫。只不過等待的操作由作業系統來做,當前執行緒被掛起了。recv同理也一樣。非阻塞則是不管可寫或者非可寫send都會直接返回,可寫send返回寫入的位元組數,不可寫返回socket_error.這時候要不採用迴圈send的方法,直到send成功,或者用select先去檢查socket是否可寫再進行send寫入操作,同理recv也相同,檢測是否可讀,再recv讀入資料。

        這中間其實有個問題,就是無論是用阻塞或者非阻塞,都只有當socket的狀態為可寫或者可讀的時候,所有的操作才會成功,藉助於select可以幫助我們提前檢測socket的狀態,捨去不必要的等待或者迴圈(也是等待,不過中間可以做點事情)。而且select可以加入多個socket,並且可以設定超時,這個時候如果所有socket都沒有發生訊號改變,可以做點額外的事情。

    但是在Windows下提供了一套更好的功能,就是將socket讀寫和重疊IO(非同步io)結合起來,只要將原有的send,recv,sendto,recvfrom改為WSASend,WSARecv,WSASendto,WSARecvFrom就可以享受到這一優勢。重疊io的好處是無論socket是阻塞還是非阻塞該投遞操作都會立即返回。操作完成會通過Overlapped結構中的一個event進行通知。比如WSARecv這個操作投遞後,如果收到了event通知io完成,我們直接可以用buf中的資料,作業系統已經把資料從核心copy到了我們的使用者緩衝中。這就比select更進一步,select只能發現socket狀態改變,然後我們還需要呼叫recv去從核心copy資料。

          當然更有優勢的事情是,重疊io和結合iocp(完成埠)來接受更多的連線,因為上面的事件模型,需要用WaitForMultiEvent去發現事件的狀態改變,但是WSAWaitForMultipleEvent等待的控制代碼有上限只有64個。所以如果連線數很多,則需要將重疊io和完成埠結合起來。