Socket 客户端与服务端通信【心跳机制+断线重连】

直接上代码,让代码说话!
//Server.h
#pragma once
#include  
#include 
#include 
#include 
#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 >		m_clientInfo;
};

//Server.cpp
#include "stdafx.h"
#include "Server.h"
#include 

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 >::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  
#include 
#include 
#include 

#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 

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

你可能感兴趣的:(Scoket)