服務端:
//vc的第一套socket,第二套socket,可能存在衝突 #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_RET, CMD_LOGOUT, CMD_LOGOUT_RET, CMD_NEW_USER_JOIN, CMD_ERROR }; struct DataHeader { int dataLength; CMD cmd; }; 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_RET; ret = 0; } int ret; }; 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_RET; ret = 0; } int ret; }; struct NewUserJoin :public DataHeader { NewUserJoin() { dataLength = sizeof(NewUserJoin); cmd = CMD_NEW_USER_JOIN; sock = -1; } int sock; }; int processor(SOCKET _cSock) { //5 接收客戶端數據 char szRecv[1024] = {}; int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0); if (nLen <= 0) { printf("客戶端%d已退出。\n", _cSock); return -1; } DataHeader* pHeader = (DataHeader*)szRecv; switch (pHeader->cmd) { case CMD_LOGIN: { recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); Login *pLogin = (Login*)szRecv; printf("客戶端%d,cmd logint,user:%s,password:%s\n", _cSock, pLogin->userName, pLogin->passWord); //---------------------------- //---------------------------- LoginResult longinRet = {}; send(_cSock, (const char*)&longinRet, sizeof(LoginResult), 0); } break; case CMD_LOGOUT: { recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); LogOut *pLogOut = (LogOut*)szRecv; printf("客戶端%d,cmd logout,user:%s\n", _cSock, pLogOut->userName); //---------------------------- LogOutResult logOutRet = {}; send(_cSock, (const char*)&logOutRet, sizeof(LogOutResult), 0); } break; default: { //剩餘的垃圾數據是否要讀取出來? if (pHeader->dataLength >0) { recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); } DataHeader header = {}; header.cmd = CMD_ERROR; header.dataLength = 0; send(_cSock, (const char*)&header, sizeof(DataHeader), 0); } break; } return 0; } std::vector<SOCKET> g_clients; 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 //_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); _sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用127.0.0.1可以防止外網訪問 //啓用本機全部的ip地址可以使用,INADDR_ANY if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in))) { printf("錯誤,綁定網絡端口失敗...\n"); } else { printf("綁定網絡端口成功...\n"); } //3 listen 監聽網絡端口 if (SOCKET_ERROR == listen(_sock, 5)) { printf("錯誤,監聽網絡端口失敗...\n"); } else { printf("監聽網絡端口成功...\n"); } while (true) { fd_set fdRead; 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); for (size_t n = 0; n<g_clients.size(); n++) { FD_SET(g_clients[n], &fdRead); } timeval t = {1,0}; int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, &t); if (ret < 0) { printf("select任務結束。\n"); break; } if (FD_ISSET(_sock, &fdRead)) { //4 accept等待接受客戶端連接 sockaddr_in clientAddr = {}; int nAddrLen = sizeof(clientAddr); SOCKET _cSock = INVALID_SOCKET; _cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen); if (INVALID_SOCKET == _cSock) { printf("錯誤,接受到無效客戶端socket...\n"); } else { for (int n = 0; n<g_clients.size(); n++) { NewUserJoin msg; msg.sock = _cSock; send(g_clients[n], (const char*)&msg, 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++) { auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]); if (iter != g_clients.end()) { if (-1 == processor(fdRead.fd_array[n])) { closesocket(*iter); g_clients.erase(iter); } } } } //6 關閉套接字closesocket for (size_t n = 0; n<g_clients.size(); n++) { closesocket(g_clients[n]); } closesocket(_sock); //清除Windows socket環境 WSACleanup(); printf("已退出。\n"); getchar(); return 0; }
客戶端:
編譯指令g++ main.cpp -std=c++11 -pthread -o client
#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS //也可以放到工程屬性,預處理中 #define _CRT_SECURE_NO_WARNINGS #ifdef _WIN32 #include <windows.h> #include <WinSock2.h> #else #include<unistd.h> #include<arpa/inet.h> #include<string.h> #define SOCKET int #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR (-1) #endif #include <stdio.h> #include <thread> #pragma comment(lib, "ws2_32.lib") enum CMD { CMD_LOGIN, CMD_LOGIN_RET, CMD_LOGOUT, CMD_LOGOUT_RET, CMD_NEW_USER_JOIN, CMD_ERROR }; struct DataHeader { int dataLength; CMD cmd; }; 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_RET; ret = 1; } int ret; }; 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_RET; ret = 1; } int ret; }; struct NewUserJoin :public DataHeader { NewUserJoin() { dataLength = sizeof(NewUserJoin); cmd = CMD_NEW_USER_JOIN; sock = -1; } int sock; }; bool g_bRun = true; int processor(SOCKET _sock) { //5 接收客戶端數據 char szRecv[1024] = {}; int nLen = recv(_sock, szRecv, sizeof(DataHeader), 0); if (nLen <= 0) { printf("服務端%d已退出。\n", _sock); return -1; } DataHeader* pHeader = (DataHeader*)szRecv; switch (pHeader->cmd) { case CMD_LOGIN_RET: { recv(_sock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); LoginResult *pLogin = (LoginResult*)szRecv; printf("login result:%d\n", pLogin->ret); } break; case CMD_LOGOUT_RET: { recv(_sock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); LogOutResult *pLogOut = (LogOutResult*)szRecv; printf("log out result:%d\n", pLogOut->ret); } break; case CMD_NEW_USER_JOIN: { recv(_sock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); NewUserJoin *pMsg = (NewUserJoin*)szRecv; printf("new user:%d join.\n", pMsg->sock); } break; } return 0; } void cmdThread(SOCKET _sock) { while (true) { //3輸入請求命令 char cmdBuf[128] = {}; scanf("%s", cmdBuf); getchar(); //4處理請求 if (0 == strcmp(cmdBuf, "exit")) { printf("退出cmdThread線程\n"); g_bRun = false; return; } else if (0 == strcmp(cmdBuf, "login")) { Login login = {}; strcpy(login.userName, "jj"); strcpy(login.passWord, "ww"); send(_sock, (const char*)&login, sizeof(Login), 0); } else if (0 == strcmp(cmdBuf, "logout")) { LogOut log_out = {}; strcpy(log_out.userName, "jj"); send(_sock, (const char*)&log_out, sizeof(LogOut), 0); } else { printf("wrong cmd, please input cmd again.\n"); } } } int main() { #ifdef _WIN32 //啓動Windows socket 2.x環境 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif //--------------------------- //--用Socket API建立簡易TCP客戶端 //1 建立一個socket SOCKET _sock = INVALID_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);//host to net unsigned #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.10.157"); #endif int ret = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("錯誤,連接服務端失敗...\n"); return 0; } else { printf("連接服務端成功...\n"); } std::thread t(cmdThread, _sock); t.detach(); while (g_bRun) { fd_set fdRead; FD_ZERO(&fdRead); FD_SET(_sock, &fdRead); timeval t = { 1,0 }; int ret = select(_sock + 1, &fdRead, 0, 0, &t); if (ret < 0) { printf("select任務結束1。\n"); break; } if (FD_ISSET(_sock, &fdRead)) { if (-1 == processor(_sock)) { printf("select任務結束2。\n"); break; } } } #ifdef _WIN32 //關閉套接字closesocket closesocket(_sock); //清除Windows socket環境 WSACleanup(); #else close(_sock); #endif printf("已退出"); getchar(); return 0; }