WinSocket的select函數的用法(windows通訊端比較研究)

來源:互聯網
上載者:User

標籤:windows   winsock   select   tcp   通訊端   

總體上來說select函數的作用:

確定一個或多個套介面的狀態,本函數用於確定一個或多個套介面的狀態,對每一個套介面,調用者可查詢它的可讀性、可寫性及錯誤狀態資訊,用fd_set結構來表示一組等待檢查的套介面,在調用返回時,這個結構存有滿足一定條件的套介面組的子集,並且select()返回滿足條件的套介面的數目。


簡單來說select用來填充一組可用的socket控制代碼,當滿足下列之一條件時:

1.可以讀取的sockets。當這些socket被返回時,在這些socket上執行recv/accept等操作不會產生阻塞;
2.可以寫入的sockets。當這些socket被返回時,在這些socket上執行send等不會產生阻塞;
3.返回有錯誤的sockets。

select的處理函數一般有:

同時和select配對使用的還有:
FD_CLR(s, *set) 
Removes the descriptor s from set. 
FD_ISSET(s, *set) 
Nonzero if s is a member of the set. Otherwise, zero. 
FD_SET(s, *set) 
Adds descriptor s to set. 
FD_ZERO(*set) 
Initializes the set to the null set. 


應該都知道,accept()函數是一個阻塞的函數,當有用戶端請求串連服務端時才返回。如下面的例子

server端:

#include <iostream>#include <Windows.h>using namespace std;#define  PORT 4000#define  IP_ADDRESS "127.0.0.1"#pragma comment(lib, "WS2_32.lib")int main(int argc, char* argv[]){WSADATA  Ws;SOCKET ServerSocket, CientSocket;struct sockaddr_in LocalAddr, ClientAddr;int Ret = 0;int AddrLen = 0;HANDLE hThread = NULL;//Init Windows Socketif (WSAStartup(MAKEWORD(2, 2), &Ws) != 0){cout << "Init Windows Socket Failed::" << GetLastError() << endl;return -1;}ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ServerSocket == INVALID_SOCKET){cout << "Create Socket Failed::" << GetLastError() << endl;return -1;}LocalAddr.sin_family = AF_INET;LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);LocalAddr.sin_port = htons(PORT);memset(LocalAddr.sin_zero, 0x00, 8);Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));if (Ret != 0){cout << "Bind Socket Failed::" << GetLastError() << endl;return -1;}Ret = listen(ServerSocket, 10);if (Ret != 0){cout << "listen Socket Failed::" << GetLastError() << endl;return -1;}cout << "服務端已經啟動" << endl;char RecvBuffer[MAX_PATH];while (true){AddrLen = sizeof(ClientAddr);CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);//阻塞的if (CientSocket == INVALID_SOCKET){cout << "Accept Failed::" << GetLastError() << endl;break;}cout << "用戶端串連::" << inet_ntoa(ClientAddr.sin_addr) << ":" << ClientAddr.sin_port << endl;int Ret = 0;while (true){memset(RecvBuffer, 0x00, sizeof(RecvBuffer));Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0);if (Ret == 0 || Ret == SOCKET_ERROR){cout << "用戶端退出!" << endl;break;}cout << "接收到客戶資訊為:" << RecvBuffer << endl;}CloseHandle(hThread);}closesocket(ServerSocket);closesocket(CientSocket);WSACleanup();return 0;}

client端:

#include <iostream>#include <Windows.h>#pragma comment(lib, "ws2_32.lib")using namespace std;#define  PORT 4000#define  IP_ADDRESS "127.0.0.1"int main(int argc, char* argv[]){WSADATA  Ws;SOCKET CientSocket;struct sockaddr_in ServerAddr;int Ret = 0;int AddrLen = 0;HANDLE hThread = NULL;char SendBuffer[MAX_PATH];//Init Windows Socketif (WSAStartup(MAKEWORD(2, 2), &Ws) != 0){cout << "Init Windows Socket Failed::" << GetLastError() << endl;return -1;}//Create SocketCientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (CientSocket == INVALID_SOCKET){cout << "Create Socket Failed::" << GetLastError() << endl;return -1;}ServerAddr.sin_family = AF_INET;ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);ServerAddr.sin_port = htons(PORT);memset(ServerAddr.sin_zero, 0x00, 8);Ret = connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));if (Ret == SOCKET_ERROR){cout << "Connect Error::" << GetLastError() << endl;return -1;}else{cout << "串連成功!" << endl;}while (true){cin.getline(SendBuffer, sizeof(SendBuffer));Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), 0);if (Ret == SOCKET_ERROR){cout << "Send Info Error::" << GetLastError() << endl;break;}}closesocket(CientSocket);WSACleanup();return 0;}
上面的例子一個用戶端只能接收處理一個用戶端的請求!

加上select的應用,就能讓一個server處理來自多個用戶端的請求:

改寫後的server端

#include <WinSock2.h>#include <stdio.h>#pragma  comment(lib,"ws2_32.lib")#define PORT 4000bool InitAndListern(SOCKET &sListen){WSADATA wsaData;sockaddr_in local;WORD version = MAKEWORD(2, 0);int ret = WSAStartup(version, &wsaData);if (ret != 0){printf("WSAStarup failed\n");return 0;}local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons((u_short)PORT);sListen = socket(AF_INET, SOCK_STREAM, 0);if (sListen == INVALID_SOCKET){printf("Initial socket failed\n");return 0;}if (bind(sListen, (sockaddr*)&local, sizeof(local)) != 0){printf("Bind socket failed\n");return 0;}if (listen(sListen, 10) != 0){printf("Listen socket failed\n");return 0;}return 1;}int main(){SOCKET sListen;if (InitAndListern(sListen) == 0)return 0;printf("Server wait for client connect...\n");fd_set fdSocket;FD_ZERO(&fdSocket);FD_SET(sListen, &fdSocket);//將sListen添加進該集合while (true){fd_set fdRead = fdSocket;int nRet = select(NULL, &fdRead, NULL, NULL, NULL);if (nRet <= 0)break;for (int i = 0; i < (int)fdSocket.fd_count; ++i){if (FD_ISSET(fdSocket.fd_array[i], &fdRead)){if (fdSocket.fd_array[i] == sListen){sockaddr_in addrRemote;int nAddrLen = sizeof(addrRemote);SOCKET sNew = ::accept(sListen, (sockaddr*)&addrRemote, &nAddrLen);FD_SET(sNew, &fdSocket);printf("Clietn %s connected\n", inet_ntoa(addrRemote.sin_addr));}else{char buffer[1024];memset(buffer, 0, 1024);int nRecev = recv(fdSocket.fd_array[i], buffer, 1024, 0);if (nRecev > 0){printf("Received Client Msg:%s\n", buffer);send(fdSocket.fd_array[i], buffer, strlen(buffer), 0);}else{closesocket(fdSocket.fd_array[i]);FD_CLR(fdSocket.fd_array[i], &fdSocket);}}}}}return 0;}


WinSocket的select函數的用法(windows通訊端比較研究)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.