直接上代码,让代码说话! //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; }