服务器端:

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 ;
}

最新文章

  1. 目录的文件权限-X
  2. android—-线性布局
  3. js正则表达式
  4. iOS之 PJSIP蓝牙外设音频支持
  5. 洛谷 U4792 Acheing 单调队列
  6. 第五周PSP
  7. jquery 根据年 月设置报表表头
  8. Java基础の第二弹 基础语法
  9. Lintcode: Interval Minimum Number
  10. MVC开发 好的扩展套件(Visual Studio 插件)
  11. 【转】Eclipse提示No java virtual machine(转载)
  12. MS SQL Sever数据库还原
  13. C++_内部类
  14. [转载] 十五分钟介绍 Redis数据结构
  15. Java基础知识总结(超级经典)
  16. win7旗舰版64位GHOST版的,安装telnet客户端时,提示:出现错误。并非所有的功能被成功更改。
  17. html面页与JAVA通过webSocket 通讯
  18. TestNg-数据驱动-dataProvider
  19. 18-10-09 Linux常用命令大全(非常全!!!)
  20. Servlet案例2:文件下载

热门文章

  1. JavaScript:undefined!=false之解 及==比较的规则
  2. python进阶--多线程多进程
  3. ORA-00054:Orcacle表锁定
  4. 【Qt开发】QT4 升级到 QT5 改动
  5. strtotime 的 BUG
  6. codevs 2853:方格游戏
  7. SpringEl表达式解析
  8. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记
  9. Httpwatch教程
  10. [翻译] 深入浅出Go语言调度器:第一部分 - 系统调度器