客户端:
DataHeader.hpp
#ifndef _MessageHeader_hpp_ #define _MessageHeader_hpp_ 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; }; #endif
EasyTcpClient.hpp
#ifndef _EasyTcpClient_hpp_ #define _EasyTcpClient_hpp_ #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 "DataHeader.hpp" class EasyTcpClient { public: EasyTcpClient() { _sock = INVALID_SOCKET; } //虚析构函数 virtual ~EasyTcpClient() { Close(); } //初始化socket void InitSocket() { #ifdef _WIN32 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif // _WIN32 if (_sock != INVALID_SOCKET) { Close(); } _sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == _sock) { printf("错误,建立Socket失败...\n"); } else { printf("建立Socket成功...\n"); } } int Connect(const char* ip, unsigned short port) { if (INVALID_SOCKET == _sock) { InitSocket(); } sockaddr_in _sin = {}; _sin.sin_family = AF_INET; _sin.sin_port = htons(port); #ifdef _WIN32 _sin.sin_addr.S_un.S_addr = inet_addr(ip); #else _sin.sin_addr.s_addr = inet_addr(ip); #endif int ret = connect(_sock, (const sockaddr*)&_sin, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("错误,连接服务端失败...\n"); } else { printf("连接服务端成功...\n"); } return ret; } bool isRun() { return (_sock != INVALID_SOCKET); } bool onRun() { if (isRun()) { 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) { Close(); printf("select任务结束1。\n"); return false; } if ( FD_ISSET(_sock, &fdRead) ) { if (-1 == RecvData()) { Close(); printf("select任务结束2。\n"); return false; } } } } //接收数据 处理粘包 拆分包 int RecvData() { char szRecv[1024] = {}; int nLen = recv(_sock, szRecv, sizeof(DataHeader), 0); if (nLen <= 0) { printf("服务端%d已退出。\n", _sock); return -1; } DataHeader* pHeader = (DataHeader*)szRecv; recv(_sock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); onNetMsg(pHeader); return 0; } void onNetMsg(DataHeader* pData) { switch (pData->cmd) { case CMD_LOGIN_RET: { LoginResult *pLogin = (LoginResult*)pData; printf("login result:%d\n", pLogin->ret); } break; case CMD_LOGOUT_RET: { LogOutResult *pLogOut = (LogOutResult*)pData; printf("log out result:%d\n", pLogOut->ret); } break; case CMD_NEW_USER_JOIN: { NewUserJoin *pMsg = (NewUserJoin*)pData; printf("new user:%d join.\n", pMsg->sock); } break; } } //发送数据 int SendData(DataHeader* pHeader) { if (isRun() && pHeader) { return send(_sock, (const char*)pHeader, pHeader->dataLength, 0); } return SOCKET_ERROR; } void Close() { if (_sock != INVALID_SOCKET) { #ifdef _WIN32 closesocket(_sock); //清除Windows socket环境 WSACleanup(); #else close(_sock); #endif _sock = INVALID_SOCKET; } } private: SOCKET _sock; }; #endif
main.cpp
#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS //也可以放到工程属性,预处理中 #define _CRT_SECURE_NO_WARNINGS #pragma comment(lib, "ws2_32.lib") #endif #include <stdio.h> #include <thread> #include "EasyTcpClient.hpp" void cmdThread(EasyTcpClient* pClient) { while (true) { //3输入请求命令 char cmdBuf[128] = {}; scanf("%s", cmdBuf); getchar(); //4处理请求 if (0 == strcmp(cmdBuf, "exit")) { printf("退出cmdThread线程\n"); pClient->Close(); return; } else if (0 == strcmp(cmdBuf, "login")) { Login login = {}; strcpy(login.userName, "jj"); strcpy(login.passWord, "ww"); pClient->SendData(&login); } else if (0 == strcmp(cmdBuf, "logout")) { LogOut log_out = {}; strcpy(log_out.userName, "jj"); pClient->SendData(&log_out); } else { printf("wrong cmd, please input cmd again.\n"); } } } int main() { EasyTcpClient client; client.Connect("192.168.10.157", 4567); std::thread t(cmdThread, &client); t.detach(); while ( client.isRun() ) { client.onRun(); } client.Close(); printf("已退出"); getchar(); return 0; }
服务端:
DataHeader.hpp
#ifndef _MessageHeader_hpp_ #define _MessageHeader_hpp_ 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 = 7; } 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 = 9; } int ret; }; struct NewUserJoin :public DataHeader { NewUserJoin() { dataLength = sizeof(NewUserJoin); cmd = CMD_NEW_USER_JOIN; sock = -1; } int sock; }; #endif
EasyTcpServer.hpp
#ifndef _EasyTcpServer_hpp_ #define _EasyTcpServer_hpp_ #ifdef _WIN32 //vc的第一套socket,第二套socket,可能存在冲突 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS //也可以放到工程属性,预处理中 #include <windows.h> #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") #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 <vector> #include "DataHeader.hpp" class EasyTcpServer { public: EasyTcpServer() { _sock = INVALID_SOCKET; } virtual ~EasyTcpServer() { Close(); } //初始化socket SOCKET InitSocket() { #ifdef _WIN32 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif // _WIN32 if (_sock != INVALID_SOCKET) { Close(); } _sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == _sock) { printf("错误,建立Socket失败...\n"); } else { printf("建立Socket成功...\n"); } return _sock; } //绑定端口号 int Bind(const char* ip, unsigned short port) { sockaddr_in _sin = {}; _sin.sin_family = AF_INET; _sin.sin_port = htons(4567);//host to net unsigned short //使用127.0.0.1可以防止外网访问 //启用本机全部的ip地址可以使用,INADDR_ANY #ifdef _WIN32 if (ip) { _sin.sin_addr.S_un.S_addr = inet_addr(ip); } else { _sin.sin_addr.S_un.S_addr = INADDR_ANY; } #else if (ip) { _sin.sin_addr.s_addr = inet_addr(ip); } else { _sin.sin_addr.s_addr = INADDR_ANY; } #endif int ret = bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in)); if (SOCKET_ERROR == ret) { printf("错误,绑定网络端口失败...\n"); } else { printf("绑定网络端口成功...\n"); } return ret; } //监听端口号 int Listen(int cnt) { int ret = listen(_sock, cnt); if (SOCKET_ERROR == ret) { printf("错误,监听网络端口失败...\n"); } else { printf("监听网络端口成功...\n"); } return ret; } //接收客户端连接 int Accept() { sockaddr_in clientAddr = {}; int nAddrLen = sizeof(clientAddr); 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 { NewUserJoin msg; msg.sock = _cSock; Send2All(&msg); clients.push_back(_cSock); printf("新客户端加入:socket=%d,IP=%s\n", _cSock, inet_ntoa(clientAddr.sin_addr)); } return _cSock; } //关闭socket void Close() { if (_sock != INVALID_SOCKET) { #ifdef _WIN32 // 关闭套接字 for (size_t n = 0; n<clients.size(); n++) { closesocket(clients[n]); } closesocket(_sock); //清除Windows socket环境 WSACleanup(); #else for (size_t n = 0; n<clients.size(); n++) { close(clients[n]); } close(_sock); #endif clients.clear(); } } //处理网络消息 bool OnRun() { if (!isRun()) { return false; } 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); SOCKET maxSock = _sock; for (size_t n = 0; n<clients.size(); n++) { FD_SET(clients[n], &fdRead); if (maxSock < clients[n]) { maxSock = clients[n]; } } timeval t = { 1,0 }; int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExp, &t); if (ret < 0) { printf("select任务结束。\n"); Close(); return false; } if (FD_ISSET(_sock, &fdRead)) { FD_CLR(_sock, &fdRead);//从集合移除了_sock Accept(); } for (int n = (int)clients.size() - 1; n >= 0; n--) { if (FD_ISSET(clients[n], &fdRead)) { if (-1 == RecvData(clients[n])) { auto iter = clients.begin() + n; if (iter != clients.end()) { clients.erase(iter); } } } } return true; } //是否工作中 bool isRun() { return _sock != INVALID_SOCKET; } //接收数据 处理粘包 拆分包 int RecvData(SOCKET _cSock) { //5 接收客户端数据 char szRecv[1024] = {}; int nLen = (int)recv(_cSock, szRecv, sizeof(DataHeader), 0); if (nLen <= 0) { printf("客户端%d已退出。\n", _cSock); return -1; } DataHeader* pHeader = (DataHeader*)szRecv; recv(_cSock, szRecv + sizeof(DataHeader), pHeader->dataLength - sizeof(DataHeader), 0); onNetMsg(_cSock, pHeader); return 0; } //响应网络消息 virtual void onNetMsg(SOCKET _cSock, DataHeader* header) { switch (header->cmd) { case CMD_LOGIN: { Login *pLogin = (Login*)header; printf("客户端%d,cmd logint,user:%s,password:%s\n", _cSock, pLogin->userName, pLogin->passWord); //---------------------------- //---------------------------- LoginResult longinRet = {}; Send(_cSock, &longinRet); } break; case CMD_LOGOUT: { LogOut *pLogOut = (LogOut*)header; printf("客户端%d,cmd logout,user:%s\n", _cSock, pLogOut->userName); //---------------------------- LogOutResult logOutRet = {}; Send(_cSock, &logOutRet); } break; default: { DataHeader header = {}; header.cmd = CMD_ERROR; header.dataLength = 0; Send(_cSock, &header); } break; } } //指定socket发送数据 int Send(SOCKET _cSock, DataHeader* header) { if (isRun() && header) { return send(_cSock, (const char*)header, header->dataLength, 0); } return SOCKET_ERROR; } void Send2All(DataHeader* header) { if (isRun() && header) { for (int n = (int)clients.size() - 1; n >= 0; n--) { Send(clients[n], header); } } } private: SOCKET _sock; std::vector<SOCKET> clients; }; #endif
main.cpp
#include "EasyTcpServer.hpp" int main() { EasyTcpServer server; server.InitSocket(); server.Bind(nullptr, 4567); server.Listen(5); while ( server.isRun() ) { server.OnRun(); } server.Close(); printf("已退出。\n"); getchar(); return 0; }