server.h:
#pragma once
#include
#include
#include
#pragma comment (lib ,“ws2_32.lib”)
class server
{
public:
server();
~server();
void severStart(int port);
void serverStop();
void serverAccpt();
void serverRecv();
void selectHeart();
private:
SOCKET serverSide;
SOCKET clientSide;
std::map
};
server.cpp
#include “stdafx.h”
#include “server.h”
#include
#include
using namespace std;
server::server()
{
}
server::~server()
{
}
//启动服务器
void server::severStart(int port)
{
WSADATA wsa = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsa); //使用函数WSAStartup()绑定相应版本的套接字库:成功返回0;
//创建套接字(参数为:)地址系列 套接字类型 协议号
//socket(domain = AF_INET, type = SOCK_STREAM, proto = IPPROTO_TCP)
serverSide = socket(AF_INET, SOCK_STREAM, 0);
//1.domain : Domain参数指定了通信的”域” AF_UNIX:AF_LOCAL本地通信 AF_INET : IPv4网络通信 AF_INET6 : IPv6网络通信 AF_PACKET : 链路层通信
//2. Type: Type就是socket的类型,对于AF_INET协议族而言有流套接字(SOCK_STREAM)、数据包套接字(SOCK_DGRAM)、原始套接字(SOCK_RAW)
SOCKADDR_IN addr; //地址集
addr.sin_family = AF_INET; // 设置地址族为IPV4
addr.sin_port = htons(port); // 设置地址的端口号信息
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置IP地址;
// bind() 服务端套接字绑定自己的IP地址与端口号,客户端那边可以不写,内核会给它分配一个临时的端口。
//参数:
// 1)、sockfd: 服务器或者客户端自己创建的socket
// 2)、sockaddr : 服务器或者客户端自己的地址信息(协议族、IP、端口号)
::bind(serverSide, (SOCKADDR *)&addr, sizeof(addr));
// listen(int sockfd, int backlog);
//用法:函数应该在调用socket和bind这两个函数之后,accept函数之前调用。
// 作用:让服务器套接字sockfd进入监听状态。
// 参数:sockfd:套接字,成功返回后进入监听模式,当有新连接并accept后会再建立一个套接字保存新的连接;
// backlog:并发连接数 backlog的取值范围 ,一般为0-5。
listen(serverSide, 5);
thread t1(&server::serverAccpt, this);
t1.detach();
}
void server::serverStop()
{
closesocket(serverSide);
cout << “服务端关闭” << endl;
WSACleanup();
}
//连接客户端
void server::serverAccpt()
{
//检测心跳
thread tt(&server::selectHeart, this);
tt.detach();
while (true)
{
SOCKADDR_IN clientAddr;
int len = sizeof(clientAddr);
//accept函数返回值是一个客户端和服务器连接的SOCKET类型的描述符,在服务器端标识着这个客户端。
clientSide = accept(serverSide, (SOCKADDR*)&clientAddr, &len);
if (clientSide == INVALID_ATOM)
{
cout << “无效连接” << endl;
}
char ip[32];
inet_ntop(AF_INET, &clientAddr.sin_addr, ip, sizeof(ip));
cout << “连接” << endl;
clientHeart[clientSide] = 5;
//收包
thread t1(&server::serverRecv,this);
t1.detach();
}
}
// 收报;
void server::serverRecv()
{
while (true)
{
char buff[1024] = {0};
// recv(socket s, char * buf, int len,intflag)
// 参数socket:创建的可以传输消息过来的套接字
// 参数buf:接受消息的字符串缓存
// 参数len:允许接收字符串的缓存的最大长度
// 第四个参数:会对函数行为产生影响,一般设置为0
// send和recv实际上分别是write和read函数的基础上扩展了第四个参数:
int res=recv(clientSide, buff, sizeof(buff), 0);
if (res<=0)
{
cout << “连接断开***” << endl;
break;
}
cout << res << endl;
clientHeart[clientSide] = 5;
}
}
// 心跳机制
void server::selectHeart()
{
cout << “进入心跳” << endl;
while (true)
{
for (auto iter=clientHeart.begin();iter!=clientHeart.end()?//条件判断放到else中判断;
{
iter->second = iter->second -1;
clientHeart[iter->first] = iter->second;
cout << iter->second << endl;
if (iter->second == 0)
{
cout << “客户端断开” << endl;
closesocket(clientSide);
clientHeart.erase(iter++);// iter++;删除到末尾时for循环中的iter++会指向后面不存在地址;程序崩;
if (iter == clientHeart.end())
{
cout << “到末尾” << endl;
break;
}
}
else
{
// 使用指针时,删除的话会跳一个,
iter++;
}
}
Sleep(3000); // 设置3秒发射一个信号判断;总计时为15秒;
}
}
mian.cpp
#include “stdafx.h”
#include “server.h”
#include
#include
using namespace std;
int main()
{
server s;
s.severStart(7777);
string cmd;
cin >> cmd;
if (cmd ==“exit”)
{
s.serverStop();
}
return 0;
}