【C++】创建TCP客户端

news/2024/10/23 2:23:20/

目录

一、实现发送字符串功能

二、实现接收字符串功能

三、客户端接收乱码问题

四、客户端发送乱码问题

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

六、子线程接收数据

七、发送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;  //IPv4serverAddress.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;  //IPv4serverAddress.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博客


http://www.ppmy.cn/news/1541215.html

相关文章

使用pyqt创建一个移动的矩形

使用pyqt创建一个移动的矩形 程序功能概述效果详细代码 程序功能概述 程序的主要功能是在一个窗口内绘制一个矩形框&#xff0c;并使这个矩形框能够以固定的速度向右移动。当矩形框移动出窗口右侧边界时&#xff0c;它会重新出现在窗口的左侧。 效果 详细代码 import sys fr…

【C++】— 一篇文章让你认识STL

文章目录 &#x1f335;1.什么是STL&#xff1f;&#x1f335;2.STL的版本&#x1f335;3.STL的六大组件&#x1f335;4.STL的重要性&#x1f335;5. 如何学习STL&#x1f335;6. 学习STL的三种境界 &#x1f335;1.什么是STL&#xff1f; STL是Standard Template Library的简称…

Leetcode - 周赛419

目录 一&#xff0c;3318. 计算子数组的 x-sum I 二&#xff0c;3319. 第 K 大的完美二叉子树的大小 三&#xff0c;3320. 统计能获胜的出招序列数 四&#xff0c;3321. 计算子数组的 x-sum II 一&#xff0c;3318. 计算子数组的 x-sum I 本题数据范围较小&#xff0c;可以…

iOS18 TabbarController 切换动画

升级 iOS18后&#xff0c; TabbarController 切换会带有一个缩放动画&#xff0c;下面是自定义取消动画的代码 #import "TabBarVC.h"interface TabBarVC () <UIViewControllerAnimatedTransitioning, UITabBarControllerDelegate>endimplementation TabBarVC-…

代码训练营 day42|LeetCode 518,LeetCode 377,LeetCode 70

前言 这里记录一下陈菜菜的刷题记录&#xff0c;主要应对25秋招、春招 个人背景 211CS本CUHK计算机相关硕&#xff0c;一年车企软件开发经验 代码能力&#xff1a;有待提高 常用语言&#xff1a;C 系列文章目录 第42天 &#xff1a;第九章 动态规划 part05 文章目录 前言系…

软考-软件设计师(10)-专业英语词汇汇总与新技术知识点

场景 以下为高频考点、知识点汇总。 软件设计师上午选择题知识点、高频考点、口诀记忆技巧、经典题型汇总: 软考-软件设计师(1)-计算机基础知识点:进制转换、数据编码、内存编址、串并联可靠性、海明校验码、吞吐率、多媒体等: 软考-软件设计师(1)-计算机基础知识点:进制…

优选算法第一讲:双指针模块

优选算法第一讲&#xff1a;双指针模块 1.移动零2.复写零3.快乐数4.盛最多水的容器5.有效三角形的个数6.查找总价格为目标值的两个商品7.三数之和8.四数之和 1.移动零 链接: 移动零 下面是一个画图&#xff0c;其中&#xff0c;绿色部分标出的是重点&#xff1a; 代码实现&am…

PHP中的ReflectionClass常见用法

ReflectionClass是 PHP 中的一个类&#xff0c;它提供了有关类的信息的反射。 使用ReflectionClass可以在运行时获取关于类的各种信息&#xff0c;例如类的名称、方法、属性、注释等。 以下是一些常见的用法&#xff1a; 获取类的名称&#xff1a; $reflection new Reflec…