Win32套接字编程——Tcp一对多(C++)


纯干货


客户端和服务器.h文件

#pragma once
#include <iostream>
#include <Windows.h>

class CTcpSocketBase
{
public:
	CTcpSocketBase() = default;
	virtual ~CTcpSocketBase() = default;

protected:
	virtual bool StartupWsa();
	virtual bool CreatSocket(SOCKET& SocketID);
};

class CTcpServer : public CTcpSocketBase
{
public:
	CTcpServer(const std::string& szNetAddres, uint32_t nNetPort);
	~CTcpServer();
	bool StartServer();

private:
	bool BindSocket();
	bool ListenSocket();
	static void ReceiveAllClientsFunc(LPVOID para);

private:
	SOCKET m_Socket = INVALID_SOCKET;
	std::string m_szNetAddres;
	uint32_t m_nNetPort;
};

class CTcpClient : public CTcpSocketBase
{
public: 
	CTcpClient(const std::string& szNetAddres, uint32_t nNetPort);
	~CTcpClient();
	bool StartClient();

private:
	bool ConnectSocket();
	static void ReceiveFromServerFunc(LPVOID para);

private:
	SOCKET m_Socket = INVALID_SOCKET;
	std::string m_szNetAddres;
	uint32_t m_nNetPort;
};

客户端和服务器.cpp文件

#include "NetServerSingleUser.h"
#pragma comment(lib,"ws2_32.lib")

bool CTcpSocketBase::StartupWsa()
{
	WSADATA wsaData;
	auto Result = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (0 != Result)
	{
		std::cout << "Startup wsa failed. Error codeid:" << Result << std::endl;
		return false;
	}
	return true;
}

bool CTcpSocketBase::CreatSocket(SOCKET &SocketID)
{
	SOCKET ServerID = ::socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == ServerID)
	{
		std::cout << "Creat socket failed. Error codeid:" << WSAGetLastError() << std::endl;
		return false;
	}

	SocketID = ServerID;
	return true;
}

CTcpServer::CTcpServer(const std::string& szNetAddres, uint32_t nNetPort)
	: m_szNetAddres(szNetAddres), m_nNetPort(nNetPort)
{
}

CTcpServer::~CTcpServer()
{
}


bool CTcpServer::StartServer()
{
	SOCKET ClientID = INVALID_SOCKET;

	if (!StartupWsa())
	{
		return false;
	}
	std::cout << "Startup wsa successful." << std::endl;

	if (!CreatSocket(m_Socket))
	{
		return false;
	}
	std::cout << "Creat socket successful." << std::endl;

	if (!BindSocket())
	{
		return false;
	}
	std::cout << "Bind socket successful." << std::endl;

	if (!ListenSocket())
	{
		return false;
	}
	std::cout << "Listen socket successful." << std::endl;

	while (true)
	{
		ClientID = ::accept(m_Socket, NULL, NULL);
		if (INVALID_SOCKET == ClientID)
		{
			std::this_thread::sleep_for(std::chrono::seconds(1));
		}

		_beginthread(CTcpServer::ReceiveAllClientsFunc, NULL, &ClientID);
	}
}

bool CTcpServer::BindSocket()
{
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(m_nNetPort);
	serverAddr.sin_addr.S_un.S_addr = inet_addr(m_szNetAddres.c_str());

	auto Result = ::bind(m_Socket, (sockaddr*)&serverAddr, sizeof(sockaddr));
	if (SOCKET_ERROR == Result)
	{
		std::cout << "Bind socket failed. Error codeid:" << WSAGetLastError() << std::endl;
		return false;
	}

	return true;
}

bool CTcpServer::ListenSocket()
{
	auto Result = ::listen(m_Socket, SOMAXCONN);
	if (SOCKET_ERROR == Result)
	{
		std::cout << "Listen socket failed. Error codeid:" << WSAGetLastError() << std::endl;
		return false;
	}

	return true;
}

void CTcpServer::ReceiveAllClientsFunc(LPVOID para)
{
	if (nullptr == para)
	{
		std::cout << "ClientID is NULL." << std::endl;
		return;
	}
	SOCKET ClientID = *((SOCKET*)para);

	char cMsg[200] = { 0 };
	sockaddr_in ClientMsg;
	int nLen = sizeof(sockaddr);
	
	getpeername(ClientID, (sockaddr*)&ClientMsg, &nLen);
	std::cout << "Client(" << ClientMsg.sin_addr.S_un.S_addr << " : " << ClientMsg.sin_port << "): has connected!" << std::endl;

	while (true)
	{
		memset(cMsg, '\0', sizeof(cMsg));

		if (::recv(ClientID, cMsg, sizeof(cMsg), NULL) < 0)
		{
			std::cout << "Receive message failed!" << std::endl;;
			return;
		}

		std::cout << "Client(" << ClientMsg.sin_port << "): " << cMsg << std::endl;
		::send(ClientID, "Received,Please send again.", 100, NULL); //其实一般项目,服务端收到客户端消息后,都是需要解析这个消息并返回对应报文的,肯定不会这么随意
	}
}

CTcpClient::CTcpClient(const std::string& szNetAddres, uint32_t nNetPort)
	: m_szNetAddres(szNetAddres), m_nNetPort(nNetPort)
{
}

CTcpClient::~CTcpClient()
{
}

bool CTcpClient::StartClient()
{
	if (!StartupWsa())
	{
		return false;
	}
	std::cout << "Startup wsa successful." << std::endl;

	if (!CreatSocket(m_Socket))
	{
		return false;
	}
	std::cout << "Creat socket successful." << std::endl;

	if (!ConnectSocket())
	{
		return false;
	}
	std::cout << "Connect socket successful." << std::endl;

	_beginthread(CTcpClient::ReceiveFromServerFunc, NULL, &m_Socket);

	char cMsg[200] = { 0 };
	while (true)
	{
		memset(cMsg, '\0', sizeof(cMsg));
		std::cin.getline(cMsg, 100);
		send(m_Socket, cMsg, 100, NULL);
	}
}

bool CTcpClient::ConnectSocket()
{
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(m_nNetPort);
	serverAddr.sin_addr.S_un.S_addr = inet_addr(m_szNetAddres.c_str());

	auto Result = connect(m_Socket, (SOCKADDR*)&serverAddr, sizeof(serverAddr));
	if (SOCKET_ERROR == Result)
	{
		std::cout << "Connect socket failed. Error codeid:" << WSAGetLastError() << std::endl;
		return false;
	}

	return true;
}

void CTcpClient::ReceiveFromServerFunc(LPVOID para)
{
	if (nullptr == para)
	{
		std::cout << "ServerID is NULL." << std::endl;
		return;
	}
	SOCKET ServerID = *((SOCKET*)para);

	char CMsg[200] = { 0 };

	while (true)
	{
		memset(CMsg, '\0', sizeof(CMsg));

		if (recv(ServerID, CMsg, 200, NULL) < 0)
		{
			std::cout << "recv fail!" << std::endl;
			return;
		}

		std::cout << "(Server): " << CMsg << std::endl;
	}
}
int main(int argc, char *argv[])
{
	if (strcmp(argv[1], "-S") == 0)
	{
		CTcpServer server("127.0.0.1", 23);
		server.StartServer();
	}
	else if (strcmp(argv[1], "-C") == 0)
	{
		CTcpClient client("127.0.0.1", 23);
		client.StartClient();
	}
	else 
	{
		std::cout << "Parameter error!" << std::endl;
	}
	return 0;
}

注释:
萌新运行方式:找到编译出来的可执行程序.exe,cmd下运行.exe程序,添加执行参数-S开启服务端,执行参数-C开启客户端,有兴趣扩展将地址和端口作为参数传入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I love LiLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值