封装Server类和Client类
服务器端:
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类的更多相关文章
- 基于MongoDb官方C#驱动封装MongoDbCsharpHelper类(CRUD类)
近期工作中有使用到 MongoDb作为日志持久化对象,需要实现对MongoDb的增.删.改.查,但由于MongoDb的版本比较新,是2.4以上版本的,网上已有的一些MongoDb Helper类都是基 ...
- 适用于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通用)>,现在重新整理一 ...
- Redis进阶实践之九 独立封装的RedisClient客户端工具类(转载9)
Redis进阶实践之九 独立封装的RedisClient客户端工具类 一.引言 今天开始有关Redis学习的第九篇文章了,以后肯定会大量系统使用Redis作为缓存介质,为了更好的更好的Redis,自己 ...
- iOS开发拓展篇—封装音频文件播放工具类
iOS开发拓展篇—封装音频文件播放工具类 一.简单说明 1.关于音乐播放的简单说明 (1)音乐播放用到一个叫做AVAudioPlayer的类 (2)AVAudioPlayer常用方法 加载音乐文件 - ...
- 在client类中设置访问属性 address,business和individua
php 5.4中的traits,是新引入的特性,中文还真不知道如何准确翻译好.其实际的目的, 是为了有的场合想用多继承,但PHP又没多继承 ,于是就发明了这样的一个东西. Traits可以理解为一组能 ...
- JBPM4入门——4.封装流程管理的工具类(JbpmUtil)
本博文只是简要对JBPM4进行介绍,如需更详细内容请自行google 链接: JBPM入门系列文章: JBPM4入门——1.jbpm简要介绍 JBPM4入门——2.在eclipse中安装绘制jbpm流 ...
- c# 封装的文件夹操作类之复制文件夹
c# 封装的文件夹操作类之复制文件夹 一.复制文件夹原理: 1.递归遍历文件夹 2.复制文件 二.FolderHelper.cs /// <summary> /// 文件夹操作类 /// ...
- Sql Server中的数据类型和Mysql中的数据类型的对应关系(转)
Sql Server中的数据类型和Mysql中的数据类型的对应关系(转):https://blog.csdn.net/lilong329329/article/details/78899477 一.S ...
- python mysql redis mongodb selneium requests二次封装为什么大都是使用类的原因,一点见解
1.python mysql redis mongodb selneium requests举得这5个库里面的主要被用户使用的东西全都是面向对象的,包括requests.get函数是里面每次都是实例 ...
随机推荐
- python进阶--多线程多进程
一.线程和进程 进程是拥有独立内存,能够独立运行的最小单位,也是程序执行的最小单位,线程是程序运行过程中,一个单一的顺序控制流程,是程序执行流的最小单位,一个进程至少包含一个线程,多线程共享进程的内存 ...
- 知识点整理-mysql怎么查看优化器优化后的sql
背景 1.新建两张表 CREATE TABLE t1 (m1 )); CREATE TABLE t2 (m2 )); 2.插入些数据 INSERT INTO t1 VALUES(, , , 'c'); ...
- MVC框架实例教程 【转载】
1 什么是MVC MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller ...
- 大白带你侃JAVA——封装的概述及好处
封装概述: 封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式 这是什么意思呢? 简单的来说就是我将不想给别人看的数据,以及别人无需知道的内部细节, "锁起来" ,我们只留下 ...
- axios设置请求头内容
axios设置请求头中的Authorization 和 cookie 信息: GET请求 axios.get(urlString, { headers: { 'Authorization': 'Bea ...
- loback的介绍与配置-(通俗易通)
一.logback的配置介绍 Logback的配置分为三个内容:Logger.appender及layout Logger:作为日志的记录器,主要用于存放日志对象,也可以定义日志类型.级别. appe ...
- HDU1401(双向BFS)
题意:http://acm.hdu.edu.cn/showproblem.php?pid=1401 给你8*8的棋盘和4个棋子初始位置.最终位置,问你能否在8次操作后达到该状态. 思路: 双向BFS, ...
- redis快速开始
1 下载地址:http://redis.io/download 2 安装步骤: 3 # 安装gcc 4 yum install gcc 5 6 # 把下载好的redis‐5.0.3.tar.gz放在/ ...
- python爬取网页数据并存储到mysql数据库
#python 3.5 from urllib.request import urlopen from urllib.request import urlretrieve from bs4 impor ...
- python — 表的操作(一)
1. 创建表 创建表: create table t1 (id int,name char(4)); create table t2 (id int,name char(4)) engine=myis ...