直接上代码,让代码说话!
//Server.h
#pragma once
#include <stdio.h>
#include <iostream>
#include <winsock2.h>
#include <map>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
#define SERVER_HOST "XX.X.XX.XX"
enum Type { HEART, CTRL_INFO, OTHER};
//packet
struct PACKET
{
//报文头
Type type;
//报文信息
char msg[1024];
};
class Server
{
public:
Server();
//construction
Server(string ip, int port);
//deconstruction
~Server();
//
void Bind();
//
void Accept();
//
void Listen(int queue_len);
//
void Recv();
//
void Run();
//心跳线程入口函数
static UINT HeartProc(LPVOID lparam);
private:
sockaddr_in m_serverAddr;
//监听fd
int m_listen_fd;
//最大监听fd
int m_max_fd;
//超时信息
timeval m_timeout;
//所有fd集合,包括监听fd和客户端fd
fd_set m_master_set;
//工作集合
fd_set m_working_set;
//Server Thread
CWinThread* m_serverThread;
public:
//记录链接的客户端信息,链接、IP、未发送心跳次数
map<int, pair<string, int> > m_clientInfo;
};
//Server.cpp
#include "stdafx.h"
#include "Server.h"
#include <Ws2tcpip.h>
Server::Server(string ip, int port)
{
m_serverThread = NULL;
//
WSADATA wsaData;
// Initialize Windows socket library
WSAStartup(0x0202, &wsaData);
//先将保存地址的server置为全0
memset(&m_serverAddr, 0, sizeof(SOCKADDR_IN));
//
m_serverAddr.sin_family = AF_INET;
m_serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
m_serverAddr.sin_port = htons(port);
// create socket to listen
m_listen_fd = socket(PF_INET, SOCK_STREAM, 0);
if (m_listen_fd < 0)
{
//AfxMessageBox为MFC中的提示对话框接口
AfxMessageBox(_T("Create Socket Failed!"), MB_ICONINFORMATION);
//exit(1);
}
else{}
int opt = 1;
//closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket
setsockopt(m_listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
}
Server::Server()
{
}
Server::~Server()
{
for (int fd = 0; fd <= m_max_fd; ++fd)
{
if (FD_ISSET(fd, &m_max_fd))
{
closesocket(fd);
}
else{}
}
}
void Server::Bind()
{
if (-1 == (bind(m_listen_fd, (struct sockaddr*)&m_serverAddr, sizeof(m_serverAddr))))
{
AfxMessageBox(_T("Server Bind Failed!"), MB_ICONINFORMATION);
//exit(1);
}
else{}
}
void Server::Accept()
{
sockaddr_in client_addr;
//
socklen_t client_addr_len = sizeof(client_addr);
//
int new_fd = accept(m_listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);
if (new_fd < 0)
{
AfxMessageBox(_T("Server Accept Failed!"), MB_ICONINFORMATION);
//exit(1);
}
else{}
// 获取客户端IP
string ip(inet_ntoa(client_addr.sin_addr));
//将客户端连接信息放入容器
m_clientInfo.insert(make_pair(new_fd, make_pair(ip, 0)));
// 将新建立的连接的fd加入master_set
FD_SET(new_fd, &m_master_set);
//
if (new_fd > m_max_fd)
{
m_max_fd = new_fd;
}
else{}
}
void Server::Listen(int queue_len)
{
if (-1 == listen(m_listen_fd, queue_len))
{
AfxMessageBox(_T("Server Listen Failed!"), MB_ICONINFORMATION);
//exit(1);
}
else{}
}
void Server::Recv()
{
for (int fd = 0; fd <= m_max_fd; ++fd)
{
if (FD_ISSET(fd, &m_working_set))
{
// 标记当前连接是否断开了
bool close_conn = false;
//
char recv_buffer[1028];
//初始化
memset(recv_buffer, 0, sizeof(recv_buffer));
//recv
int _recv = recv(fd, recv_buffer, sizeof(recv_buffer), 0);
//
PACKET recv_head;
//
memset(&recv_head, 0, sizeof(recv_head));
//将recv_buffer转为结构体PACKET
memcpy(&recv_head, recv_buffer, sizeof(recv_buffer));
//判断报文头类型是否为心跳
if (recv_head.type == HEART)
{
PACKET send_head;
send_head.type = HEART;
memset(send_head.msg, 0, sizeof(send_head.msg));
//
char send_buffer[1028];
//
memset(send_buffer, 0, sizeof(send_buffer));
//
memcpy(send_buffer, &send_head, sizeof(send_head));
//收到client的心跳包后回复心跳包
int _send = send(fd, send_buffer, sizeof(send_buffer), 0);
//每次收到心跳包,count置0
m_clientInfo[fd].second = 0;
cout << "Received heart-beat from client.\n";
}
else
{
close_conn = true;
}
Sleep(3000);
// 当前这个连接有问题,关闭它
if (close_conn)
{
closesocket(fd);
FD_CLR(fd, &m_master_set);
// 需要更新max_fd;
if (fd == m_max_fd)
{
while (FD_ISSET(m_max_fd, &m_master_set) == false)
--m_max_fd;
}
else{}
}
}
}
}
void Server::Run()
{
//创建心跳监测线程
m_serverThread = AfxBeginThread(HeartProc, (void*)this, 0, 0, 0);
//
if (m_serverThread == 0)
{
cout << "Can not create heart-beat checking thread.\n";
}
else{}
//初始化max_fd
m_max_fd = m_listen_fd;
//
FD_ZERO(&m_master_set);
//添加监听fd
FD_SET(m_listen_fd, &m_master_set);
//
while (1)
{
FD_ZERO(&m_working_set);
memcpy(&m_working_set, &m_master_set, sizeof(m_master_set));
//
m_timeout.tv_sec = 15;
m_timeout.tv_usec = 0;
//select函数来实现多路复用输入/输出模型
int nums = select(m_max_fd + 1, &m_working_set, NULL, NULL, &m_timeout);
if (nums < 0)
{
cout << "select() error!";
//exit(1);
}
else if (nums == 0)
{
continue;
}
else{}
if (FD_ISSET(m_listen_fd, &m_working_set))
{
//有新的客户端请求
Accept();
}
else
{
//接收客户端的消息
Recv();
}
}
}
UINT Server::HeartProc(LPVOID lparam)
{
cout << "The heartbeat checking thread started.\n";
//
Server* s = (Server*)lparam;
while (1)
{
map<int, pair<string, int> >::iterator it = s->m_clientInfo.begin();
for (; it != s->m_clientInfo.end();)
{
// 3s*5没有收到心跳包,判定客户端掉线
if (it->second.second == 5)
{
string _str1 = it->second.first;
//
CString _str2;
//
_str2 = _str1.c_str();
//
CString _msg = _T("The client ") + _str2 + _T(" has been offline");
//
AfxMessageBox(_msg, MB_ICONINFORMATION);
//
int fd = it->first;
//关闭该连接
closesocket(fd);
//
FD_CLR(fd, &s->m_master_set);
//需要更新max_fd;
if (fd == s->m_max_fd)
{
while (FD_ISSET(s->m_max_fd, &s->m_master_set) == false)
s->m_max_fd--;
}
// 从map中移除该记录
s->m_clientInfo.erase(it++);
}
else if (it->second.second < 5 && it->second.second >= 0)
{
it->second.second += 1;
++it;
}
else
{
++it;
}
}
Sleep(3000); // 定时三秒
}
}
//Client.h
#pragma once
#include <stdio.h>
#include <iostream>
#include <map>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
//
#define WM_UPDATE_CONTROLLER_STATUS (WM_USER + 501)
//
enum Type { HEART, CTRL_INFO, OTHER };
//packet
struct PACKET
{
Type type;
char msg[1024];
};
class Client
{
public:
//
Client();
//
Client(string ip, int port);
//
~Client();
//
void CreateSocket();
//
void Connect();
//
void RunSendHeart();
//
void RunRecvHeart();
//客户端发送心跳线程入口函数
static UINT SendHeartProc(LPVOID lparam);
//客户端接收心跳线程入口函数
static UINT RecvHeartProc(LPVOID lparam);
private:
//
sockaddr_in m_serverAddr;
//
int m_fd;
//发送心跳线程
CWinThread* m_sendHeartThread;
//接收心跳线程
CWinThread* m_recvHeartThread;
};
//Client.cpp
#include "stdafx.h"
#include "Client.h"
#include <Ws2tcpip.h>
//
Client::Client()
{
}
Client::Client(string ip, int port)
{
m_sendHeartThread = NULL;
//
m_recvHeartThread = NULL;
//
memset(&m_serverAddr, 0, sizeof(SOCKADDR_IN));
m_serverAddr.sin_family = AF_INET;
const char* ch = ip.c_str();
m_serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());
m_serverAddr.sin_port = htons(port);
}
Client::~Client()
{
closesocket(m_fd);
}
void Client::CreateSocket()
{
// create socket
m_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_fd < 0)
{
cout << "Create Socket Failed!";
AfxMessageBox(_T("Create Socket Failed!"), MB_ICONHAND);
exit(1);
}
else{}
}
void Client::Connect()
{
if (connect(m_fd, (struct sockaddr*)&m_serverAddr, sizeof(SOCKADDR_IN)) < 0)
{
AfxMessageBox(_T("Can not Connect to Server IP!"), MB_ICONHAND);
//根据实际情况决定:链接不上服务器是否退出
//exit(1);
}
else{}
}
void Client::RunSendHeart()
{
//创建发送心跳线程
m_sendHeartThread = AfxBeginThread(SendHeartProc, (void*)this, 0, 0, 0);
if (m_sendHeartThread = NULL)
{
exit(1);
}
else{}
}
void Client::RunRecvHeart()
{
//创建心跳接收线程
m_recvHeartThread = AfxBeginThread(RecvHeartProc, (void*)this, 0, 0, 0);
if (m_recvHeartThread = NULL)
{
exit(1);
}
else{}
}
UINT Client::SendHeartProc(LPVOID lparam)
{
Client* c = (Client*)lparam;
int _count(0);
//
while (1)
{
char send_buffer[1028];
PACKET send_head;
send_head.type = HEART;
//初始化报文内容
memset(send_head.msg, 0, sizeof(send_head.msg));
//初始化buffer
memset(send_buffer, 0, sizeof(send_buffer));
//将报文转buffer
memcpy(send_buffer, &send_head, sizeof(send_head));
int isSend = send(c->m_fd, send_buffer, sizeof(send_buffer), 0);
//如果服务端掉线,重连,直到连接上为止
if (isSend < 0)
{
closesocket(c->m_fd);
c->m_fd = socket(AF_INET, SOCK_STREAM, 0);
while (-1 == connect(c->m_fd, (struct sockaddr*)&(c->m_serverAddr), sizeof(c->m_serverAddr)))
{
Sleep(3000);
}
AfxMessageBox(_T("Server has been online!"), MB_ICONINFORMATION);
}
else{}
// 定时3秒
Sleep(3000);
}
return 0;
}
UINT Client::RecvHeartProc(LPVOID lparam)
{
Client* c = (Client*)lparam;
//
int _count(0);
//
while (1)
{
char recv_buffer[1028];
PACKET recv_head;
memset(recv_buffer, 0, sizeof(recv_buffer));
memset(&recv_head, 0, sizeof(recv_head));
//接收报文
int isRecv = recv(c->m_fd, recv_buffer, sizeof(recv_head), 0);
memcpy(&recv_head, recv_buffer, sizeof(recv_head));
//根据报文头类型进行处理
if (recv_head.type != HEART)
{
_count++;
if (_count == 5)
{
AfxMessageBox(_T("Server has been offline!"), MB_ICONINFORMATION);
}
else{}
}
else{}
Sleep(3000);
}
//
return 0;
}
版权声明:本文为Pan2015原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。