介绍

C++ Windows实现EchoServer非阻塞客户端(TPC协议)
Windows IDE:Visual Studio 2022
我们将采用分文件编写方式

支持库

#include <string>
#include <winsock2.h>

代码

main.h

#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <string>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

class EchoClient
{
private:
	SOCKET socketFD = 0;
	WORD sockVersion;
	WSADATA data;
	int InitCode;

	timeval timeout{};

	sockaddr_in serAddr{};

	fd_set wait{};

	int bufferSize = 1024;

        bool block;

public:
	EchoClient();
	~EchoClient();

	int connectServer(std::string ip, int port, bool noBlock = true);

	int sendServerDate(std::string date);

	void closeConnect();

	void setTimeout(int s = 3, int us = 0);

	std::string getServerDate();
};

main.cpp

#include "main.h"

EchoClient::EchoClient()
{
	sockVersion = MAKEWORD(2, 2);
	InitCode = WSAStartup(sockVersion, &data);
	setTimeout();
}

EchoClient::~EchoClient()
{
	WSACleanup();
}

int EchoClient::connectServer(std::string ip, 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;
		}
	}

	block = !noBlock;
 
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(port);
	serAddr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
	int ret = connect(socketFD, (sockaddr*)&serAddr, sizeof(serAddr));
 
	if (block) {
		if (ret >= 0)
			return 0;
		else
			return -1;
	}
	if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
		return -2;
	}

	FD_ZERO(&wait);
	FD_SET(socketFD, &wait);

	timeval tv = timeout;
	ret = select(socketFD + 1, NULL, &wait, NULL, &tv);

	if (ret == 0) {
		return -1;
	}
	else if (ret < 0) {
		return -2;
	}

	return 0;
}

int EchoClient::sendServerDate(std::string date)
{
	int code = send(socketFD, date.c_str(), date.size(), 0);
	return code;
}

void EchoClient::closeConnect()
{
	closesocket(socketFD);
}

void EchoClient::setTimeout(int s, int us)
{
	timeout.tv_sec = s;
	timeout.tv_usec = us;
}

std::string EchoClient::getServerDate()
{
	std::string result = "";
	char * buffer = new char[bufferSize];
	int len = 0,ready;
	buffer[len] = '\0';

	timeval tv = timeout;
	FD_ZERO(&wait);
	FD_SET(socketFD, &wait);

	while (true)
	{
		if (block)
			goto getMessage;
		ready = select(socketFD + 1, &wait, NULL, NULL, &tv);
		if (ready > 0) {
			if (FD_ISSET(socketFD, &wait)) {
			getMessage:
				len = recv(socketFD, buffer, bufferSize - 1, 0);
				if (len > 0) {
					buffer[len] = '\0';
					result.append(buffer);
					if (block)
						continue;
				}	
				else 
					break;
			}
		}
		else
			break;
	}
	delete[] buffer;
	return result;
}


测试

假设服务端地址:192.168.2.2 | 端口是1001

#include "main.h"
#include <iostream>

int main()
{
	std::string ip = "192.168.2.2";
	EchoClient ehoClient;
	while (true)
	{
		std::cout << "开始连接:" << ip;
		if (ehoClient.connectServer(ip, 1001) == 0) {
			ehoClient.setTimeout(1);
			ehoClient.sendServerDate("hello");
			std::string result = ehoClient.getServerDate();
			std::cout << "[Success] 发送数据:hello | 返回数据:" << result << "\n";
			if (result == "close") {
				std::cout << "Find:" << ip;
				ehoClient.closeConnect();
				break;
			}
				
		}
		else {
			std::cout << "[Fail]\n";
		}
	}
	return 0;

}

这里展示一下局域网快速搜索
假设局域网IP段是192.168.222 | 端口是1001

#include "main.h"
#include <iostream>

int main()
{
	std::string ipBasic = "192.168.222.";
	std::string ip = "";
	uint8_t i = 0;
	EchoClient ehoClient;

	ehoClient.setTimeout(0,1000);
	while (true)
	{
		ip = ipBasic + std::to_string(i++);
		std::cout << "开始连接:" << ip;
		if (ehoClient.connectServer(ip, 1001) == 0) {
			ehoClient.setTimeout(1);
			ehoClient.sendServerDate("hello");
			std::string result = ehoClient.getServerDate();
			std::cout << "[Success] 发送数据:hello | 返回数据:" << result << "\n";
			if (result == "close") {
				std::cout << "Find:" << ip;
				ehoClient.closeConnect();
				break;
			}
				
		}
		else {
			std::cout << "[Fail]\n";
		}
	}
	return 0;

}