介绍
C++ Windows实现EchoServer非阻塞服务端(TPC协议)
客户端文章在:http://t.csdnimg.cn/1hkFr
Windows IDE:Visual Studio 2022
我们将采用分文件编写方式
支持库
#include <winsock2.h>
#include <string>
代码
main.h
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <string>
#pragma comment(lib,"ws2_32.lib")
class EchoServer
{
private:
int port = 0;
WORD sockVersion;
WSADATA data;
int InitCode;
SOCKET socketFD = 0;
SOCKET clientFD = 0;
sockaddr_in sin{};
sockaddr_in clientAddr{};
timeval timeout{};
fd_set wait{};
bool serverIsOpen = false;
int bufferSize = 1024;
bool isBlock = false;
public:
EchoServer();
~EchoServer();
int initServer(int port, bool noBlock = true);
void openServre();
std::string getClientIP();
int sendClientDate(std::string date);
std::string getClientDate(bool fastRead = false);
void CloseServer();
void setTimeout(int s = 3, int us = 0);
protected:
virtual void handleConnect() = 0;
};
这里使用了虚函数handleConnect作为接口
main.cpp
#include "main.h"
EchoServer::EchoServer()
{
sockVersion = MAKEWORD(2, 2);
InitCode = WSAStartup(sockVersion, &data);
setTimeout();
}
EchoServer::~EchoServer()
{
WSACleanup();
}
int EchoServer::initServer(int port, bool noBlock)
{
if (InitCode != 0)
return -3;
socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socketFD == INVALID_SOCKET)
return -2;
if (noBlock) {
u_long on = 1;
if (ioctlsocket(socketFD, FIONBIO, &on) < 0) {
return -4;
}
}
isBlock = !noBlock;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(socketFD, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
return -5;
if (listen(socketFD, 5) == SOCKET_ERROR)
return -1;
return 0;
}
void EchoServer::openServre()
{
serverIsOpen = true;
DWORD start, end, elapsedTime;
while (serverIsOpen)
{
if (!isBlock)
start = GetTickCount64();
int clientAddrSize = sizeof(clientAddr);
clientFD = accept(socketFD, (SOCKADDR*)&clientAddr, &clientAddrSize);
if (clientFD != INVALID_SOCKET) {
handleConnect();
closesocket(clientFD);
continue;
}
if (!isBlock)
end = GetTickCount64();
if (!isBlock)
elapsedTime = end - start;
if (!isBlock)
Sleep(elapsedTime);
}
}
std::string EchoServer::getClientIP()
{
std::string result = "";
result.append(inet_ntoa(clientAddr.sin_addr));
return result;
}
int EchoServer::sendClientDate(std::string date)
{
timeval tv = timeout;
FD_ZERO(&wait);
FD_SET(clientFD, &wait);
if (select(clientFD + 1, NULL, &wait, NULL, &tv) > 0) {
return send(clientFD, date.c_str(), date.size(), 0);
}
return -1;
}
std::string EchoServer::getClientDate(bool fastRead)
{
std::string result = "";
char* buffer = new char[bufferSize + 1];
int len = 0,ready;
buffer[len] = '\0';
timeval tv = timeout;
FD_ZERO(&wait);
FD_SET(clientFD, &wait);
while (true)
{
if (isBlock)
goto getMessage;
ready = select(clientFD + 1, &wait, NULL, NULL, &tv);
if (ready > 0) {
if (FD_ISSET(clientFD, &wait)) {
getMessage:
len = recv(clientFD, buffer, bufferSize, 0);
if (len > 0) {
buffer[len] = '\0';
result.append(buffer);
if (len < bufferSize && fastRead) {
break;
}
if (isBlock)
continue;
}
else
break;
}
}
else
break;
}
delete[] buffer;
return result;
}
void EchoServer::CloseServer()
{
if (socketFD)
closesocket(socketFD);
serverIsOpen = false;
}
void EchoServer::setTimeout(int s, int us)
{
timeout.tv_sec = s;
timeout.tv_usec = us;
}
示例
#include <iostream>
#include "main.h"
class EchoServerRewirte : public EchoServer {
void handleConnect() {
std::string result = getClientDate();
std::string ip = getClientIP();
std::cout << result << "来自IP:" << ip << "\n";
sendClientDate("close");
CloseServer();
}
};
int main()
{
EchoServerRewirte echoServer;
echoServer.initServer(1001);
echoServer.openServre();
return 0;
}