windows通訊端I/O模型之——阻塞模型(1)

來源:互聯網
上載者:User

      Windows通訊端可以在兩種模式下執行I/O操作:阻塞模式和非阻塞模式。在阻塞模式下,I/O操作完成前,執行操作的Winsock調用(例如send和recv)會一直等候下雲,不會立即返回到程式中。

      我們現在就來研究一下阻塞模式是怎樣工作的。

      阻塞模式的socket都遵照一種“生產者-消費者“模型來編製。下面先以一個最簡單的server-client程式碼範例:


#include <winsock2.h>#include <stdio.h>#pragma comment(lib,"WS2_32.lib")void main(void){WSADATA              wsaData;SOCKET               ListeningSocket;SOCKET               NewConnection;SOCKADDR_IN          ServerAddr;SOCKADDR_IN          ClientAddr;int                  ClientAddrLen;int                  Port = 5150;int                  Ret;char                 DataBuffer[1024];// 初始化 Winsock version 2.2if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0){//注意,如果這裡調用失敗了,我們不可以像處理其他錯誤一樣使用WSAGetLastError來得到錯誤碼。//因為Winsock沒有啟動成功,就不可以使用Winsock相關函數來處理問題。我們只能報告這個錯誤狀態,//接著退出。printf("WSAStartup failed with error %d\n", Ret);return;}//建立一個新的socket來監聽客戶的串連請求,AF_INET指明使用IPV4協議//SOCK_STREAM指明基於流來傳送而不是資料報//IPPROTO_TCP指明使用使用TCP協議if ((ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET){printf("socket failed with error %d\n", WSAGetLastError());WSACleanup();return;}//建立一個SOCKADDR_IN結構以供bind綁定使用ServerAddr.sin_family = AF_INET;//使用IPV4協議ServerAddr.sin_port = htons(Port);//轉換成網路位元組序    ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);//接收任何IP的串連請求// 將地址資訊與socket綁定起來if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR){printf("bind failed with error %d\n", WSAGetLastError());closesocket(ListeningSocket);WSACleanup();return;}//設定ListeningSocket為監聽狀態,設定最多儲存5個未處理的串連請求if (listen(ListeningSocket, 5) == SOCKET_ERROR){printf("listen failed with error %d\n", WSAGetLastError());closesocket(ListeningSocket);WSACleanup();return;} printf("We are awaiting a connection on port %d.\n", Port);// accept建立會一個新的socket,如果有一個串連請求到達,//則接收串連請求把這個新的串連控制代碼返回給NewConnectionClientAddrLen = sizeof(SOCKADDR);if ((NewConnection = accept(ListeningSocket, (SOCKADDR *) &ClientAddr,&ClientAddrLen)) == INVALID_SOCKET){printf("accept failed with error %d\n", WSAGetLastError());closesocket(ListeningSocket);WSACleanup();return;}printf("We successfully got a connection from %s:%d.\n",inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));//這個時候可以回頭再監聽其他串連請求,也可以處理NewConnection這個串連上的//收發訊息事務,為示範的簡單起見,我們不回頭監聽其他串連,在這裡關閉這個監聽,//然後直接處理收發訊息的事件。closesocket(ListeningSocket);//為了簡單起見,這裡只是簡單的接收NewConnection這個已經串連的客戶的一個訊息//然後輸出接收到的內容和長度。printf("We are waiting to receive data...\n");if ((Ret = recv(NewConnection, DataBuffer, sizeof(DataBuffer), 0)) == SOCKET_ERROR){printf("recv failed with error %d\n", WSAGetLastError());closesocket(NewConnection);WSACleanup();return;} printf("We successfully received %s %d byte(s).\n", DataBuffer,uRet);   //這是一個簡單樣本,我們不打算做太多的處理,現在可以關閉NewConnection串連了。printf("We are now going to close the client connection.\n");closesocket(NewConnection);//最後要記得調用WSACleanup來終止WinsockWSACleanup();}

      上面的關鍵點在accept函數調用的時候,如果當時沒有串連請求到達,會形成阻塞,使程式進入等待狀態。另外recv函數如果沒有收到客戶的資料,也會進入阻塞狀態。其他部分在代碼注釋已經解析的很清楚。

      下面再看一下配套的用戶端,代碼相對較簡單:

//使用方式:本程式名 服務端IP  如:tcpclient.exe 192.168.0.213#include <winsock2.h>#include <stdio.h>#pragma comment(lib,"WS2_32.lib")void main(int argc, char **argv){WSADATA              wsaData;SOCKET               s;SOCKADDR_IN          ServerAddr;int                  Port = 5150;int                  Ret;if (argc <= 1){printf("USAGE: tcpclient <Server IP address>.\n");return;}if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0){printf("WSAStartup failed with error %d\n", Ret);return;}if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))== INVALID_SOCKET){printf("socket failed with error %d\n", WSAGetLastError());WSACleanup();return;}ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(Port);    ServerAddr.sin_addr.s_addr = inet_addr(argv[1]);//以第一個參數作為服務端IP// 使用s請求串連printf("We are trying to connect to %s:%d...\n",inet_ntoa(ServerAddr.sin_addr), htons(ServerAddr.sin_port));if (connect(s, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR){printf("connect failed with error %d\n", WSAGetLastError());closesocket(s);WSACleanup();return;} printf("Our connection succeeded.\n");//在串連已經完成的情況下,我們可以接收和發送資料,這裡為了簡單示範//只做發送資料printf("We will now try to send a hello message.\n");if ((Ret = send(s, "Hello", 5, 0)) == SOCKET_ERROR){printf("send failed with error %d\n", WSAGetLastError());closesocket(s);WSACleanup();return;}printf("We successfully sent %d byte(s).\n", Ret);// 當串連的socket上不需要收發資料了,關閉這個socketprintf("We are closing the connection.\n");closesocket(s);//退出之前調用WSACleanupWSACleanup();}

      從用戶端看來,請求串連和發送資料是沒有等待的,也沒是沒有阻塞。實際上,阻塞只存在於服務端的accept,以及任何一端的recv。

      實際上,從上面的服務端和用戶端看來,都是非常簡單而不實用的。那麼,如果需要做一個真實可用的阻塞模式的服務和客戶程式,怎麼做?答案是利用多線程。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.