登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
登录
注册
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
1
Star
0
简锋
/
服务器编程
代码
Issues
17
Pull Requests
0
Wiki
流水线
服务
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
封装server类
待办的
#I43J5H
简锋
拥有者
创建于
2021-08-02 16:53
#ifndef _EasyTcpServer_hpp_ #define _EasyTcpServer_hpp_ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <windows.h> #include <WinSock2.h> #include <WS2tcpip.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 <cstdio> #include <vector> #include "MessageHeader.hpp" class EasyTcpServer { public: EasyTcpServer() { _sock = INVALID_SOCKET; } virtual ~EasyTcpServer() { Close(); } // 初始化socket void InitSocket() { #ifdef _WIN32 //启动Windows socket 2.x环境 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif if (INVALID_SOCKET != _sock) { printf("<socket=%d>关闭旧连接...\n", _sock); Close(); } _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == _sock) { printf("错误,建立Socket失败...\n"); } else { printf("建立Socket成功...\n"); } } // 绑定iph和端口号 int Bind(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 if(ip) { _sin.sin_addr.S_un.S_addr = inet_pton(AF_INET , ip , &_sin.sin_addr.S_un.S_addr); } 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("error , bind port<%d> failed\n" , port); } else { printf("bind port<%d> success\n" , port); } return ret; } // 监听端口号 int Listen(int backlog) { int ret = listen(_sock, backlog); if ( ret == SOCKET_ERROR) { printf("socket=<%d> listen failed\n" , _sock); } else { printf("socket<%d> listen success\n" , _sock); } return ret; } // 接收客户端连接 int Accept() { // 4 accept 等待客户端连接 sockaddr_in clientAddr = {}; int cAddrlen = sizeof(sockaddr_in); SOCKET csock = INVALID_SOCKET; #ifdef _WIN32 csock = accept(_sock, (sockaddr*)&clientAddr, &cAddrlen); #else csock = accept(sock, (sockaddr*)&clientAddr, (socklen_t*)&cAddrlen); #endif if (csock == INVALID_SOCKET) { printf("socket<%d>failed , recieve a invalid client socket\n", _sock); } else { NewUserJoin userJoin; SendDataToAll(&userJoin); g_clients.push_back(csock); printf("socket<%d>new client join:socket = %d , IP = %s \n", _sock , csock, inet_ntoa(clientAddr.sin_addr)); } return csock; } // 关闭socket void Close() { if (INVALID_SOCKET != _sock) { #ifdef _WIN32 for (int i = g_clients.size() - 1; i >= 0; i--) { closesocket(g_clients[i]); } // 6 关闭套接字 closesocket(_sock); WSACleanup(); #else for (int i = g_clients.size() - 1; i >= 0; i--) { close(g_clients[i]); } close(sock); #endif } } // 处理网络消息 bool OnRun() { if (isRun()) { // 伯克利套接字 BSD Socket fd_set fdRead; fd_set fdWrite; fd_set fdExcep; //清理集合 FD_ZERO(&fdRead); FD_ZERO(&fdWrite); FD_ZERO(&fdExcep); //描述符(socket)加入集合 FD_SET(_sock, &fdRead); FD_SET(_sock, &fdWrite); FD_SET(_sock, &fdExcep); SOCKET maxSock = _sock; for (int i = (int)g_clients.size() - 1; i >= 0; i--) { FD_SET(g_clients[i], &fdRead); if (maxSock < g_clients[i]) { maxSock = g_clients[i]; } } // nfds是一个整数值,是指fd_set集合中所有描述符(socket)的范围,而不是数量 // 既是所有文件描述符的最大值+1,在Windows中这个参数可以写0 timeval t = { 1 ,0 }; int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExcep, &t); if (ret < 0) { printf("select massion end\n"); Close(); return false; } //判断socket是否在集合中 if (FD_ISSET(_sock, &fdRead)) { FD_CLR(_sock, &fdRead); Accept(); } for (int i = (int)g_clients.size() - 1; i >= 0; i--) { if (FD_ISSET(g_clients[i], &fdRead)) { if (-1 == RecvData(g_clients[i])) { // std::vector<SOCKET>::iterator iter auto iter = g_clients.begin() + i; if (iter != g_clients.end()) { g_clients.erase(iter); } } } } return true; } return false; } // 是否工作中 bool isRun() { return _sock != INVALID_SOCKET; } // 接收数据 处理粘包 拆包 int RecvData(SOCKET csock) { // 缓冲区 char szRecv[1024] = {}; // 接收客户端的请求数据 int len = recv(csock, szRecv, sizeof(DataHeader), 0); DataHeader* header = (DataHeader*)szRecv; if (len <= 0) { printf("client = <Socket = %d> exit\n", csock); return -1; } recv(csock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); OnNetMsg(csock, header); return 0; } // 响应网络消息 virtual void OnNetMsg(SOCKET csock , DataHeader* header) { // 处理请求 switch (header->cmd) { case CMD_LOGIN: { Login* login = (Login*)header; printf("收到<Socket = %d>:CMD_LOGIN , 数据长度: %d , 用户名:%s" "密码: %s \n", csock, login->dataLength, login->userName, login->PassWord); // 忽略判断用户名密码是否正确的过程 LoginResult ret; send(csock, (char*)&ret, sizeof(LoginResult), 0); } break; case CMD_LOGOUT: { Logout* logout = (Logout*)header; printf("收到<Socket = %d>:CMD_LOGOUT , 数据长度: %d , 用户名:%s" "\n", csock, logout->dataLength, logout->userName); // 忽略判断用户名密码是否正确的过程 LogoutResult ret; send(csock, (char*)&ret, sizeof(LogoutResult), 0); } break; default: { DataHeader header = { 0 , CMD_ERROR }; send(csock, (char*)&header, sizeof(DataHeader), 0); } break; } } // 发送给指定Socket消息 int SendData(SOCKET csock , DataHeader* header) { if (isRun() && header) { return send(csock, (const char*)header, header->dataLength, 0); } return SOCKET_ERROR; } void SendDataToAll(DataHeader* header) { if (isRun() && header) { for (int i = g_clients.size() - 1; i >= 0; i--) { SendData(g_clients[i], header); } } } private: SOCKET _sock; std::vector<SOCKET> g_clients; }; #endif // _EasyTcpServer_hpp_ --------------------------------------------------------------- #include "EasyTcpServer.hpp" int main() { EasyTcpServer server; server.InitSocket(); server.Bind(nullptr, 4567); server.Listen(5); while (server.isRun()) { server.OnRun(); //printf("空闲时间...\n"); } server.Close(); printf("任务完成\n"); getchar(); return 0; }
#ifndef _EasyTcpServer_hpp_ #define _EasyTcpServer_hpp_ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <windows.h> #include <WinSock2.h> #include <WS2tcpip.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 <cstdio> #include <vector> #include "MessageHeader.hpp" class EasyTcpServer { public: EasyTcpServer() { _sock = INVALID_SOCKET; } virtual ~EasyTcpServer() { Close(); } // 初始化socket void InitSocket() { #ifdef _WIN32 //启动Windows socket 2.x环境 WORD ver = MAKEWORD(2, 2); WSADATA dat; WSAStartup(ver, &dat); #endif if (INVALID_SOCKET != _sock) { printf("<socket=%d>关闭旧连接...\n", _sock); Close(); } _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == _sock) { printf("错误,建立Socket失败...\n"); } else { printf("建立Socket成功...\n"); } } // 绑定iph和端口号 int Bind(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 if(ip) { _sin.sin_addr.S_un.S_addr = inet_pton(AF_INET , ip , &_sin.sin_addr.S_un.S_addr); } 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("error , bind port<%d> failed\n" , port); } else { printf("bind port<%d> success\n" , port); } return ret; } // 监听端口号 int Listen(int backlog) { int ret = listen(_sock, backlog); if ( ret == SOCKET_ERROR) { printf("socket=<%d> listen failed\n" , _sock); } else { printf("socket<%d> listen success\n" , _sock); } return ret; } // 接收客户端连接 int Accept() { // 4 accept 等待客户端连接 sockaddr_in clientAddr = {}; int cAddrlen = sizeof(sockaddr_in); SOCKET csock = INVALID_SOCKET; #ifdef _WIN32 csock = accept(_sock, (sockaddr*)&clientAddr, &cAddrlen); #else csock = accept(sock, (sockaddr*)&clientAddr, (socklen_t*)&cAddrlen); #endif if (csock == INVALID_SOCKET) { printf("socket<%d>failed , recieve a invalid client socket\n", _sock); } else { NewUserJoin userJoin; SendDataToAll(&userJoin); g_clients.push_back(csock); printf("socket<%d>new client join:socket = %d , IP = %s \n", _sock , csock, inet_ntoa(clientAddr.sin_addr)); } return csock; } // 关闭socket void Close() { if (INVALID_SOCKET != _sock) { #ifdef _WIN32 for (int i = g_clients.size() - 1; i >= 0; i--) { closesocket(g_clients[i]); } // 6 关闭套接字 closesocket(_sock); WSACleanup(); #else for (int i = g_clients.size() - 1; i >= 0; i--) { close(g_clients[i]); } close(sock); #endif } } // 处理网络消息 bool OnRun() { if (isRun()) { // 伯克利套接字 BSD Socket fd_set fdRead; fd_set fdWrite; fd_set fdExcep; //清理集合 FD_ZERO(&fdRead); FD_ZERO(&fdWrite); FD_ZERO(&fdExcep); //描述符(socket)加入集合 FD_SET(_sock, &fdRead); FD_SET(_sock, &fdWrite); FD_SET(_sock, &fdExcep); SOCKET maxSock = _sock; for (int i = (int)g_clients.size() - 1; i >= 0; i--) { FD_SET(g_clients[i], &fdRead); if (maxSock < g_clients[i]) { maxSock = g_clients[i]; } } // nfds是一个整数值,是指fd_set集合中所有描述符(socket)的范围,而不是数量 // 既是所有文件描述符的最大值+1,在Windows中这个参数可以写0 timeval t = { 1 ,0 }; int ret = select(maxSock + 1, &fdRead, &fdWrite, &fdExcep, &t); if (ret < 0) { printf("select massion end\n"); Close(); return false; } //判断socket是否在集合中 if (FD_ISSET(_sock, &fdRead)) { FD_CLR(_sock, &fdRead); Accept(); } for (int i = (int)g_clients.size() - 1; i >= 0; i--) { if (FD_ISSET(g_clients[i], &fdRead)) { if (-1 == RecvData(g_clients[i])) { // std::vector<SOCKET>::iterator iter auto iter = g_clients.begin() + i; if (iter != g_clients.end()) { g_clients.erase(iter); } } } } return true; } return false; } // 是否工作中 bool isRun() { return _sock != INVALID_SOCKET; } // 接收数据 处理粘包 拆包 int RecvData(SOCKET csock) { // 缓冲区 char szRecv[1024] = {}; // 接收客户端的请求数据 int len = recv(csock, szRecv, sizeof(DataHeader), 0); DataHeader* header = (DataHeader*)szRecv; if (len <= 0) { printf("client = <Socket = %d> exit\n", csock); return -1; } recv(csock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0); OnNetMsg(csock, header); return 0; } // 响应网络消息 virtual void OnNetMsg(SOCKET csock , DataHeader* header) { // 处理请求 switch (header->cmd) { case CMD_LOGIN: { Login* login = (Login*)header; printf("收到<Socket = %d>:CMD_LOGIN , 数据长度: %d , 用户名:%s" "密码: %s \n", csock, login->dataLength, login->userName, login->PassWord); // 忽略判断用户名密码是否正确的过程 LoginResult ret; send(csock, (char*)&ret, sizeof(LoginResult), 0); } break; case CMD_LOGOUT: { Logout* logout = (Logout*)header; printf("收到<Socket = %d>:CMD_LOGOUT , 数据长度: %d , 用户名:%s" "\n", csock, logout->dataLength, logout->userName); // 忽略判断用户名密码是否正确的过程 LogoutResult ret; send(csock, (char*)&ret, sizeof(LogoutResult), 0); } break; default: { DataHeader header = { 0 , CMD_ERROR }; send(csock, (char*)&header, sizeof(DataHeader), 0); } break; } } // 发送给指定Socket消息 int SendData(SOCKET csock , DataHeader* header) { if (isRun() && header) { return send(csock, (const char*)header, header->dataLength, 0); } return SOCKET_ERROR; } void SendDataToAll(DataHeader* header) { if (isRun() && header) { for (int i = g_clients.size() - 1; i >= 0; i--) { SendData(g_clients[i], header); } } } private: SOCKET _sock; std::vector<SOCKET> g_clients; }; #endif // _EasyTcpServer_hpp_ --------------------------------------------------------------- #include "EasyTcpServer.hpp" int main() { EasyTcpServer server; server.InitSocket(); server.Bind(nullptr, 4567); server.Listen(5); while (server.isRun()) { server.OnRun(); //printf("空闲时间...\n"); } server.Close(); printf("任务完成\n"); getchar(); return 0; }
评论 (
0
)
登录
后才可以发表评论
状态
待办的
待办的
进行中
已完成
已关闭
负责人
未设置
标签
未设置
标签管理
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
参与者(1)
1
https://gitee.com/linux_jianfeng123/server-programming.git
git@gitee.com:linux_jianfeng123/server-programming.git
linux_jianfeng123
server-programming
服务器编程
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册