基于 Windows API 的 Tcp 通信(Server 端为多线程模式)

1 设计思路简介

1.1 服务端通过 Server 类实现:

(1)通信初始化。包括套接字版本检查、套接字配置(套接字创建和地址配置);

(2)绑定套接字 + 端口监听;

(3)链接客户端。当客户端发来链接请求时,服务端与客户端连接;链接成功后,服务端起一个线程处理本次链接成功的客服端请求(接受客服端请求信息,给客服端返回响应数据)。

服务端仅仅提供一个通讯准备函数 ready(),不处理具体的数据,把业务逻辑留给线程函数。

1.2 客户端通过 Client 类实现:

(1)通信初始化。包括套接字版本检查,创建客户端套接字,配置将要链接的服务端的端口和ip地址等;

(2)发送链接请求。客户端主动给服务端发送链接请求,服务端起一个线程处理请求。

(3)设置发送数据。

(4)数据发送函数。

(5)数据接收函数。

客户端的数据都放到 Client 里方便读写操作。

2 Tcp通信实现(传输 struct 格式的数据)

/*
* 服务端 .h 文件
*/

#pragma once
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")

#include<winsock2.h>
#include<windows.h>
#include <iostream>


class Server
{
public:
	Server();
	~Server();

public:
	bool ready(int hton = 10008, const char* ip = "127.0.0.1", int backlog = 10);

private:
	bool intalize(int hton, const char* ip);
	bool blind_listen(int backlog);
	bool server_connect();

public:
	WSADATA data;
	SOCKET sock_server;
	SOCKET sock_client;
	SOCKADDR_IN add_server;
	SOCKADDR_IN add_client;
};

/*
* 服务端 .cpp 文件
*/

#include "Server.h"
#include<thread>

#define _CRT_SECURE_NO_WARINGS



bool Server::ready(int hton, const char* ip, int backlog)
{
	if (intalize(hton, ip) && blind_listen(backlog) && server_connect())
	{
		return true;
	}
	return false;
}

Server::Server()
{
	return;
}

Server::~Server()
{
	WSACleanup();
	closesocket(sock_client);
	closesocket(sock_server);
	return;
}

bool Server::intalize(int hton, const char* ip)
{
	//socket版本检查 
	if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
	{
		std::cout << "套接字的版本错误" << std::endl;
		return false;
	}

	//套接字接口的配置
	sock_server = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_server == INVALID_SOCKET)
	{
		std::cout << "设置失败" << std::endl;
		return false;
	}
	//配置地址的设置
	add_server.sin_family = AF_INET;
	add_server.sin_port = htons(hton);					//填写一个端口
	add_server.sin_addr.S_un.S_addr = inet_addr(ip);	//填写一个Ip地址

	return true;
}

bool Server::blind_listen(int backlog) {
	int n = sizeof(SOCKADDR_IN);

	if ((bind(sock_server, (LPSOCKADDR)&add_server, n) == SOCKET_ERROR) || (listen(sock_server, backlog) == SOCKET_ERROR))
	{
		std::cout << "套接字绑定失败或者监听失败" << std::endl;
		return false;
	}

	return true;
}

void serverThread(int sock_c);
//链接客户端 给每一个链接起一个线程
bool Server::server_connect()
{
	std::cout << "请稍等。。。" << "\t" << "正在准备链接客户端" << std::endl;
	Sleep(3000);
	while (true)
	{
		int len = sizeof(SOCKADDR);
		//首先开始链接客户端 
		sock_client = accept(sock_server, (LPSOCKADDR)&add_client, &len);
		if (sock_client == SOCKET_ERROR)
		{
			std::cout << "链接失败" << std::endl;
			break;
		}
		//起一个线程 
		std::thread(&serverThread,(int)sock_client).detach();
	}
	return true;
}





/*
* 服务端应用文件
*/


#include "Server.h"


int main()
{
	Server a;
	if (a.ready() == false) {
		return -1;
	}
	return 0;
}

typedef struct
{
	int ID;
	char action[8];		//接受并且进行打印的数组
	float speed;
	float x;
	float y;
	float z;
}DATA;

void serverThread(int sock_c)
{
	DATA requestData;
	DATA responseData;
	int i = 10;
	while (true) {
		if (recv((SOCKET)sock_c, (char*)&requestData, sizeof(DATA), 0) == SOCKET_ERROR) {
			std::cout << "线程 " << GetCurrentThreadId() << " 退出" << std::endl;
			return;
		}
		std::cout << "线程ID " << GetCurrentThreadId() <<
			"客户端的信息是:" << requestData.ID << " " <<
			requestData.action << " " << requestData.speed << " " <<
			requestData.x << " " << requestData.y << " " << requestData.z << "\n" << std::endl;

		responseData.ID = i;
		strcpy(responseData.action, "move");
		responseData.speed = i;
		responseData.x = i;
		responseData.y = i;
		responseData.z = i;
		if (send((SOCKET)sock_c, (const char*)&responseData, sizeof(DATA), 0) == SOCKET_ERROR)
		{
			std::cout << "线程 " << GetCurrentThreadId() << " 退出" << std::endl;
			return;
		}
		++i;
	}
}

/*
* 客户端 .h文件
*/

#pragma once
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")

#include<winsock2.h>
#include<windows.h>
#include <iostream>


typedef struct 
{
	int ID;
	char action[8];		//接受并且进行打印的数组
	float speed;
	float x;
	float y;
	float z;
}DATA;

class Client
{
public:
	Client();
	~Client();

public:
	bool ready();
	bool sendInfor();
	bool receiveInfor();

	void setSendData(DATA &sendData);
	DATA& getReceiveData();

private:
	bool intalize(int hton = 10008, const char *ip = "127.0.0.1");
	bool client_connect();

private:
	SOCKET sock_client;
	SOCKADDR_IN  addr_server;
	SOCKADDR_IN  client_ADDR;
	WSADATA data;
	int len;

private:
	DATA sendData;
	DATA receiveData;
};
/*
* 客服端 .cpp 文件
*/

#include "Client.h"


bool Client::ready() {
	if (intalize() && client_connect())
	{
		return true;
	}
	return false;
}

bool Client::sendInfor() {
	if (send(sock_client, (const char*)&sendData, sizeof(DATA), 0) == SOCKET_ERROR)
	{
		std::cout << "发送失败" << std::endl;
		return false;
	}
	return true;
}

bool Client::receiveInfor()
{
	if (recv(sock_client, (char*)&receiveData, sizeof(DATA), 0) == SOCKET_ERROR)
	{
		std::cout << "接收失败" << std::endl;
		return false;
	}

	std::cout << "线程ID " << GetCurrentThreadId() <<
		"  服务器的信息是:" << receiveData.ID << "\n" << receiveData.action <<
		"\n" << receiveData.speed << "\n" << receiveData.x << "\n" << receiveData.y <<
		"\n" << receiveData.z << "\n" << std::endl;

	return true;
}

void Client::setSendData(DATA& sendData)
{
	this->sendData = sendData;
}

DATA& Client::getReceiveData()
{
	return this->receiveData;
}




Client::Client()
{
	return;
}

Client::~Client()
{
	WSACleanup();
	closesocket(sock_client);
	return;
}

bool Client::intalize(int hton, const char* ip)
{
	//socket版本检查 
	if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
	{
		std::cout << "套接字的版本错误" << std::endl;
		return false;
	}
	//套接字接口的配置
	memset(&addr_server, 0, sizeof(addr_server));
	sock_client = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_client == INVALID_SOCKET)
	{
		std::cout << "设置失败" << std::endl;
		return false;
	}
	addr_server.sin_family = AF_INET;
	addr_server.sin_port = htons(hton);//选择一个电脑的任意端口
	addr_server.sin_addr.S_un.S_addr = inet_addr(ip);//填写一个Ip地址
	len = sizeof(SOCKADDR_IN);
	return true;
}

bool Client::client_connect()
{
	if (connect(sock_client, (LPSOCKADDR)&addr_server, len) == SOCKET_ERROR)
	{
		std::cout << "连接失败" << std::endl;
		return false;
	}

	return true;
}

/*
* 客服端应用文件
*/

#include "Client.h"

int main()
{
	Client a;
	if (a.ready()) {
		DATA  ser;
		ser.ID = 1;
		strcpy(ser.action, "success");
		ser.speed = 1;
		ser.x = 1;
		ser.y = 1;
		ser.z = 1;
		a.setSendData(ser);
		for (int i = 0 ; i < 100; ++i)
		{
			a.sendInfor();
			a.receiveInfor();
			Sleep(100);
		}

	}
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Pointer/article/details/108820127