【C++】创建TCP客户端

目录

一、实现发送字符串功能

二、实现接收字符串功能

三、客户端接收乱码问题

四、客户端发送乱码问题

五、客户端接收到数据时进行回调

六、子线程接收数据

七、发送Json格式数据

源码


一、实现发送字符串功能

头文件

#pragma once
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
using namespace std;

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

class TCPClient {
public:
	TCPClient(string ip, int port);
	int createSocket();  //创建套接字
	void setServerAddress();  //设置服务器地址信息
	int connectServer();  //连接到服务器
	void sendMsg(const char* sendbuf);  //发送数据

	int iResult;
	SOCKET clientSocket;
	sockaddr_in serverAddress;
	string IP;
	int Port;
};

源文件

# include "TCPClientTest.h"

TCPClient::TCPClient(string ip,int port):IP(ip),Port(port)
{
	WSADATA wsaData;
	this->iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		std::cerr << "WSAStartup failed: " << iResult << std::endl;
	}
	else
	{
		this->createSocket();
	}
}

int TCPClient::createSocket()	// 创建套接字
{
	this->clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (clientSocket == INVALID_SOCKET) {
		std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl;
		WSACleanup();
		return 1;
	}
	else
	{
		this->setServerAddress();
	}
}

void TCPClient::setServerAddress()  // 设置服务器地址信息
{
	this->serverAddress;
	serverAddress.sin_family = AF_INET;  //IPv4
	serverAddress.sin_port = htons(Port); // 服务器端口
	inet_pton(AF_INET, IP.c_str(), &serverAddress.sin_addr); // 服务器 IP 地址
	this->connectServer();
}

int TCPClient::connectServer()  // 连接到服务器
{
	this->iResult = connect(clientSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress));
	if (this->iResult == SOCKET_ERROR) {
		std::cerr << "connect failed: " << WSAGetLastError() << std::endl;
		closesocket(clientSocket);
		WSACleanup();
		return 1;
	}
	else
	{
		std::cout << "Connected to server." << std::endl;
	}
}

void TCPClient::sendMsg(const char* sendbuf)  // 发送数据
{
	this->iResult = send(clientSocket, sendbuf, strlen(sendbuf), 0);
	if (this->iResult == SOCKET_ERROR) {
		std::cerr << "send failed: " << WSAGetLastError() << std::endl;
		closesocket(clientSocket);
		WSACleanup();
	}
}

调用:

TCPClient("127.0.0.1", 8888).sendMsg("hello");

结果:

二、实现接收字符串功能

在头文件中添加一个接收数据的方法

string recvMsg();  //接收数据

在源文件中实现:

string TCPClient::recvMsg()  //接收数据
{
	char recvbuf[1024];
	this->iResult = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
	if (this->iResult > 0) {
		return string(recvbuf, iResult);
	}
	else if (this->iResult == 0) {
		std::cout << "Connection closed" << std::endl;
		return "1";
	}
	else {
		std::cerr << "recv failed: " << WSAGetLastError() << std::endl;
		return "2";
	}
}

调用:

TCPClient client = TCPClient("127.0.0.1", 8888);
	while (true)
	{
		string msg = client.recvMsg();
		if (msg=="1"||msg=="2")
		{
			break;
		}
		else
		{
			cout << msg << endl;
		}
	}

三、客户端接收乱码问题

如果服务端发来的是中文,可能接收打印的是乱码,为了解决这个问题,我们添加如下代码。首先在头文件中定义一个编码转换的方法:

string utf8ToGbk(const std::string& utf8Str);  //解决中文乱码

在源文件中实现:

string TCPClient::utf8ToGbk(const string& utf8Str) {
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);
	wchar_t* wstr = new wchar_t[len];
	MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wstr, len);

	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
	char* gbkStr = new char[len];
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, gbkStr, len, nullptr, nullptr);

	string result(gbkStr);
	delete[] wstr;
	delete[] gbkStr;
	return result;
}

在接收数据时调用该方法

运行结果如下,可以看到客户端可以正常接收打印中文信息。

四、客户端发送乱码问题

在头文件中定义一个转utf8编码的方法 

在源文件中实现:

string TCPClient::localToUtf8(const string& localStr)
{
	int len = MultiByteToWideChar(CP_ACP, 0, localStr.c_str(), -1, nullptr, 0);
	vector<wchar_t> wstr(len);
	MultiByteToWideChar(CP_ACP, 0, localStr.c_str(), -1, wstr.data(), len);

	len = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr);
	vector<char> utf8Str(len);
	WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, utf8Str.data(), len, nullptr, nullptr);

	return string(utf8Str.data());
}

在发送数据时使用编码转换方法

此时发送中文就不会乱码了

TCPClient client = TCPClient("127.0.0.1", 8888);
string msg = "你好";
client.sendMsg(msg.c_str());

五、客户端接收到数据时进行回调

对客户端的接收函数做如下修改,让TCPClient类的receiveData方法接受一个回调函数指针作为参数,当有数据接收时,调用这个回调函数并将接收到的数据作为参数传递给它。 

main函数中,TCPClient对象在连接到服务器后,调用receiveData方法并传入一个回调函数dataReceivedCallback,当有数据接收时,这个回调函数会被调用并输出接收到的数据。

结果如下所示,当服务端发送数据后,客户端会执行回调函数,输出打印服务端发来的数据,但是会阻塞main函数后续的逻辑,因此需要将接收函数作为一个子线程去执行。

六、子线程接收数据

在头文件中先引入线程库

对接收函数做如下修改

void TCPClient::recvMsg(void(*callback)(const std::string&))  //接收数据
{
	thread thread_recvMsg([this, callback]() {
		char recvbuf[1024];
		while (true)
		{
			this->iResult = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
			if (this->iResult > 0) {
				string recvData = utf8ToGbk(string(recvbuf, iResult));
				string receivedData(recvData);
				callback(receivedData);
			}
			else if (this->iResult == 0) {
				cout << "Connection closed" << endl;
				break;
			}
			else {
				cerr << "recv failed: " << WSAGetLastError() << endl;
				break;
			}
		}
	});
	thread_recvMsg.detach();
}

在main函数中调用:

结果如下,可以看到接收函数作为子线程去执行,就不会阻塞主线程。

七、发送Json格式数据

将封装Json所用到文件拷贝到项目下(资源地址:https://download.csdn.net/download/ChaoChao66666/89886662)

在main函数中使用json库发送json字符串到TCP服务端:

结果:

源码

main.cpp 

#include <iostream>
#include "swap.h"
#include <vector>
#include <list>
#include <deque>
#include <algorithm>
#include <map>
#include "InfraredAttenuation.h"
#include "TCPClientTest.h"
#include "Json/json.h"
using namespace std;
using namespace Json;

void dataReceivedCallback(const string& data) {
	cout << "Received data: " << data << endl;
}

int main() {
	TCPClient client = TCPClient("127.0.0.1", 8888);
	
	client.recvMsg(dataReceivedCallback);

	//主线程可以继续执行其它任务
	while (true) {
		Value root;
		root["msg"] = "你好";

		FastWriter fw;
		client.sendMsg(fw.write(root).c_str());  //fw.write()可以将json对象转为string类型
		std::this_thread::sleep_for(std::chrono::seconds(3));
	}
}

TCPClientTest.h 

#pragma once
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <vector>
#include <thread>
using namespace std;

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

class TCPClient {
public:
	TCPClient(string ip, int port);
	int createSocket();  //创建套接字
	void setServerAddress();  //设置服务器地址信息
	int connectServer();  //连接到服务器
	void sendMsg(const char* sendbuf);  //发送数据
	void recvMsg(void(*callback)(const std::string&));  //接收数据
	string utf8ToGbk(const std::string& utf8Str);  //解决接收中文乱码
	string localToUtf8(const string& localStr);  //解决发送中文乱码

	int iResult;
	SOCKET clientSocket;
	sockaddr_in serverAddress;
	string IP;
	int Port;
};

 TCPClientTest.cpp

# include "TCPClientTest.h"

TCPClient::TCPClient(string ip,int port):IP(ip),Port(port)
{
	WSADATA wsaData;
	this->iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		cerr << "WSAStartup failed: " << iResult << endl;
	}
	else
	{
		this->createSocket();
	}
}

int TCPClient::createSocket()	// 创建套接字
{
	this->clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (clientSocket == INVALID_SOCKET) {
		cerr << "Error at socket(): " << WSAGetLastError() << endl;
		WSACleanup();
		return 1;
	}
	else
	{
		this->setServerAddress();
	}
}

void TCPClient::setServerAddress()  // 设置服务器地址信息
{
	this->serverAddress;
	serverAddress.sin_family = AF_INET;  //IPv4
	serverAddress.sin_port = htons(Port); // 服务器端口
	inet_pton(AF_INET, IP.c_str(), &serverAddress.sin_addr); // 服务器 IP 地址
	this->connectServer();
}

int TCPClient::connectServer()  // 连接到服务器
{
	this->iResult = connect(clientSocket, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress));
	if (this->iResult == SOCKET_ERROR) {
		std::cerr << "connect failed: " << WSAGetLastError() << std::endl;
		closesocket(clientSocket);
		WSACleanup();
		return 1;
	}
	else
	{
		cout << "Connected to server." << endl;
	}
}

void TCPClient::sendMsg(const char* sendbuf)  // 发送数据
{
	string sendbuf_utf8 = this->localToUtf8(sendbuf);
	this->iResult = send(clientSocket, sendbuf_utf8.c_str(), strlen(sendbuf_utf8.c_str()), 0);
	if (this->iResult == SOCKET_ERROR) {
		cerr << "send failed: " << WSAGetLastError() << endl;
		closesocket(clientSocket);
		WSACleanup();
	}
}

void TCPClient::recvMsg(void(*callback)(const std::string&))  //接收数据
{
	thread thread_recvMsg([this, callback]() {
		char recvbuf[1024];
		while (true)
		{
			this->iResult = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
			if (this->iResult > 0) {
				string recvData = utf8ToGbk(string(recvbuf, iResult));
				string receivedData(recvData);
				callback(receivedData);
			}
			else if (this->iResult == 0) {
				cout << "Connection closed" << endl;
				break;
			}
			else {
				cerr << "recv failed: " << WSAGetLastError() << endl;
				break;
			}
		}
	});
	thread_recvMsg.detach();
}

string TCPClient::utf8ToGbk(const string& utf8Str) {
	int len = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);
	wchar_t* wstr = new wchar_t[len];
	MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wstr, len);

	len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
	char* gbkStr = new char[len];
	WideCharToMultiByte(CP_ACP, 0, wstr, -1, gbkStr, len, nullptr, nullptr);

	string result(gbkStr);
	delete[] wstr;
	delete[] gbkStr;
	return result;
}

string TCPClient::localToUtf8(const string& localStr)
{
	int len = MultiByteToWideChar(CP_ACP, 0, localStr.c_str(), -1, nullptr, 0);
	vector<wchar_t> wstr(len);
	MultiByteToWideChar(CP_ACP, 0, localStr.c_str(), -1, wstr.data(), len);

	len = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr);
	vector<char> utf8Str(len);
	WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, utf8Str.data(), len, nullptr, nullptr);

	return string(utf8Str.data());
}

服务端代码:

【C++】创建TCP服务端-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/896895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.net framework 3.5sp1插件怎么安装

以下是在不同操作系统电脑上安装.NET Framework 3.5 SP1 的几种常见方法&#xff1a; 一、Windows 10 及以上操作系统&#xff1a; 1.在线安装&#xff08;需要网络连接稳定&#xff09;&#xff1a; 按键盘上的 Windows 键&#xff0c;键入 “Windows 功能”&#xff0c;然…

15分钟学Go 第3天:编写第一个Go程序

第3天&#xff1a;编写第一个Go程序 1. 引言 在学习Go语言的过程中&#xff0c;第一个程序通常是“Hello, World!”。这个经典的程序不仅教会你如何编写代码&#xff0c;还引导你理解Go语言的基本语法和结构。本节将详细介绍如何编写、运行并理解第一个Go程序&#xff0c;通过…

建库建表练习

目录 根据以下需求完成图书管理系统数据库及表设计&#xff0c;并建库建表&#xff0c;并截图创建表的详细信息(desc 表名),不用添加数据 1. 用户表: 字段: 姓名&#xff0c;用户名&#xff0c;密码&#xff0c;电话&#xff0c;住址&#xff0c;专业及年级 2. 图书表: 字段: 图…

你知道吗?这个岗位只招2人,但HR那边却收到了1w份简历

引言 在当前经济环境下&#xff0c;求职者面临的挑战越来越大。互联网行业尤其如此&#xff0c;许多人挤破头都想进入大厂&#xff0c;但竞争异常激烈。如今的就业市场确实变得异常艰难。然而&#xff0c;随着AI大模型技术的兴起&#xff0c;对于那些掌握了相关技能的专业人才…

基于vue框架的的地铁站智慧管理系统的设计n09jb(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,上班打卡,下班打卡,人员管理,交接班,视频巡检,车辆巡检,车辆管理 开题报告内容 基于Vue框架的地铁站智慧管理系统的设计开题报告 一、研究背景与意义 随着城市化进程的加速&#xff0c;地铁站作为城市交通系统的重要组成部分&am…

PC端视频编辑解决方案,跨平台SDK,构建多端统一的创作生态

从短视频的兴起&#xff0c;到中长视频内容的蓬勃发展&#xff0c;视频创作领域正经历着一场深刻的变革。在这场变革中&#xff0c;美摄科技以其卓越的PC端视频编辑解决方案&#xff0c;不仅站在了技术创新的前沿&#xff0c;更以开放的姿态&#xff0c;为企业用户搭建起通往未…

Java项目-基于springboot框架的校园疫情防控系统系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

Springboot 使用POI导出Excel文件

Springboot 使用POI导出Excel文件 Excel导出系列目录&#xff1a;引入依赖逻辑处理controllerservice数据查询Excel文件内容处理样式封装 导出效果思考 Excel导出系列目录&#xff1a; 【Springboot 使用EasyExcel导出Excel文件】 【Springboot 使用POI导出Excel文件】 【Spri…

VScode远程开发之remote 远程开发(二)

VScode远程开发之remote 远程开发&#xff08;二&#xff09; 使用vscode进行远程开发很简单&#xff0c;在拓展里搜索 Remote Development&#xff0c;就可以搜索到微软提供的远程开发大礼包&#xff0c;里面包含了 通过 SSH 远程服务器 远程容器 远程 WSL&#xff08;Win…

演示:基于WPF的DrawingVisual开发的高刷新率示波器

一、目的&#xff1a;分享一个基于WPF的DrawingVisual开发的高刷新率示波器 二、效果演示 特此说明&#xff1a;由于Gif录制工具帧率不够&#xff0c;渲染60帧用了4.6秒&#xff0c;平均帧率在12Hz左右&#xff0c;所以展示效果不好&#xff0c;想要看好些的效果可以看文章下面…

安科瑞智慧能源管理系统EMS3.0在浙江某能源集团有限公司的应用

安科瑞戴婷 Acrel-Fanny 一、项目背景 浙江某能源集团有限公司位于浙江省宁波前湾新区&#xff0c;主营业务范围包括了储能技术服务&#xff0c;光伏风力发电技术服务&#xff0c;充电桩技术服务&#xff0c;新能源项目的施工以及为企业提供配电房运维服务。 随着新能源的兴…

[ComfyUI]Flux:爆火禅语小和尚素材!禅意人生,享受自在

在快节奏的现代生活中&#xff0c;人们越来越渴望一种宁静和放松的状态。而禅意小和尚素材正是这样一种能够带给我们内心宁静和智慧的存在。ComfyUI的Flux框架结合了禅意小和尚素材&#xff0c;为我们提供了一个探索禅意人生的独特方式。 禅意小和尚素材源于佛教文化&#xff…

DORA 机器人中间件学习教程(5)——3D激光雷达数据可视化

DORA中3D激光雷达数据可视化 1 总体思路2 DORA数据接收节点3 编写yml文件4 启动节点参考资料 截止目前 DORA还没有类似于ROS2中RVIZ这样的可视化工具&#xff0c;而在调试算法时我们又需要将数据进行可视化以验证代码的正确性。目前的解决方法是将DORA中的数据发送到ROS2中&…

ubuntu查看系统版本命令

查看系统版本指令 在 Ubuntu 操作系统中&#xff0c;您可以使用多个命令来查看系统版本。以下是一些常用的命令&#xff1a; lsb_release -a 这个命令会显示详细的 Ubuntu 版本信息&#xff0c;包括发行版名称、版本号、代号等。lsb_release -acat /etc/os-release 这个命令会显…

实用好助手

在现代职场中&#xff0c;拥有高效且适用的工具能够显著提升我们的工作效率与质量。除了常见的办公软件&#xff0c;还有许多小众但非常实用的工具可以大幅度优化工作流程。以下是另外五个推荐的工作软件&#xff0c;它们各自具备独特的功能与优势&#xff0c;值得一试。 1 …

MongoDB未授权访问

mongodb未授权访问漏洞 复现 MongoDB[//]: # (忙够DB)是一种流行的开源文档数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;基于分布式文件存储的数据库属于NoSQL数据库的一种。 开启MongoDB服务时不添加任何参数时,默认是没有权限验证的 登录的用户可以通过默认端口…

双十一适合买什么东西?2024年双十一收藏榜单必买好物分享!

随着双十一的脚步日益临近&#xff0c;各大电商平台已经开始了激烈的促销竞争。作为每年最盛大的购物节&#xff0c;双十一不仅吸引了无数消费者的积极参与&#xff0c;也成为品牌和产品质量的一次大检阅。那么&#xff0c;在2024年的双十一&#xff0c;哪些商品是值得购买的呢…

智能手表核心芯片~手表心率监测芯片AFE4900浅析(附一篇智能手表专利推荐)

智能手表核心芯片~手表心率监测芯片AFE4900浅析(附一篇智能手表专利推荐) 本期是平台君和您分享的第89期内容 2024年8月,安徽华米信息技术及美国的智能手表品牌ZEPP公司在美国对深圳思佰特公司提起诉讼,涉及专利、商标和不正当竞争。 起诉书(来源:RPX网站) 看到这则新闻…

linux 离线安装redis

1.官网下载 https://redis.io/download 或者去github下载 2.安装 Redis 解压 unzip redis-6.2.16.zip安装gcc #由于 redis 是用 C 语言开发&#xff0c;安装之前必先确认是否安装 gcc 环境&#xff08;gcc -v&#xff09; gcc -v若无安装gcc&#xff0c;参考我的文章 Lin…

三维可视化生产线,是工业4.0最直接的落地应用

工业 4.0 强调智能化、数字化和网络化的生产模式&#xff0c;而三维可视化生产线完美地契合了这些要求。通过三维可视化技术&#xff0c;生产线的各个环节可以以逼真的三维模型形式呈现在人们眼前。 从设计阶段开始&#xff0c;工程师们可以利用三维可视化工具进行生产线的规划…