服务器端:

EasyTcpServer.hpp

#ifndef _EasyTcpServer_hpp_
#define _EasyTcpServer_hpp_ #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<WinSock2.h>
#include<Windows.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 "MessageHeader.hpp" class EasyTcpServer
{
private:
SOCKET _sock;
std::vector<SOCKET> g_client; //容器里装的是所有的客户端 public :
EasyTcpServer()
{
_sock = INVALID_SOCKET;//将Socket设置为一个无效的套接字
}
virtual ~EasyTcpServer()
{
Close();
}
//初始化Socket
SOCKET InitSock()
{
//启动Win Socket2.x环境
#ifdef _WIN32
WORD ver = MAKEWORD(, );
WSADATA dat;
WSAStartup(ver, &dat);
#endif
//1.建立一个socket
if (INVALID_SOCKET != _sock) // 如果当前的socket并不是无效的,则将其关闭
{
printf("<socket=%d>关闭旧连接......\n", _sock);
Close();
}
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 新建一个socket,此时的Socket为有效
if (INVALID_SOCKET == _sock)
{
printf("ERROR:建立失败!\n");
}
else{
printf("客户端绑定成功......\n");
}
return _sock;
}
//绑定端口号
int Bind(const char* ip,unsigned short port)
{
/*if (_sock == INVALID_SOCKET)
{
InitSock();
}*/
sockaddr_in _sin = {}; //创建网络地址
_sin.sin_family = AF_INET; //IPV4
_sin.sin_port = htons(port); //Host to Network Short
#ifdef _WIN32
if (ip) //验证IP是否存在
{
_sin.sin_addr.S_un.S_addr = inet_addr(ip); // IP地址
}
else{
_sin.sin_addr.S_un.S_addr = INADDR_ANY;
} #else
_sin.sin_addr.s_addr=INADDR_ANY;
#endif
int ret = bind(_sock, (sockaddr *)&_sin, sizeof(_sin));
if (SOCKET_ERROR == ret)
{
printf("错误:绑定网络端口<%d>失败......\n",port);
}
else
{
printf("绑定网络窗口<%d>成功......\n", port);
}
return ret;
}
//监听端口号
int Listen(int n)
{
//3.监听网络端口
int ret = listen(_sock, n);//第二个参数为最大等待多少人可以同时连接
if (SOCKET_ERROR == ret)
{
printf("<Socket=%d>错误:监听网络端口失败......\n",_sock);
}
else
{
printf("<Socket=%d>服务器端监听成功......\n",_sock);
}
return ret;
}
//接受客户端连接
SOCKET Accept()
{
//4.等待接收客户端连接
sockaddr_in clientAddr = {}; //创建客户端网络地址
int nAddrLen = sizeof(sockaddr_in);
SOCKET _cSOCK = INVALID_SOCKET; //将SOCKET的对象_cSOCK初始化无效的Socket
#ifdef _WIN32
_cSOCK = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);//接收来自客户端传来的SOCKET
#else
_cSOCK = accept(_sock, (sockaddr *)&clientAddr, (socklen_t *)&nAddrLen);
#endif
if (_cSOCK == INVALID_SOCKET)
{
printf("<Socket=%d>错误,接收到无效客户端SOCKET!\n",_sock);
}
else
{
//向客户端发送新来的用户
NewUserJoin userJoin;
SendDataToAll(&userJoin); //将服务端收到的socket存入容器中
g_client.push_back(_cSOCK);
printf("<Socket=%d>新客户端加入:Socket=%d,IP = %s\n", _sock,(int)_cSOCK, inet_ntoa(clientAddr.sin_addr));//inet_ntoa(clientAddr.sin_addr)将接收到的IP地址转化为字符串
}
return _cSOCK;
}
//关闭socket void Close()
{
if (_sock != INVALID_SOCKET)
{
#ifdef _WIN32
for (int n = (int)g_client.size(); n >= ; n--)
{
//8.关闭自身的socket
closesocket(g_client[n]);
} //8.关闭自身的socket
closesocket(_sock);
//清除Windows socket环境
WSACleanup();
#else
for (int n = (int)g_client.size(); n >= ; n--)
{
//8.关闭自身的socket
closesocket(g_client[n]);
}
close(_sock);
#endif
} }
//处理网络消息
bool onRun()
{
if (isRun())
{
fd_set fd_Read;
fd_set fd_Write;
fd_set fd_Exp; FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
FD_ZERO(&fd_Write);
FD_ZERO(&fd_Exp); FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
FD_SET(_sock, &fd_Write);
FD_SET(_sock, &fd_Exp);
SOCKET maxSock = _sock;
for (int n = g_client.size() - ; n >= ; n--)
{
FD_SET(g_client[n], &fd_Read);
if (maxSock < g_client[n])
{
maxSock = g_client[n];
}
} /*
select(
_In_ int nfds,
_Inout_opt_ fd_set FAR * readfds,
_Inout_opt_ fd_set FAR * writefds,
_Inout_opt_ fd_set FAR * exceptfds,
_In_opt_ const struct timeval FAR * timeout
);
*/ //nfds是一个整数值,是指fd_set集合所有的描述符(select里的第一个参数)的范围(而不是数量)
//既是所有文件描述符最大值+1
timeval t = { , }; int ret = select(_sock + , &fd_Read, &fd_Write, &fd_Exp, &t); //系统提供select函数来实现多路复用输入/输出模型
if (ret < )
{
printf("select任务结束!\n");
Close();
return false;
}
if (FD_ISSET(_sock, &fd_Read))
{
FD_CLR(_sock, &fd_Read);
Accept();
}
for (int n = (int)g_client.size() - ; n >= ; n--)
{
if (FD_ISSET(g_client[n], &fd_Read))
{
if (RecvData(g_client[n]) == -)
{
auto iter = g_client.begin() + n;
if (iter != g_client.end())
{
g_client.erase(iter);
}
}
} }
return true;
}
return false;
}
//是否工作中
bool isRun()
{
return _sock != INVALID_SOCKET;
}
//接收数据 处理粘包和拆包
int RecvData(SOCKET _cSOCK)
{
//增加一个缓冲区
char szRecv[] = {};
//5.接收客户端新数据
int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), );
DataHeader *header = (DataHeader*)szRecv; if (nLen <= )
{
printf("客户端已退出!任务结束!");
return -;
}
recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), );
OnNetMsg(_cSOCK, header);
return ;
}
//响应网络消息
virtual void OnNetMsg(SOCKET _cSOCK, DataHeader* header)
{
switch (header->cmd){
case CMD_Login:
{
Login *login = (Login*)header;
printf("收到客户端<Socket=%d>请求:CMD_Login,数据长度:%d\nUserName:%s\nPassWord:%s\n", _cSOCK, login->dataLength, login->username, login->password);
//忽略判断用户密码是否正确的过程
LoginResult ret;
send(_cSOCK, (char *)&ret, sizeof(LoginResult), ); //再发消息体 }
case CMD_Logout:
{
Logout* logout = (Logout*)header;
printf("收到命令:CMD_Logout,数据长度:%d\nUserName:%s\n", logout->dataLength, logout->username); //忽略判断用户密码是否正确的过程
LogoutResult let;
send(_cSOCK, (char *)&let, sizeof(let), ); //再发消息体
}
break;
case CMD_New_User_Join:
{
NewUserJoin* UserJoin = (NewUserJoin*)header;
printf("收到命令:CMD_Logout,数据长度:%d\nUserName:%s\n", UserJoin->dataLength); //忽略判断用户密码是否正确的过程
NewUserJoin let;
send(_cSOCK, (char *)&let, sizeof(let), ); //再发消息体
}
break;
default:
{
DataHeader header = { };
send(_cSOCK, (char *)&header.cmd, sizeof(header), );
} break;
} }
//单次发送指定socket数据
int SendData(SOCKET _cSOCK, DataHeader* header)
{
if (isRun() && header)
{
return send(_cSOCK, (const char*)header, header->dataLength, );
}
return SOCKET_ERROR;
}
//群发指定socket数据
void SendDataToAll(DataHeader* header)
{
if (isRun() && header)
{
for (int n = (int)g_client.size() - ; n >= ; n--)
{
SendData(g_client[n], header);
}
}
}
}; #endif

MessageHeader.hpp

enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_New_User_Join, CMD_ERROR };

//包头
struct DataHeader
{
short dataLength;
short cmd;
};
//包体
struct Login :public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_Login;
}
char username[];
char password[];
}; struct LoginResult :public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_Login_Result;
result = ;
}
int result;
}; struct Logout :public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_Logout;
}
char username[];
}; struct LogoutResult :public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_Logout_Result;
result = ;
}
int result;
}; struct NewUserJoin :public DataHeader
{
NewUserJoin()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_New_User_Join;
sock = ;
}
int sock;
};

Server.cpp

#include "EasyTcpServer.hpp"

int main()
{
EasyTcpServer server;
server.InitSock();
server.Bind(nullptr,);
server.Listen(); while (server.isRun())
{
server.onRun();
//printf("空闲时间处理其他业务.......\n");
}
server.Close();
system("pause");
return ;
}

客户端:

EasyTcpClient.hpp

#ifndef _EasyTcpClient_hpp_
#define _EasyTcpClient_hpp_
#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN
#include<WinSock2.h>
#include<Windows.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 "MessageHeader.hpp" class EasyTcpClient
{
SOCKET _sock;
public:
EasyTcpClient()
{
_sock = INVALID_SOCKET;//将Socket设置为一个无效的套接字
} //虚构函数
virtual ~EasyTcpClient()
{
Close();
} //初始化socket
void initSocket()
{
//启动Win Socket2.x环境
#ifdef _WIN32
WORD ver = MAKEWORD(, );
WSADATA dat;
WSAStartup(ver, &dat);
#endif
//1.建立一个socket
if (INVALID_SOCKET != _sock) // 如果当前的socket并不是无效的,则将其关闭
{
printf("<socket=%d>关闭旧连接......\n",_sock);
Close();
}
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 新建一个socket
if (INVALID_SOCKET == _sock)
{
printf("ERROR:建立失败!\n");
}
else{
printf("客户端绑定成功......\n");
}
} //连接服务器
int Connet(char* ip, unsigned short port)
{
if (INVALID_SOCKET == _sock)
{
initSocket();
}
sockaddr_in _sin = {}; //创建网络地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(port); //Host to Network Short
#ifdef _WIN32
_sin.sin_addr.S_un.S_addr = inet_addr(ip);//inet_addr("127.0.0.1"); // IP地址
#else
_sin.sin_addr.s_addr = inet_addr(ip);
#endif
int ret = connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in));
if (SOCKET_ERROR == ret)
{
printf("ERROR:连接失败!\n");
}
else
{
printf("客户端连接成功......\n");
}
return ret;
} //关闭服务器
void Close()
{
if (_sock != INVALID_SOCKET)
{
//关闭Win Socket2.x环境
#ifdef _WIN32
closesocket(_sock);
//WinSocket关闭
WSACleanup();
#else
close(_sock);
#endif
_sock = INVALID_SOCKET;
} } //查询网络消息
bool onRun()
{
if (isRun())
{
//伯克利 socket
fd_set fd_Read;
FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
timeval t = { , };
int ret = select(_sock, &fd_Read, , , &t);
if (ret < )
{
printf("<socket=%d>select任务结束1!", _sock);
return false;
}
if (FD_ISSET(_sock, &fd_Read))
{
FD_CLR(_sock, &fd_Read); if (RecvData(_sock) == -)
{
printf("<socket = %d>select任务结束2!", _sock);
return false;
}
}
return true;
}
return false;
} //是否工作中
bool isRun()
{
return _sock != INVALID_SOCKET;
} //接收数据 处理粘包和拆包
int RecvData(SOCKET _cSOCK)
{
//增加一个缓冲区
char szRecv[] = {};
//5.接收客户端新数据
int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), );
DataHeader *header = (DataHeader*)szRecv; if (nLen <= )
{
printf("与服务器断开连接!任务结束!");
return -;
}
recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), );
OnNetMsg(header);
return ;
} //响应网络消息
void OnNetMsg(DataHeader* header)
{
switch (header->cmd){
case CMD_Login_Result:
{ LoginResult *loginresult = (LoginResult*)header;
printf("收到服务端消息请求:CMD_Login_Result,数据长度:%d\n", loginresult->dataLength);
}
break;
case CMD_Logout_Result:
{
LogoutResult* logoutresult = (LogoutResult*)header;
printf("收到服务端消息请求:CMD_Logout_Result,数据长度:%d\nUserName:%s\n", logoutresult->dataLength);
}
break;
case CMD_New_User_Join:
{
NewUserJoin* newuserjoin = (NewUserJoin*)header;
printf("收到服务端消息请求:CMD_New_User_Join,数据长度:%d\nUserName:%s\n", newuserjoin->dataLength);
}
break;
}
} //发送数据
int SendData(DataHeader* header)
{
if (isRun() && header)
{
return send(_sock, (const char*)header, header->dataLength, );
}
return SOCKET_ERROR;
} private: }; #endif

MessageHeader.hpp

enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_New_User_Join, CMD_ERROR };

//包头
struct DataHeader
{
short dataLength;
short cmd;
};
//包体
struct Login :public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_Login;
}
char username[];
char password[];
}; struct LoginResult :public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_Login_Result;
result = ;
}
int result;
}; struct Logout :public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_Logout;
}
char username[];
}; struct LogoutResult :public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_Logout_Result;
result = ;
}
int result;
}; struct NewUserJoin :public DataHeader
{
NewUserJoin()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_New_User_Join;
sock = ;
}
int sock;
};

client.cpp

#include "EasyTcpClient.hpp"
//线程库头文件
#include<thread> void cmdThread(EasyTcpClient* client)
{
while (true)
{
char cmdBuf[] = {};
scanf("%s", cmdBuf);
if ( == strcmp(cmdBuf, "exit"))
{
client->Close();
printf("退出!\n");
break;
}
else if ( == strcmp(cmdBuf, "login"))
{
Login login;
strcpy(login.username, "sutaoyu");
strcpy(login.password, "sutaoyu01");
client->SendData(&login);
}
else if ( == strcmp(cmdBuf, "logout"))
{
Logout logout;
strcpy(logout.username, "sutaoyu");
client->SendData(&logout);
}
else{
printf("不支持的命令!");
}
} } int main()
{
EasyTcpClient client;
client.initSocket();
client.Connet("127.0.0.1", ); //启动线程
std::thread t1(cmdThread, &client);
t1.detach(); while (client.isRun())
{
client.onRun(); }
client.Close(); printf("已退出!\n");
getchar();
return ;
}

封装Server类和Client类的更多相关文章

  1. 基于MongoDb官方C#驱动封装MongoDbCsharpHelper类(CRUD类)

    近期工作中有使用到 MongoDb作为日志持久化对象,需要实现对MongoDb的增.删.改.查,但由于MongoDb的版本比较新,是2.4以上版本的,网上已有的一些MongoDb Helper类都是基 ...

  2. 适用于app.config与web.config的ConfigUtil读写工具类 基于MongoDb官方C#驱动封装MongoDbCsharpHelper类(CRUD类) 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD) C# 实现AOP 的几种常见方式

    适用于app.config与web.config的ConfigUtil读写工具类   之前文章:<两种读写配置文件的方案(app.config与web.config通用)>,现在重新整理一 ...

  3. Redis进阶实践之九 独立封装的RedisClient客户端工具类(转载9)

    Redis进阶实践之九 独立封装的RedisClient客户端工具类 一.引言 今天开始有关Redis学习的第九篇文章了,以后肯定会大量系统使用Redis作为缓存介质,为了更好的更好的Redis,自己 ...

  4. iOS开发拓展篇—封装音频文件播放工具类

    iOS开发拓展篇—封装音频文件播放工具类 一.简单说明 1.关于音乐播放的简单说明 (1)音乐播放用到一个叫做AVAudioPlayer的类 (2)AVAudioPlayer常用方法 加载音乐文件 - ...

  5. 在client类中设置访问属性 address,business和individua

    php 5.4中的traits,是新引入的特性,中文还真不知道如何准确翻译好.其实际的目的, 是为了有的场合想用多继承,但PHP又没多继承 ,于是就发明了这样的一个东西. Traits可以理解为一组能 ...

  6. JBPM4入门——4.封装流程管理的工具类(JbpmUtil)

    本博文只是简要对JBPM4进行介绍,如需更详细内容请自行google 链接: JBPM入门系列文章: JBPM4入门——1.jbpm简要介绍 JBPM4入门——2.在eclipse中安装绘制jbpm流 ...

  7. c# 封装的文件夹操作类之复制文件夹

    c#  封装的文件夹操作类之复制文件夹 一.复制文件夹原理: 1.递归遍历文件夹 2.复制文件 二.FolderHelper.cs /// <summary> /// 文件夹操作类 /// ...

  8. Sql Server中的数据类型和Mysql中的数据类型的对应关系(转)

    Sql Server中的数据类型和Mysql中的数据类型的对应关系(转):https://blog.csdn.net/lilong329329/article/details/78899477 一.S ...

  9. python mysql redis mongodb selneium requests二次封装为什么大都是使用类的原因,一点见解

    1.python mysql  redis mongodb selneium requests举得这5个库里面的主要被用户使用的东西全都是面向对象的,包括requests.get函数是里面每次都是实例 ...

随机推荐

  1. Centos7 系统更改apache默认网站目录(解决You don't have permission to access / on this server问题)

    当我们在Centos7中配置好Apache时,发现apache默认解析目录是在 /var/www/html,也就是说当访问服务器 IP 或者本地 localhost 时, 默认定位到这个目录里的 in ...

  2. iOS-SVPullToRefresh​下拉刷新,上拉加载(转)

    https://github.com/Sephiroth87/ODRefreshControl 类似刷新控件,类似qq动画的那种刷新. 一.下载第三方库 https://github.com/samv ...

  3. DELL服务器管理工具和RACADM介绍

    DELL服务器管理工具和RACADM介绍 一.Dell服务器管理工具介绍 Dell对服务器(DELL PowerEdge)的管理主要提供了三种管理工具,分别是Dell Remote Access Co ...

  4. golang web框架设计1:框架规划

    GO WEB 编程13节,如何设计一个web框架 学习谢大的web框架设计 总体介绍 实现一个简易的web框架,我们采用mvc模式来进行开发. model:模型,代表数据结构.通常来说,模型类时包含查 ...

  5. Flutter 底部的renderflex溢出

    一开始直接使用Scaffold布局,body:new Column  然后模拟器会提示捕获异常: 然后百度了一下Flutter的溢出问题,发现解决办法是使用SingleChildScrollView包 ...

  6. vulstudy

    vulstudy是专门收集当下流行的漏洞学习平台,并将其制作成docker镜像,方便大家快速搭建环境,节省搭建时间,专注于的漏洞学习上.目前vulstudy包含以下漏洞学习平台: 序号 漏洞平台 包含 ...

  7. Python Elasticsearch

    以下所用版本为Elasticsearch 7.2.0 1.安装 pip3 install elasticsearch -i https://pypi.tuna.tsinghua.edu.cn/simp ...

  8. 【DSP开发】德州仪器达芬奇五年之路七宗罪,嵌入式处理器架构之争决战2012

    芯片是产业链上游重要的一个环节,一颗小小的芯片具有极高的技术含量和价值,半导体行业每年都会有一个各大厂商营业额的排名,除去2009年,常年盘踞在前三名位置的分别是英特尔,三星半导体和德州仪器,英特尔凭 ...

  9. 最新 前程无忧java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.前程无忧等10家互联网公司的校招Offer,因为某些自身原因最终选择了前程无忧.6.7月主要是做系统复习.项目复盘.Leet ...

  10. VMware Windows Server 2008 R2 X64 虚拟机安装教程

    首先进入VMware Workstation中,点击创建新的虚拟机 然后按如下步骤操作 然后进去选择拷贝的Windows 2008的映像文件 下面这个网址里面提供了各种映像文件的下载http://is ...