客户端升级为select模型
生活随笔
收集整理的這篇文章主要介紹了
客户端升级为select模型
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 1 客戶端升級為select模型
- 1.1 概述
- 1.2 客戶端代碼實現
- 1.3 服務端代碼實現
1 客戶端升級為select模型
1.1 概述
這里我們為了讓客戶端有時間去處理其它業務邏輯,因此我們需要在客戶端也引入select模型。
1.2 客戶端代碼實現
#define WIN32_LEAN_AND_MEAN#include<windows.h> #include<WinSock2.h> #include<stdio.h>#pragma comment(lib,"ws2_32.lib")enum CMD {CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR };struct DataHeader {short dataLength;short cmd; };//DataPackage struct Login : public DataHeader {Login(){dataLength = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char PassWord[32]; };struct LoginResult : public DataHeader {LoginResult(){dataLength = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;result = 0;}int result; };struct Logout : public DataHeader {Logout(){dataLength = sizeof(Logout);cmd = CMD_LOGOUT;}char userName[32]; };struct LogoutResult : public DataHeader {LogoutResult(){dataLength = sizeof(LogoutResult);cmd = CMD_LOGOUT_RESULT;result = 0;}int result; };struct NewUserJoin : public DataHeader {NewUserJoin(){dataLength = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;scok = 0;}int scok; };int processor(SOCKET _cSock) {//緩沖區char szRecv[4096] = {};// 5 接收客戶端數據int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);DataHeader* header = (DataHeader*)szRecv;if (nLen <= 0){printf("與服務器斷開連接,任務結束。\n", _cSock);return -1;}switch (header->cmd){case CMD_LOGIN_RESULT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);LoginResult* login = (LoginResult*)szRecv;printf("收到服務端消息:CMD_LOGIN_RESULT,數據長度:%d\n", _cSock, login->dataLength);}break;case CMD_LOGOUT_RESULT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);LogoutResult* logout = (LogoutResult*)szRecv;printf("收到服務端消息:CMD_LOGOUT_RESULT,數據長度:%d\n", _cSock, logout->dataLength);}break;case CMD_NEW_USER_JOIN:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);NewUserJoin* userJoin = (NewUserJoin*)szRecv;printf("收到服務端消息:CMD_NEW_USER_JOIN,數據長度:%d\n", _cSock, userJoin->dataLength);}break;} }int main() {//啟動Windows socket 2.x環境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);//------------//-- 用Socket API建立簡易TCP客戶端// 1 建立一個socketSOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == _sock){printf("錯誤,建立Socket失敗...\n");}else {printf("建立Socket成功...\n");}// 2 連接服務器 connectsockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(4567);_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));if (SOCKET_ERROR == ret){printf("錯誤,連接服務器失敗...\n");}else {printf("連接服務器成功...\n");}while (true){fd_set fdReads;FD_ZERO(&fdReads);FD_SET(_sock, &fdReads);timeval t = {0,0};int ret = select(_sock, &fdReads, 0, 0, &t);if (ret < 0){printf("select任務結束1\n");break;}if (FD_ISSET(_sock, &fdReads)){FD_CLR(_sock, &fdReads);if (-1 == processor(_sock)){printf("select任務結束2\n");break;}}printf("空閑時間處理其它業務..\n");Login login;strcpy(login.userName, "lyd");strcpy(login.PassWord, "lyd");send(_sock, (const char*)&login, sizeof(Login), 0);//Sleep(1000);}// 7 關閉套節字closesocketclosesocket(_sock);//清除Windows socket環境WSACleanup();printf("已退出。\n");getchar();return 0; }1.3 服務端代碼實現
服務端的代碼做出微小改動:
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS#include<windows.h> #include<WinSock2.h> #include<stdio.h>#include <vector>#pragma comment(lib,"ws2_32.lib")enum CMD {CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR };struct DataHeader {short dataLength;short cmd; };//DataPackage struct Login: public DataHeader {Login(){dataLength = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char PassWord[32]; };struct LoginResult : public DataHeader {LoginResult(){dataLength = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;result = 0;}int result; };struct Logout : public DataHeader {Logout(){dataLength = sizeof(Logout);cmd = CMD_LOGOUT;}char userName[32]; };struct LogoutResult : public DataHeader {LogoutResult(){dataLength = sizeof(LogoutResult);cmd = CMD_LOGOUT_RESULT;result = 0;}int result; };struct NewUserJoin : public DataHeader {NewUserJoin(){dataLength = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;scok = 0;}int scok; };std::vector<SOCKET> g_clients;int processor(SOCKET _cSock) {//緩沖區char szRecv[4096] = {};// 5 接收客戶端數據int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);DataHeader* header = (DataHeader*)szRecv;if (nLen <= 0){printf("客戶端<Socket=%d>已退出,任務結束。\n", _cSock);return -1;}switch (header->cmd){case CMD_LOGIN:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);Login* login = (Login*)szRecv;printf("收到客戶端<Socket=%d>請求:CMD_LOGIN,數據長度:%d,userName=%s PassWord=%s\n", _cSock, login->dataLength, login->userName, login->PassWord);//忽略判斷用戶密碼是否正確的過程LoginResult ret;send(_cSock, (char*)&ret, sizeof(LoginResult), 0);}break;case CMD_LOGOUT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);Logout* logout = (Logout*)szRecv;printf("收到客戶端<Socket=%d>請求:CMD_LOGOUT,數據長度:%d,userName=%s \n", _cSock, logout->dataLength, logout->userName);//忽略判斷用戶密碼是否正確的過程LogoutResult ret;send(_cSock, (char*)&ret, sizeof(ret), 0);}break;default:{DataHeader header = { 0,CMD_ERROR };send(_cSock, (char*)&header, sizeof(header), 0);}break;} }int main() {//啟動Windows socket 2.x環境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);//------------//-- 用Socket API建立簡易TCP服務端// 1 建立一個socket 套接字SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 2 bind 綁定用于接受客戶端連接的網絡端口sockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(4567);//host to net unsigned short_sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1");if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin))){printf("錯誤,綁定網絡端口失敗...\n");}else {printf("綁定網絡端口成功...\n");}// 3 listen 監聽網絡端口if (SOCKET_ERROR == listen(_sock, 5)){printf("錯誤,監聽網絡端口失敗...\n");}else {printf("監聽網絡端口成功...\n");}while (true){//伯克利套接字 BSD socketfd_set fdRead;//描述符(socket) 集合 fd_set fdWrite;fd_set fdExp;//清理集合FD_ZERO(&fdRead);FD_ZERO(&fdWrite);FD_ZERO(&fdExp);//將描述符(socket)加入集合 FD_SET(_sock, &fdRead);FD_SET(_sock, &fdWrite);FD_SET(_sock, &fdExp);for (int n = (int)g_clients.size()-1; n >= 0 ; n--){FD_SET(g_clients[n], &fdRead);}///nfds 是一個整數值 是指fd_set集合中所有描述符(socket)的范圍,而不是數量///既是所有文件描述符最大值+1 在Windows中這個參數可以寫0timeval t = {0,0};int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, &t);if (ret < 0){printf("select任務結束。\n");break;}//判斷描述符(socket)是否在集合中 if (FD_ISSET(_sock, &fdRead)){FD_CLR(_sock, &fdRead);// 4 accept 等待接受客戶端連接sockaddr_in clientAddr = {};int nAddrLen = sizeof(sockaddr_in);SOCKET _cSock = INVALID_SOCKET;_cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);if (INVALID_SOCKET == _cSock){printf("錯誤,接受到無效客戶端SOCKET...\n");}else {for (int n = (int)g_clients.size() - 1; n >= 0; n--){NewUserJoin userJoin;send(g_clients[n], (const char*)&userJoin, sizeof(NewUserJoin), 0);}g_clients.push_back(_cSock);printf("新客戶端加入:socket = %d,IP = %s \n", (int)_cSock, inet_ntoa(clientAddr.sin_addr));}}for (size_t n = 0; n < fdRead.fd_count; n++){if (-1 == processor(fdRead.fd_array[n])){auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);if (iter != g_clients.end()){g_clients.erase(iter);}}}printf("空閑時間處理其它業務..\n");}for (size_t n = g_clients.size() - 1; n >= 0; n--){closesocket(g_clients[n]);}// 8 關閉套節字closesocketclosesocket(_sock);//------------//清除Windows socket環境WSACleanup();printf("已退出。\n");getchar();return 0; }參考資料:
總結
以上是生活随笔為你收集整理的客户端升级为select模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务端升级为select模型处理多客户端
- 下一篇: 为客户端加入输入线程