server
#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN //这个宏尽量避免早期库和现代库的冲突 #define _WINSOCK_DEPRECATED_NO_WARNINGS //避免inet_ntoa不让用,报错 #include//早期 #include //现代 #pragma comment(lib,"ws2_32.lib")//也可以在属性中添加库 #else #include #include #include #define SOCKET int #define INVALID_SOCKET (SOCKET)(~0) //(-1) #define SOCKET_ERROR (-1) #endif #include #include #include 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; sock = 0; } int sock; }; std::vector g_clients; int process(SOCKET _cSock) { //缓冲区 char szRecv[1024] = {}; //5.接受客户端数据 int nLen = (int)recv(_cSock, szRecv, sizeof(DataHeader), 0); DataHeader* header = (DataHeader*)szRecv; if (nLen <= 0) { printf("客户端 已退出,任务结束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("收到客户端 请求:CMD_LOGIN 数据长度:%d userName=%s PassWord=%sn", _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("收到客户端 请求:CMD_LOGOUT 数据长度:%d userName=%sn", _cSock, logout->dataLength, logout->userName); //忽略 判断用户名密码是否正确的 过程 LogoutResult ret; send(_cSock, (char*)&ret, sizeof(LoginResult), 0); } break; default: DataHeader header = { 0,CMD_ERROR }; send(_cSock, (char*)&header, sizeof(header), 0); break; } return 0; } int main() { #ifdef _WIN32 WORD ver = MAKEWORD(2, 2);//版本号 WSADATA dat; WSAStartup(ver, &dat); #endif //------------------------------------ //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); #ifdef _WIN32 _sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1");//一个主机不止一个IP地址,随便一个都可以访问主机 #else _sin.sin_addr.s_addr = INADDR_ANY; #endif if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == SOCKET_ERROR) { printf("错误,绑定网络端口失败...n"); } else { printf("绑定网络端口成功...n"); } //3.listen监听网络端口 if (listen(_sock, 5) == SOCKET_ERROR) { printf("错误,监听网络端口失败...n"); } else { printf("监听网络端口成功...n"); } while (true) { //伯克利套接字 fd_set fdRead;//描述符(socket)集合 fd_set fdWrite; fd_set fdExp; FD_ZERO(&fdRead);//清空 FD_ZERO(&fdWrite); FD_ZERO(&fdExp); //将描述符加入集合 FD_SET(_sock, &fdRead); FD_SET(_sock, &fdWrite); FD_SET(_sock, &fdExp); SOCKET maxSock = _sock; for (int n = (int)g_clients.size() - 1; n >= 0; n--) { FD_SET(g_clients[n], &fdRead); if (maxSock < g_clients[n]) { maxSock = g_clients[n]; } } //nfds 为一个整数值 是指集合fd_set中,所有描述符的范围,而不是数量,所有描述符最大值,在windows无所谓 timeval t = { 1,0 }; int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExp, &t); if (ret < 0) { printf("select任务结束n"); break; } //判断描述符是否在集合中 if (FD_ISSET(_sock, &fdRead)) { FD_CLR(_sock, &fdRead); //4.accept等待客户端连接 sockaddr_in clientAddr = {}; int nAddrLen = sizeof(sockaddr_in); SOCKET _cSock = INVALID_SOCKET; #ifdef _WIN32 _cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen); #else _cSock = accept(_sock, (sockaddr*)&clientAddr, (socklen_t*)&nAddrLen); #endif 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 (int n = (int)g_clients.size() - 1; n >= 0; n--) { if (FD_ISSET(g_clients[n], &fdRead)) { if (-1 == process(g_clients[n])) { std::vector ::iterator iter = g_clients.begin()+n; if (iter != g_clients.end()) { g_clients.erase(iter); } } } } //printf("空闲时处理其他业务...n"); } #ifdef _WIN32 for (size_t n = g_clients.size() - 1; n >= 0; n--) { closesocket(g_clients[n]); } //8.关闭套接字socket closesocket(_sock); //------------------------------------ //清除Windows socket环境 WSACleanup(); #else for (int n = (int)g_clients.size() - 1; n >= 0; n--) { close(g_clients[n]); } close(_sock); #endif printf("已退出,任务结束n"); getchar(); return 0; }
client
#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN //这个宏尽量避免早期库和现代库的冲突 #include//早期 #include //现代 #pragma comment(lib,"ws2_32.lib")//也可以在属性中添加库 #else #include //uni std #include #include #define SOCKET int #define INVALID_SOCKET (SOCKET)(~0) //(-1) #define SOCKET_ERROR (-1) #endif #include #include 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; sock = 0; } int sock; }; int process(SOCKET _cSock) { //缓冲区 char szRecv[1024] = {}; //5.接受客户端数据 int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0); DataHeader* header = (DataHeader*)szRecv; if (nLen <= 0) { printf("与服务器断开连接,任务结束n"); 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 数据长度:%dn", 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 数据长度:%dn", 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 数据长度:%dn", header->dataLength); } break; } } bool g_bRun = true; void cmdThread(SOCKET sock) { while (true) { char cmdBuf[256] = {}; scanf("%s", cmdBuf); if (0 == strcmp(cmdBuf, "exit")) { g_bRun = false; printf("退出cmdThread线程n"); break; } else if (0 == strcmp(cmdBuf, "login")) { Login login; strcpy(login.userName, "Dragon"); strcpy(login.PassWord, "Dragonpwd"); send(sock, (const char*)&login, sizeof(Login), 0); } else if (0 == strcmp(cmdBuf, "logout")) { Logout logout; strcpy(logout.userName, "Dragon"); send(sock, (const char*)&logout, sizeof(Logout), 0); } else { printf("不支持的命令n"); } } } int main() { #ifdef _WIN32 WORD ver = MAKEWORD(2, 2);//版本号 WSADATA dat; WSAStartup(ver, &dat); #endif //------------------------------------ //1.建立一个socket SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == _sock) { printf("错误,建立socket失败...n"); } else { printf("建立socket成功...n"); } //2.连接服务器 connect sockaddr_in _sin = {}; _sin.sin_family = AF_INET; _sin.sin_port = htons(4567); #ifdef _WIN32 _sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); #else _sin.sin_addr.s_addr = inet_addr("192.168.136.1"); #endif int ret = connect(_sock + 1, (sockaddr*)&_sin, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("错误,连接服务器失败...n"); } else { printf("连接服务器成功...n"); } //启动线程 std::thread t1(cmdThread, _sock); t1.detach(); while (g_bRun) { fd_set fdReads; FD_ZERO(&fdReads); FD_SET(_sock, &fdReads); timeval t = { 1,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 == process(_sock)) { printf("select任务结束[2]n"); } } //线程thread //printf("空闲时间处理其他业务...n"); } //7.关闭套接字closesocket #ifdef _WIN32 closesocket(_sock); WSACleanup(); #else close(_sock); #endif printf("已退出n"); getchar(); return 0; }



