C++封装的基于WinSock2的TCP服务端、客户端

  无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习。

  只写好了TCP通信服务端,有空把客户端流程也封装一下。

  先上主函数:

// main.cpp : 异想家sandeepin poi!
#include "stdafx.h"
#include <iostream>
extern int JTCPserver();
extern int JTCPclient();
int main()
{
	JTCPserver(); // 两者选一
//	JTCPclient();
	return 0;
}
  JTCPserver.cpp内容:

// JTCPserver.cpp : 蒋方正封装的TCP服务端

#include "stdafx.h"
#include <iostream>
#include <string>
#include <WinSock2.h> // WinSocket 
#include <WS2tcpip.h> // IP地址转换用到inet_pton
#pragma comment(lib,"ws2_32.lib")  
using namespace std;

// 【1】初始化WinSock
bool initWinSock();
// 【2】创建socket
bool createSocket(SOCKET &listenScok);
// 【3】socket绑定本机地址信息
bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port);
// 【4】侦听socket,接收客户端请求
bool listenSocket(SOCKET &listenScok);
// 【5】等待客户端连接-阻塞
bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock);
// 【6】接收数据-阻塞
bool receiveData(SOCKET &clientSock, string &data);
// 【7】停止套接字的接收、发送
bool shutdownSocket(SOCKET &clientSock);
// 【8】发送信息
bool sendData(SOCKET &clientSock, const string &data);

int JTCPserver()
{		
	SOCKET listenScok;	// 服务端Socket
	SOCKET clientSock;	// 客户端Socket
	string data;		// 收到的数据

	// 【1】初始化WinSock
	initWinSock();
	// 【2】创建socket
	createSocket(listenScok);
	// 【3】socket绑定本机地址信息
	bindIPandPort(listenScok, "127.0.0.1", 1994);
	// 【4】侦听socket,接收客户端请求
	listenSocket(listenScok);

	// 坐等客户端连接
	bool isClientSockConnect = false;	// 是否有客户端连进来
	bool isReceiveData = false;			// 是否接收数据成功

	while (true)
	{ 
		if (!isClientSockConnect)
		{
			// 【5】等待客户端连接
			isClientSockConnect = waitClientConnect(listenScok, clientSock);
		}
		else
		{
			if(!isReceiveData)
			{
				// 【6】接收数据-阻塞
				isReceiveData = receiveData(clientSock, data);
				// 如果接收数据失败则断开
				if(!isReceiveData) 
				{
					// 【7】停止套接字的接收、发送
					shutdownSocket(clientSock);
					cout << "等待客户端再连接..." << endl;
					isClientSockConnect = false; // 可以重连了
				}
			}
			if(isReceiveData && data != "jfzpoi"  && data != "@end#")
			{
				isReceiveData = false;
			}
			if(isReceiveData && data == "jfzpoi")
			{
				// 【8】发送信息(收的数据为jfzpoi)
				sendData(clientSock, "sandeepin!\r\n");
				isReceiveData = false;
			}
			if (isReceiveData && data == "@end#")
			{
				// 【9】关闭相关
				int err;
//				err = shutdown(listenScok, 2);
//				if (err == SOCKET_ERROR)
//				{
//					cout << "关闭失败!" << endl;
//				}
				// 关闭套接字,释放资源
				err = closesocket(listenScok);
				if (err == SOCKET_ERROR)
				{
					cout << "关闭socket失败!" << endl;
				}
				// 停止使用WinSock库,释放对应资源
				if (WSACleanup() != 0)
				{
					cout << "WSA清空失败!" << endl;
				}
				cout << "关完了,坐等关机!poi" << endl;
				return 0;
			}
		}
	}
}

// 【1】初始化WinSock
bool initWinSock()
{
	WORD verision = MAKEWORD(2, 2);
	WSADATA lpData;
	int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
	if (intEr != 0)
	{
		cout << "WinSock初始化失败!" << endl;
		return false;
	}
	cout << "WinSock初始化成功!" << endl;
	return true;
}

// 【2】创建socket
bool createSocket(SOCKET &listenScok)
{
	// 创建侦听socket  
	listenScok = socket(AF_INET, SOCK_STREAM, 0);
	if (listenScok == INVALID_SOCKET)
	{
		cout << "socket创建失败!" << endl;
		return false;
	}
	cout << "socket创建成功!" << endl;
	return true;
}

// 【3】socket绑定本机地址信息
bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port)
{
	// 制作sockaddr_in结构体
	// 在bind函数,connect函数里提到了套接字编程网络地址信息结构体const struct sockaddr和const struct sockaddr_i
	sockaddr_in hostAddr;
	hostAddr.sin_family = AF_INET;
	hostAddr.sin_port = htons(port);//转换成网络字节序  
									//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序  
									//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;  
									/*
									inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
									头文件:WS2tcpip.h
									*/
	in_addr addr;
	inet_pton(AF_INET, ip.c_str(), (void*)&addr);
	hostAddr.sin_addr = addr;
	cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
	cout << "ip(常规形式):" << ip.c_str() << endl;

	// 侦听套接字listenSock绑定本机地址信息  
	int err = bind(listenScok, (struct sockaddr*)&hostAddr, sizeof(sockaddr));
	if (err != 0)
	{
		cout << "本地IP绑定失败!" << endl;
		return false;
	}
	return true;
}

// 【4】侦听socket,接收客户端请求
bool listenSocket(SOCKET &listenScok)
{
	// 设定套接字为侦听状态,准备接收客户机进程发送来的连接请求
	int err = listen(listenScok, 3);
	if (err != 0)
	{
		cout << "socket监听失败!" << endl;
		return false;
	}
	cout << "监听客户端连接中……" << endl;
	return true;
}

// 【5】等待客户端连接-阻塞
bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock)
{
	sockaddr_in clientAddr;
	int len = sizeof(struct sockaddr); // 必须指定长度,否则会导致accept返回10014错误
									   // accept会循环等待客户端连接
	clientSock = accept(listenScok, (struct sockaddr*)&clientAddr, &len);
	cout << "客户端Socket编号:" << clientSock << endl;
	if (clientSock == INVALID_SOCKET)
	{
		cout << "客户端连接失败!" << endl;
		cout << WSAGetLastError() << endl;
		return false;
	}
	return true;
}

// 【6】接收数据-阻塞
bool receiveData(SOCKET &clientSock, string &data)
{
	static int cnt = 1; // 接收数据编号-静态
						// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
	char buf[1024] = "\0";
	// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
	int buflen = recv(clientSock, buf, 1024, 0);
	if (buflen == SOCKET_ERROR)
	{
		cout << "接收失败!" << endl;
		return false;
	}
	// 一切正常则显示接收数据
	data = string(buf);
	cout << "收到第" << cnt++ << "次,内容为:\n" << buf << endl;
	return true;
}

// 【7】停止套接字的接收、发送
bool shutdownSocket(SOCKET &clientSock)
{
	//(收完就关)停止套接字的接收、发送功能(0禁止接收,1禁止发送,2禁制接收发送)
	int err = shutdown(clientSock, 2);
	if (err == SOCKET_ERROR)
	{
		cout << "关闭Socket失败!" << endl;
		return false;
	}
	return true;
}

// 【8】发送信息
bool sendData(SOCKET &clientSock, const string &data)
{

	int err = send(clientSock, data.c_str(), data.size(), 0);
	if (err == SOCKET_ERROR)
	{
		cout << "发送失败!" << endl;
		return false;
	}
	cout << "发送数据为:\n" << data << endl;
	return true;
}
  JTCPclient.cpp内容:

// JTCPclient.cpp : 蒋方正封装的TCP客户端代码

#include "stdafx.h"  
#include <iostream>
#include <WinSock2.h>  
#include <WS2tcpip.h>  
#pragma comment(lib,"ws2_32.lib")
using namespace std;

// 【1】初始化WinSock
bool initWinSockC();
// 【2】创建socket
bool createSocketC(SOCKET &listenScok);
// 【3】连接到服务器
bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port);
// 【4】发送数据
bool sendDataC(SOCKET &clientSock, const string &data);
// 【5】接收数据
bool receiveDataC(SOCKET &clientSock, string &data);

int JTCPclient()
{
	SOCKET clientSock;	// 客户端Socket
	string data;		// 收到的数据

	bool isCreateSocket = false;	// 是否创建了Socket
	bool isConnectSocket = false;	// 是否连上了服务器
	bool isSendDataOK = false;		// 是否发送成功数据
	bool isReceiveDataOK = false;	// 是否接收成功数据

	// 【1】初始化WinSock
	if(initWinSockC())
	{
		while (true)
		{
			if(!isCreateSocket)
			{
				// 【2】创建socket
				createSocketC(clientSock);
				isCreateSocket = true;
			}
			else
			{
				if(!isConnectSocket)
				{
					// 【3】连接到服务器
					connectSocketC(clientSock, "127.0.0.1", 1994);
					isConnectSocket = true;
				}
				else
				{
					if(!isSendDataOK)
					{
						// 【4】发送数据
						isSendDataOK = sendDataC(clientSock, "jfz hello\r\n");
					}
					else
					{
						if(!isReceiveDataOK)
						{
							// 【5】接收数据
							isReceiveDataOK = receiveDataC(clientSock, data);
						}
						else
						{
							if(data == "@end#")
							{
								WSACleanup();
								return 0;
							}
							isReceiveDataOK = false;
							isSendDataOK = false;
						}
					}
					
				}
			}
		}
	}
	return 0;
}

// 【1】初始化WinSock
bool initWinSockC()
{
	WORD verision = MAKEWORD(2, 2);
	WSADATA lpData;
	int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
	if (intEr != 0)
	{
		std::cout << "WinSock初始化失败!" << endl;
		return false;
	}
	std::cout << "WinSock初始化成功!" << endl;
	return true;
}

// 【2】创建socket
bool createSocketC(SOCKET &listenScok)
{
	// 创建侦听socket  
	listenScok = socket(AF_INET, SOCK_STREAM, 0);
	if (listenScok == INVALID_SOCKET)
	{
		cout << "socket创建失败!" << endl;
		return false;
	}
	cout << "socket创建成功!" << endl;
	return true;
}

// 【3】连接到服务器
bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port)
{
	// 建立地址结构体
	sockaddr_in hostAddr;
	hostAddr.sin_family = AF_INET;
	hostAddr.sin_port = htons(port);//转换成网络字节序  
									//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序  
									//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;  
									/*
									inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
									头文件:WS2tcpip.h
									*/
	in_addr addr;
	inet_pton(AF_INET, ip.c_str(), (void*)&addr);
	hostAddr.sin_addr = addr;
	cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
	cout << "ip(常规形式):" << ip.c_str() << endl;

	// 向服务器提出连接请求
	int err = connect(conSock, (sockaddr*)&hostAddr, sizeof(sockaddr));
	if (err == INVALID_SOCKET)
	{
		cout << "连接服务器失败!" << endl;
		return false;
	}
	return true;
}

// 【4】发送数据
bool sendDataC(SOCKET &clientSock, const string &data)
{
	int err = send(clientSock, data.c_str(), data.size(), 0);
	if (err == SOCKET_ERROR)
	{
		cout << "发送失败!" << endl;
		return false;
	}
	cout << "发送数据为:\n" << data.c_str() << endl;
	return true;
}

// 【5】接收数据
bool receiveDataC(SOCKET &clientSock, string &data)
{
	static int cnt = 1; // 接收数据编号-静态
						// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
	char buf[1024] = "\0";
	// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
	int buflen = recv(clientSock, buf, 1024, 0);
	if (buflen == SOCKET_ERROR)
	{
		cout << "接收失败!" << endl;
		return false;
	}
	// 一切正常则显示接收数据
	data = string(buf);
	cout << "收到第" << cnt++ << "次,内容为:\n" << buf << endl;
	return true;
}

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的 C++ 代码示例,实现基于发布订阅模式的服务端客户端服务端代码: ```cpp #include <iostream> #include <vector> #include <string> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") // 服务端监听的端口号 const int PORT = 8888; // 最大连接数 const int MAX_CONN = 5; // 服务端接收消息的缓冲区大小 const int BUFFER_SIZE = 1024; // 发布-订阅模式中的“主题” std::string topic; // 已连接的客户端列表 std::vector<SOCKET> clients; // 客户端消息队列 std::vector<std::string> message_queue; // 互斥锁和条件变量,用于线程同步 std::mutex mtx; std::condition_variable cv; // 线程函数,用于接收客户端消息并广播给所有已连接的客户端 void broadcast_messages() { while (true) { // 等待条件变量被唤醒 std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [] { return !message_queue.empty(); }); // 从消息队列中取出一条消息 std::string message = message_queue.front(); message_queue.erase(message_queue.begin()); // 广播消息给所有已连接的客户端 for (auto client : clients) { send(client, message.c_str(), message.size(), 0); } } } int main() { // 初始化 Winsock 库 WSADATA wsaData; int ret = WSAStartup(MAKEWORD(2, 2), &wsaData); if (ret != 0) { std::cerr << "WSAStartup failed with error: " << ret << std::endl; return -1; } // 创建一个 TCP socket 对象 SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (server_socket == INVALID_SOCKET) { std::cerr << "Failed to create server socket with error: " << WSAGetLastError() << std::endl; WSACleanup(); return -1; } // 绑定 IP 地址和端口号 sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); ret = bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)); if (ret == SOCKET_ERROR) { std::cerr << "Failed to bind server socket with error: " << WSAGetLastError() << std::endl; closesocket(server_socket); WSACleanup(); return -1; } // 开始监听,等待客户端连接 ret = listen(server_socket, MAX_CONN); if (ret == SOCKET_ERROR) { std::cerr << "Failed to listen on server socket with error: " << WSAGetLastError() << std::endl; closesocket(server_socket); WSACleanup(); return -1; } std::cout << "Server started listening on port " << PORT << std::endl; // 创建一个线程,用于接收客户端消息并广播给所有已连接的客户端 std::thread broadcast_thread(broadcast_messages); while (true) { // 接受客户端连接请求 sockaddr_in client_addr; int addr_len = sizeof(client_addr); SOCKET client_socket = accept(server_socket, (sockaddr*)&client_addr, &addr_len); if (client_socket == INVALID_SOCKET) { std::cerr << "Failed to accept client connection with error: " << WSAGetLastError() << std::endl; continue; } std::cout << "Accepted a client connection from " << inet_ntoa(client_addr.sin_addr) << std::endl; // 服务端客户端发送欢迎消息 std::string welcome_message = "Welcome to the server!"; send(client_socket, welcome_message.c_str(), welcome_message.size(), 0); // 将客户端加入已连接客户端列表 clients.push_back(client_socket); // 循环接收客户端消息 while (true) { // 接收客户端消息 char buffer[BUFFER_SIZE] = { 0 }; int recv_len = recv(client_socket, buffer, BUFFER_SIZE, 0); if (recv_len == SOCKET_ERROR) { std::cerr << "Failed to receive data from client with error: " << WSAGetLastError() << std::endl; break; } else if (recv_len == 0) { std::cout << "Client disconnected." << std::endl; break; } // 打印客户端消息 std::cout << "Message from client: " << buffer << std::endl; // 将客户端消息加入消息队列 std::string message = "[" + topic + "] " + buffer; message_queue.push_back(message); // 唤醒线程,广播消息给所有已连接的客户端 cv.notify_all(); } // 从已连接客户端列表中删除该客户端 clients.erase(std::remove(clients.begin(), clients.end(), client_socket), clients.end()); // 关闭客户端连接 closesocket(client_socket); } // 关闭服务端 socket closesocket(server_socket); // 等待线程结束 broadcast_thread.join(); // 清理 Winsock 库 WSACleanup(); return 0; } ``` 客户端代码: ```cpp #include <iostream> #include <string> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") // 服务端 IP 地址和端口号 const char* SERVER_IP = "127.0.0.1"; const int SERVER_PORT = 8888; // 客户端发送消息的缓冲区大小 const int BUFFER_SIZE = 1024; // 发布-订阅模式中的“主题” std::string topic; // 互斥锁和条件变量,用于线程同步 std::mutex mtx; std::condition_variable cv; // 线程函数,用于接收服务端广播的消息 void receive_messages(SOCKET client_socket) { while (true) { // 接收服务端广播的消息 char buffer[BUFFER_SIZE] = { 0 }; int recv_len = recv(client_socket, buffer, BUFFER_SIZE, 0); if (recv_len == SOCKET_ERROR) { std::cerr << "Failed to receive data from server with error: " << WSAGetLastError() << std::endl; break; } else if (recv_len == 0) { std::cout << "Server disconnected." << std::endl; break; } // 打印收到的消息 std::cout << "Message from server: " << buffer << std::endl; } } int main() { // 初始化 Winsock 库 WSADATA wsaData; int ret = WSAStartup(MAKEWORD(2, 2), &wsaData); if (ret != 0) { std::cerr << "WSAStartup failed with error: " << ret << std::endl; return -1; } // 创建一个 TCP socket 对象 SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (client_socket == INVALID_SOCKET) { std::cerr << "Failed to create client socket with error: " << WSAGetLastError() << std::endl; WSACleanup(); return -1; } // 连接服务端 sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); server_addr.sin_port = htons(SERVER_PORT); ret = connect(client_socket, (sockaddr*)&server_addr, sizeof(server_addr)); if (ret == SOCKET_ERROR) { std::cerr << "Failed to connect to server with error: " << WSAGetLastError() << std::endl; closesocket(client_socket); WSACleanup(); return -1; } std::cout << "Connected to server " << SERVER_IP << ":" << SERVER_PORT << std::endl; // 接收服务端欢迎消息 char buffer[BUFFER_SIZE] = { 0 }; int recv_len = recv(client_socket, buffer, BUFFER_SIZE, 0); if (recv_len == SOCKET_ERROR) { std::cerr << "Failed to receive data from server with error: " << WSAGetLastError() << std::endl; closesocket(client_socket); WSACleanup(); return -1; } else if (recv_len == 0) { std::cout << "Server disconnected." << std::endl; closesocket(client_socket); WSACleanup(); return -1; } // 打印服务端欢迎消息 std::cout << "Message from server: " << buffer << std::endl; // 循环发送消息 while (true) { // 从控制台读取用户输入的消息 std::cout << "请输入消息:"; std::string message; std::getline(std::cin, message); // 如果用户输入 quit,则退出循环 if (message == "quit") { break; } // 将消息发送给服务端 std::string full_message = "[" + topic + "] " + message; send(client_socket, full_message.c_str(), full_message.size(), 0); // 创建一个线程,用于接收服务端广播的消息 std::thread receive_thread(receive_messages, client_socket); // 等待线程结束 receive_thread.join(); } // 关闭客户端 socket closesocket(client_socket); // 清理 Winsock 库 WSACleanup(); return 0; } ``` 此代码实现了一个简单的发布订阅模式,服务端接收客户端消息后会将其广播给所有已连接的客户端。如果您需要更灵活的订阅机制,可以使用一些成熟的消息队列中间件,例如 RabbitMQ 或 Apache Kafka。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值