服务器和客户端简单的使用select网络模型(一)
服务器:
#define _CRT_SECURE_NO_WARNINGS #define WIN32_LEAN_AND_MEAN #define _AFXDLL #include"afx.h" #include<windows.h> #include "WinSock2.h" #include<iostream> #include<vector> #include<stdlib.h> std::vector<SOCKET> g_clients; enum CMD { CMD_LOGIN, CMD_LOGIN_RESULT, CMD_LOGIN_OUT, CMD_LOGIN_OUT_RESULT, CMD_NEW_USER_JOIN, CMD_ERROR, }; struct DataHeader { short cmd; short dataLength; }; struct Login :public DataHeader { Login() { cmd = CMD_LOGIN; dataLength = sizeof(Login); } char userName[32]; int passWord; }; struct LoginOut :public DataHeader { LoginOut() { cmd = CMD_LOGIN_OUT; dataLength = sizeof(LoginOut); } char userName[32]; }; struct LoginResult :public DataHeader { LoginResult() { cmd = CMD_LOGIN_RESULT; dataLength = sizeof(LoginResult); result = 0; } int result; }; struct LoginOutResult :public DataHeader { LoginOutResult() { cmd = CMD_LOGIN_OUT_RESULT; dataLength = sizeof(LoginOutResult); result = 0; } int result; }; struct NewUserJoin :public DataHeader { NewUserJoin() { cmd = CMD_NEW_USER_JOIN; dataLength = sizeof(NewUserJoin); sock = 0; } int sock; }; void InitSock() { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData) != 0) return; if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); return; } } int ProcessPart(SOCKET cSock) { char szRecv[1024] = {}; //接受客户端请求数据 int nLen = recv(cSock, szRecv, sizeof(DataHeader), 0); DataHeader *header = (DataHeader*)szRecv; if (nLen <= 0) { printf("客户端<socket=%d>已退出,任务结束\n", cSock); return -1; } printf("收到命令:%d 数据长度:%d\n", header->cmd, header->dataLength); switch (header->cmd) { case CMD_LOGIN: { recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); Login* login = (Login*)szRecv; printf("收到命令 用户名:%s密码%d\n", login->userName, login->passWord); LoginResult ret = {}; ret.result = 0; send(cSock, (const char *)&ret, sizeof(LoginResult), 0); break; } case CMD_LOGIN_OUT: { recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); LoginOut* loginOut = (LoginOut*)szRecv; printf("收到命令: 数据长度:%d 用户名:%s密码%s\n", loginOut->dataLength, loginOut->userName); LoginOutResult retOut = {}; retOut.result = 0; send(cSock, (const char *)&retOut, sizeof(LoginResult), 0); break; } default: { DataHeader header = { CMD_ERROR, 0 }; send(cSock, (const char *)&header, sizeof(DataHeader), 0); break; } } return 0; } int main() { //启动Windows socket 2的环境 InitSock(); //建立socket SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_ERROR == sock) { TRACE(_T("ERROR:创建socket失败\n")); return 0; } sockaddr_in sin = {}; sin.sin_family = AF_INET; sin.sin_port = htons(10000); //host to net unsigned short sin.sin_addr.S_un.S_addr = INADDR_ANY; //本机上的外网和局域网ip任意一个拿来用 //绑定 if (SOCKET_ERROR == bind(sock, (sockaddr *)&sin, sizeof(sockaddr_in))) { int nError = GetLastError(); TRACE(_T("ERROR:绑定socket失败\n")); return 0; } //监听 if (SOCKET_ERROR == listen(sock, 5)) { TRACE(_T("ERROR:socket监听失败")); return 0; } else { printf("监听开始\n"); } char recvBuf[128] = {}; while (true) { fd_set fdRead; 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); //每次进来都把所有要监视的socket添加到相应的数组中 for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++) { FD_SET(*it, &fdRead); } //开始查询,会清空3个数组所有的socket,然后如果哪个socket有事件发送,则会把此socket放入到数组返回 //当有客户端连接或者客户端发送消息都会进来 timeval time = { 2, 0 }; int ret = select(sock + 1, &fdRead, &fdWrite, &fdExp, &time);if (ret < 0) { printf("select任务结束\n"); break; } if (FD_ISSET(sock, &fdRead)) { FD_CLR(sock, &fdRead); //4接收客户端连接 SOCKET cSock = INVALID_SOCKET; sockaddr_in clientAddr = {}; int nAddrLen = sizeof(sockaddr_in); char msgBuf[100]; cSock = accept(sock, (sockaddr*)&clientAddr, &nAddrLen); if (INVALID_SOCKET == cSock) { TRACE(_T("ERROR:接受客户端连接失败\n")); return 0; } //广播所有客户端,新加入一个新的客户端 for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++) { NewUserJoin newUser; newUser.sock = cSock; send(*it, (const char *)&newUser, sizeof(NewUserJoin), 0); } g_clients.push_back(cSock); printf("新客户端加入socket = %d,ip:%s\n",cSock, inet_ntoa(clientAddr.sin_addr)); } for (size_t n = 0; n < fdRead.fd_count; n++) { if (-1 == ProcessPart(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); } } } } for (std::vector<SOCKET>::iterator it = g_clients.begin(); it != g_clients.end(); it++) { closesocket(*it); } //关闭socket closesocket(sock); WSACleanup(); return 0; }
客户端:
#define _CRT_SECURE_NO_WARNINGS #define WIN32_LEAN_AND_MEAN #define _AFXDLL #include"afx.h" #include<windows.h> #include "WinSock2.h" #include<iostream> #include<thread> #include<stdlib.h> using namespace std; bool g_bRun = TRUE; enum CMD { CMD_LOGIN, CMD_LOGIN_RESULT, CMD_LOGIN_OUT, CMD_LOGIN_OUT_RESULT, CMD_NEW_USER_JOIN, CMD_ERROR, }; struct DataHeader { short cmd; short dataLength; }; struct Login:public DataHeader { Login() { cmd = CMD_LOGIN; dataLength = sizeof(Login); } char userName[32]; int passWord; }; struct LoginOut:public DataHeader { LoginOut() { cmd = CMD_LOGIN_OUT; dataLength = sizeof(LoginOut); } char userName[32]; }; struct LoginResult:public DataHeader { LoginResult() { cmd = CMD_LOGIN_RESULT; dataLength = sizeof(LoginResult); } int result; }; struct LoginOutResult:public DataHeader { LoginOutResult() { cmd = CMD_LOGIN_OUT_RESULT; dataLength = sizeof(LoginOutResult); } int result; }; struct NewUserJoin :public DataHeader { NewUserJoin() { cmd = CMD_NEW_USER_JOIN; dataLength = sizeof(NewUserJoin); sock = 0; } int sock; }; void InitSock() { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData) != 0) return; if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); return; } } int ProcessPart(SOCKET cSock) { char szRecv[1024] = {}; //接受客户端请求数据 int nLen = recv(cSock, szRecv, sizeof(DataHeader), 0); DataHeader *header = (DataHeader*)szRecv; if (nLen <= 0) { printf("与服务器断开连接,任务结束\n", cSock); return -1; } printf("收到命令:%d 数据长度:%d\n", header->cmd, header->dataLength); switch (header->cmd) { case CMD_LOGIN_RESULT: { recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); LoginResult* loginRes = (LoginResult*)szRecv; printf("收到服务器消息:CMD_LOGIN_RESULT 数据长度%d结果%d\n", loginRes->dataLength, loginRes->result); break; } case CMD_LOGIN_OUT_RESULT: { recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); LoginOutResult* loginOutRes = (LoginOutResult*)szRecv; printf("收到服务器消息:CMD_LOGIN_OUT_RESULT: 数据长度:%d 结果:%d\n", loginOutRes->dataLength, loginOutRes->result); break; } case CMD_NEW_USER_JOIN: { recv(cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); NewUserJoin* loginOutRes = (NewUserJoin*)szRecv; printf("收到服务器消息:CMD_NEW_USER_JOIN: 数据长度:%d\n", loginOutRes->dataLength); break; } default: { DataHeader header = { CMD_ERROR, 0 }; send(cSock, (const char *)&header, sizeof(DataHeader), 0); break; } } return 0; } void CmdThread(SOCKET sock) { while (g_bRun) { char cmdBuf[256] = {}; scanf("%s", cmdBuf); if (0 == strcmp(cmdBuf, "exit")) { printf("退出\n"); return; } else if (0 == strcmp(cmdBuf,"login")) { Login login = {}; strcpy(login.userName, "赵文军"); login.passWord = 123; send(sock, (const char *)&login, sizeof(login), 0); } else if (0 == strcmp(cmdBuf, "loginOut")) { LoginOut loginOut = {}; strcpy(loginOut.userName, "赵文军"); send(sock, (const char *)&loginOut, sizeof(loginOut), 0); } } } int main() { //启动Windows socket 2的环境 InitSock(); //建立socket SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SOCKET_ERROR == sock) { TRACE(_T("ERROR:创建socket失败")); return 0; } sockaddr_in sin = {}; sin.sin_family = AF_INET; sin.sin_port = htons(10000); //host to net unsigned short sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //连接 if (SOCKET_ERROR == connect(sock, (sockaddr *)&sin, sizeof(sockaddr_in))) { int nError = GetLastError(); TRACE(_T("ERROR:连接socket失败")); return 0; } char cmdBuf[128] = {}; //启动线程 std::thread t1(CmdThread, sock); //主线程和子线程分离,防止主线程退出,子线程还在运行报错 t1.detach(); while (g_bRun) { fd_set fdReads; FD_ZERO(&fdReads); FD_SET(sock, &fdReads); timeval time = { 2, 0 }; int ret = select(sock, &fdReads, NULL, NULL, &time);if (ret < 0) { printf("select任务结束1\n"); break; } if (FD_ISSET(sock, &fdReads)) { FD_CLR(sock, &fdReads); if (-1 == ProcessPart(sock)) { printf("select任务结束2\n"); break; } } } //关闭socket closesocket(sock); WSACleanup(); return 0; }
111