From 54310e4b29b2150bc99d15682d43e13a42453b00 Mon Sep 17 00:00:00 2001 From: taynpg Date: Fri, 8 Mar 2024 15:27:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=B8=AATCP?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E5=92=8C=E4=B8=80=E4=B8=AACurl=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E6=96=87=E4=BB=B6=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpp/algorithm/类似Base64算法.cpp | 86 +++++++++++ cpp/demo/net/CMakeLists.txt | 6 + cpp/demo/net/main.cpp | 9 ++ cpp/demo/net/multi_thread_down.cpp | 93 +++++++++++ cpp/demo/net/multi_thread_down.h | 38 +++++ cpp/demo/tcpfilesend/.vscode/settings.json | 31 ++++ cpp/demo/tcpfilesend/CMakeLists.txt | 11 ++ cpp/demo/tcpfilesend/main.cpp | 89 +++++++++++ cpp/demo/tcpfilesend/server.cpp | 170 +++++++++++++++++++++ cpp/demo/tcpfilesend/server2.cpp | 125 +++++++++++++++ 10 files changed, 658 insertions(+) create mode 100644 cpp/algorithm/类似Base64算法.cpp create mode 100644 cpp/demo/net/CMakeLists.txt create mode 100644 cpp/demo/net/main.cpp create mode 100644 cpp/demo/net/multi_thread_down.cpp create mode 100644 cpp/demo/net/multi_thread_down.h create mode 100644 cpp/demo/tcpfilesend/.vscode/settings.json create mode 100644 cpp/demo/tcpfilesend/CMakeLists.txt create mode 100644 cpp/demo/tcpfilesend/main.cpp create mode 100644 cpp/demo/tcpfilesend/server.cpp create mode 100644 cpp/demo/tcpfilesend/server2.cpp diff --git a/cpp/algorithm/类似Base64算法.cpp b/cpp/algorithm/类似Base64算法.cpp new file mode 100644 index 0000000..cbb171f --- /dev/null +++ b/cpp/algorithm/类似Base64算法.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +// 问题:将一个二进制流转换成可阅读的字符串,字符串为:大写字母加数字,去除0和O,I和1相近字符。 + +const int g_bitNum = 5; +const char* g_Coder = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; + +int Encode(const char* pData, int nLen, char* res) +{ + int totalLen = nLen * 8; + int resLen = totalLen / g_bitNum; + if (totalLen % g_bitNum != 0) ++resLen; + if (res == nullptr) { + return resLen; + } + for (int i = 0; i < resLen; ++i) { + unsigned char nIndex = 0; + int index = i * g_bitNum; + for (int j = 0; j < g_bitNum && index + j < totalLen; ++j) { + + // bitValue 这一行只管取出下一位的值,放在哪里不管。 + // index 是连续的(相当于大组里面内增,换个大组继续内增) + // index / 8 或者 index % 8 就是锁定第几组char的第几个,移动到最低位取出。 + unsigned char bitValue = ((pData[index / 8] >> (7 - index % 8)) & 1); + + // value 这一行只管把值放到对应的位置,与上面不要联系。 + // 这里对 value 高位到低位的放 + nIndex |= (bitValue << (g_bitNum - 1 - j)); + + // 关键点:value 和 bitValue 各管各的。 + ++index; + } + res[i] = g_Coder[nIndex]; + } + res[resLen] = '\0'; + return resLen; +} + +int Decode(const char* pData, unsigned char* res) +{ + int len = static_cast(std::strlen(pData)); + int codeBitCount = len * g_bitNum; + int resCnt = codeBitCount / 8; + if (res == nullptr) { + return resCnt; + } + std::memset(res, 0, resCnt * sizeof(char)); + for (int i = 0; i < len; ++i) { + const char key = pData[i]; + int nIndex = static_cast(std::strchr(g_Coder, key) - g_Coder); + for (int j = 0; j < g_bitNum && i * g_bitNum + j < codeBitCount; ++j) { + // bitValue 这一行,只管取出每一位 + int bitValue = ((nIndex >> (g_bitNum - 1 - j)) & 1); + // nOffset 是原始数据位索引 + int nOffset = i * g_bitNum + j; + res[nOffset / 8] |= (bitValue << (7 - nOffset % 8)); + } + } + return resCnt; +} + +int main() +{ + int data[] = {12345, 45678}; + std::cout << "Original Data Len: " << sizeof(data) << std::endl; + char* szEncode = nullptr; + int size = Encode((const char*)data, sizeof(data), szEncode); + std::printf("Encode Data Len: %d\n", size); + szEncode = new char[size + 1]; + Encode((const char*)data, sizeof(data), szEncode); + std::printf("Encode Data: %s\n", szEncode); + + unsigned char * szDecode = nullptr; + int osize = Decode((const char *)szEncode, szDecode); + std::printf("Decode Data Len: %d\n", osize); + szDecode = new unsigned char [osize + 1]; + Decode((const char*)szEncode, szDecode); + + int* pInt = (int *)szDecode; + std::printf("Int[0]:%d, Int[1]:%d\n", pInt[0], pInt[1]); + delete[] szDecode; + delete[] szEncode; + return 0; +} \ No newline at end of file diff --git a/cpp/demo/net/CMakeLists.txt b/cpp/demo/net/CMakeLists.txt new file mode 100644 index 0000000..c976eea --- /dev/null +++ b/cpp/demo/net/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required (VERSION 3.8) + +project (cppNetTool) +set(CMAKE_CXX_STANDARD 11) +add_executable(cppNetTool main.cpp multi_thread_down.cpp) +target_link_libraries(cppNetTool curl) \ No newline at end of file diff --git a/cpp/demo/net/main.cpp b/cpp/demo/net/main.cpp new file mode 100644 index 0000000..57ff520 --- /dev/null +++ b/cpp/demo/net/main.cpp @@ -0,0 +1,9 @@ +#include +#include "multi_thread_down.h" + +int main(int argc, const char* argv[]) +{ + CThreadDownload downloader; + downloader.download(argv[1], argv[2]); + return 0; +} \ No newline at end of file diff --git a/cpp/demo/net/multi_thread_down.cpp b/cpp/demo/net/multi_thread_down.cpp new file mode 100644 index 0000000..0e7ae70 --- /dev/null +++ b/cpp/demo/net/multi_thread_down.cpp @@ -0,0 +1,93 @@ +#include "multi_thread_down.h" + +void CTimer::start() +{ + m_start = std::chrono::high_resolution_clock::now(); +} + +size_t CTimer::getTime() +{ + m_end = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds duration = std::chrono::duration_cast(m_end - m_start); + size_t cnt = duration.count(); + return cnt; +} + +CThreadDownload::CThreadDownload() += default; + +CThreadDownload::~CThreadDownload() += default; + +double CThreadDownload::getFileLength(const char* url) +{ + double len = 0; + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_HEADER, 1); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1); + if (curl_easy_perform(curl) == CURLE_OK) { + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &len); + } + else { + len = -1; + } + curl_easy_cleanup(curl); + return len; +} + +int CThreadDownload::download(const char* url, const char* filename) { + + double fileLen = getFileLength(url); + if (fileLen < 0) { + std::printf("Failed to obtain the file size!"); + return -1; + } + + std::ofstream stream(filename, std::ios::binary | std::ios::trunc); + if (!stream.is_open()) { + std::printf("Open File Failed: %s", filename); + return -1; + } + m_fstream = std::move(stream); + + + std::printf("File Len: %lf\n", fileLen); + + // try + // { + // m_fstream.seekp(fileLen - 1); + // } + // catch(const std::exception& e) + // { + // std::cerr << e.what() << '\n'; + // m_fstream.close(); + // return -1; + // } + + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunCall); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + + // 建立TCP连接,发送http请求,等待数据返回 + CURLcode res = curl_easy_perform(curl); + + if (res != CURLE_OK) { + std::printf("res: %d\n", res); + } + + curl_easy_cleanup(curl); + m_fstream.close(); + std::cout << "Done." << std::endl; + return 0; +} + +size_t CThreadDownload::writeFunCall(void* ptr, size_t size, size_t mmb, void* userdata) +{ + size_t nSize = size * mmb; + CThreadDownload* pDown = (CThreadDownload*)userdata; + pDown->m_fstream.write((const char*)ptr, nSize); + std::printf("writeFunCall: %ld\n", nSize); + return nSize; +} \ No newline at end of file diff --git a/cpp/demo/net/multi_thread_down.h b/cpp/demo/net/multi_thread_down.h new file mode 100644 index 0000000..ef2522b --- /dev/null +++ b/cpp/demo/net/multi_thread_down.h @@ -0,0 +1,38 @@ +#ifndef MULTI_DOWNLOAD_H +#define MULTI_DOWNLOAD_H + +#include +#include +#include +#include + +class CTimer +{ + using time_point_t = std::chrono::high_resolution_clock::time_point; +public: + CTimer() = default; + ~CTimer() = default; +public: + void start(); + // 秒种 + size_t getTime(); +private: + time_point_t m_start; + time_point_t m_end; +}; + +class CThreadDownload +{ +public: + CThreadDownload(); + ~CThreadDownload(); +public: + double getFileLength(const char* url); + int download(const char* url, const char* filename); + static size_t writeFunCall(void* ptr, size_t size, size_t mmb, void* userdata); + +private: + std::ofstream m_fstream; +}; + +#endif \ No newline at end of file diff --git a/cpp/demo/tcpfilesend/.vscode/settings.json b/cpp/demo/tcpfilesend/.vscode/settings.json new file mode 100644 index 0000000..40794b0 --- /dev/null +++ b/cpp/demo/tcpfilesend/.vscode/settings.json @@ -0,0 +1,31 @@ +{ + "files.autoSave": "onFocusChange", + "editor.fontSize": 14, + "editor.fontFamily": "'Cascadia Code', 'Cascadia Code', 'Cascadia Code'", + "cmake.configureOnOpen": true, + "cmake.debugConfig": { + //"externalConsole": true, + "setupCommands": [ + { + "description": "-gdb-set charset utf-8", + "text": "-gdb-set charset UTF-8" + }, + { + "description": "Enable gdb pretty-printing", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + "editor.inlayHints.enabled": "off", + "C_Cpp.intelliSenseEngine": "disabled", + "clangd.arguments": [ + "--clang-tidy", + "-j=4", + "--pch-storage=memory", + "--compile-commands-dir=build", + "--background-index", + "--ranking-model=heuristics", + "--query-driver=E:/program/mingw64/bin/g++.exe" + ] +} \ No newline at end of file diff --git a/cpp/demo/tcpfilesend/CMakeLists.txt b/cpp/demo/tcpfilesend/CMakeLists.txt new file mode 100644 index 0000000..9d9a1ba --- /dev/null +++ b/cpp/demo/tcpfilesend/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required (VERSION 3.8) + +project (tcpfilesend) +set(CMAKE_CXX_STANDARD 11) + +add_executable(tcpfilesend main.cpp) +add_executable(tcpfileserver server.cpp) +add_executable(tcpfileserver2 server2.cpp) +target_link_libraries(tcpfilesend ws2_32) +target_link_libraries(tcpfileserver ws2_32) +target_link_libraries(tcpfileserver2 ws2_32) \ No newline at end of file diff --git a/cpp/demo/tcpfilesend/main.cpp b/cpp/demo/tcpfilesend/main.cpp new file mode 100644 index 0000000..a6f7f94 --- /dev/null +++ b/cpp/demo/tcpfilesend/main.cpp @@ -0,0 +1,89 @@ +#include +#include + +#include +#include +#include +#include +#include + +constexpr int BUFFER_SIZE = 4096; + +int main(int argc, char** argv) +{ + if (argc == 1) { + std::cout << "Need Param, Example: send 127.0.0.1 9696 E:\\2.bmp\n"; + return 0; + } + + // 初始化Winsock + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "Failed to initialize Winsock." << std::endl; + return 1; + } + + // 创建套接字 + SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (clientSocket == INVALID_SOCKET) { + std::cerr << "Failed to create socket." << std::endl; + WSACleanup(); + return 1; + } + + // 设置服务器地址 + sockaddr_in serverAddress{}; + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(std::stoi(std::string(argv[2]))); + if (inet_pton(AF_INET, argv[1], &(serverAddress.sin_addr)) <= 0) { + std::cerr << "Invalid server address." << std::endl; + closesocket(clientSocket); + WSACleanup(); + return 1; + } + + // 连接到服务器 + if (connect(clientSocket, reinterpret_cast(&serverAddress), + sizeof(serverAddress)) == SOCKET_ERROR) { + std::cerr << "Failed to connect to server." << std::endl; + closesocket(clientSocket); + WSACleanup(); + return 1; + } + + // 打开要发送的文件 + FILE* file = std::fopen(argv[3], "rb"); + if (!file) { + std::cerr << "Failed to open file : " << argv[3] << std::endl; + closesocket(clientSocket); + WSACleanup(); + return 1; + } + + int Second = 3; + std::cout << Second << " Second after to start send .....\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(Second * 1000)); + // 读取文件内容并发送到服务器 + size_t allLen = 0; + char buffer[BUFFER_SIZE]; + while (!std::feof(file)) { + int nSize = std::fread(buffer, sizeof(char), BUFFER_SIZE, file); + if (nSize > 0) { + allLen += nSize; + + if (send(clientSocket, buffer, nSize, 0) == SOCKET_ERROR) { + std::cerr << "Failed to send data to server." << std::endl; + break; + } + std::cout << "already send Size = " << allLen << "\n"; + } + } + + // 关闭套接字和文件 + std::fclose(file); + closesocket(clientSocket); + WSACleanup(); + + std::cout << "Send Done, Size = " << allLen << std::endl; + return 0; +} diff --git a/cpp/demo/tcpfilesend/server.cpp b/cpp/demo/tcpfilesend/server.cpp new file mode 100644 index 0000000..571ac52 --- /dev/null +++ b/cpp/demo/tcpfilesend/server.cpp @@ -0,0 +1,170 @@ +#include +#include + +#include +#include +#include +#include + + +#define BUFFER_SIZE 1024 + +int main(int argc, char** argv) +{ + if (argc == 1) { + std::cout << "Need Param, Example: server 127.0.0.1 9696 E:\\2.bmp\n"; + return 0; + } + + // 初始化 Winsock + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "Failed to initialize Winsock." << std::endl; + return 1; + } + + // 创建套接字 + SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == INVALID_SOCKET) { + std::cerr << "Failed to create socket: " << WSAGetLastError() + << std::endl; + WSACleanup(); + return 1; + } + + // 绑定地址和端口 + sockaddr_in serverAddr{}; + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(std::stoi(std::string(argv[2]))); + serverAddr.sin_addr.s_addr = inet_addr(argv[1]); + ; + if (bind(serverSocket, reinterpret_cast(&serverAddr), + sizeof(serverAddr)) == SOCKET_ERROR) { + std::cerr << "Failed to bind socket: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + // 监听连接请求 + if (listen(serverSocket, 1) == SOCKET_ERROR) { + std::cerr << "Failed to listen on socket: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + std::cout << "server ip: " << argv[1] << " port: " << argv[2] << std::endl; + std::cout << "Server started. Waiting for client connection..." + << std::endl; + + // 接受客户端连接 + SOCKET clientSocket = accept(serverSocket, nullptr, nullptr); + if (clientSocket == INVALID_SOCKET) { + std::cerr << "Failed to accept client connection: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + std::cout << "Client connected. Receiving data..." << std::endl; + + // 创建接收缓冲区 + char buffer[BUFFER_SIZE]; + size_t allLen = 0; + // 打开文件以保存接收的数据 + FILE* file = std::fopen(argv[3], "wb"); + if (!file) { + std::cerr << "Failed to open file for writing." << std::endl; + closesocket(clientSocket); + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + // 循环接收数据 + while (true) { + // 创建事件对象 + WSAEVENT event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + std::cerr << "Failed to create event: " << WSAGetLastError() + << std::endl; + break; + } + + // 将事件对象与套接字关联 + if (WSAEventSelect(clientSocket, event, FD_READ) == SOCKET_ERROR) { + std::cerr << "Failed to associate event with socket: " + << WSAGetLastError() << std::endl; + WSACloseEvent(event); + break; + } + + // 等待事件对象变为 signaled 状态 + DWORD waitResult = + WSAWaitForMultipleEvents(1, &event, FALSE, WSA_INFINITE, FALSE); + if (waitResult == WSA_WAIT_FAILED) { + std::cerr << "Failed to wait for event: " << WSAGetLastError() + << std::endl; + WSACloseEvent(event); + break; + } + + // 检查事件对象状态 + if (waitResult == WSA_WAIT_EVENT_0) { + WSANETWORKEVENTS networkEvents; + if (WSAEnumNetworkEvents(clientSocket, event, &networkEvents) == + SOCKET_ERROR) { + std::cerr << "Failed to enumerate network events: " + << WSAGetLastError() << std::endl; + WSACloseEvent(event); + break; + } + + // 检查是否有数据可读 + if (networkEvents.lNetworkEvents & FD_READ) { + if (networkEvents.iErrorCode[FD_READ_BIT] != 0) { + std::cerr << "Error in FD_READ: " + << networkEvents.iErrorCode[FD_READ_BIT] + << std::endl; + WSACloseEvent(event); + break; + } + + // 接收数据 + int bytesRead = recv(clientSocket, buffer, BUFFER_SIZE, 0); + if (bytesRead > 0) { + // 将数据写入文件 + std::fwrite(buffer, sizeof(char), bytesRead, file); + allLen += bytesRead; + std::cout << "already recv: " << allLen << "\n"; + } else if (bytesRead == 0) { + // 客户端关闭连接 + std::cout << "Client disconnected." << std::endl; + WSACloseEvent(event); + break; + } else { + // 发生错误 + std::cerr << "Failed to receive data: " << WSAGetLastError() + << std::endl; + WSACloseEvent(event); + break; + } + } + } + + WSACloseEvent(event); + } + + // 关闭套接字和清理资源 + closesocket(clientSocket); + closesocket(serverSocket); + WSACleanup(); + std::fclose(file); + + std::cout << "Done .... file: " << argv[3] << std::endl; + return 0; +} diff --git a/cpp/demo/tcpfilesend/server2.cpp b/cpp/demo/tcpfilesend/server2.cpp new file mode 100644 index 0000000..7f0b0b6 --- /dev/null +++ b/cpp/demo/tcpfilesend/server2.cpp @@ -0,0 +1,125 @@ +#include +#include + +#include +#include +#include +#include + + +#define BUFFER_SIZE 1024 + +int main(int argc, char** argv) +{ + if (argc == 1) { + std::cout << "Need Param, Example: server 127.0.0.1 9696 E:\\2.bmp\n"; + return 0; + } + + // 初始化 Winsock + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "Failed to initialize Winsock." << std::endl; + return 1; + } + + // 创建套接字 + SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket == INVALID_SOCKET) { + std::cerr << "Failed to create socket: " << WSAGetLastError() + << std::endl; + WSACleanup(); + return 1; + } + + // 绑定地址和端口 + sockaddr_in serverAddr{}; + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(std::stoi(std::string(argv[2]))); + serverAddr.sin_addr.s_addr = inet_addr(argv[1]); + ; + if (bind(serverSocket, reinterpret_cast(&serverAddr), + sizeof(serverAddr)) == SOCKET_ERROR) { + std::cerr << "Failed to bind socket: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + // 监听连接请求 + if (listen(serverSocket, 1) == SOCKET_ERROR) { + std::cerr << "Failed to listen on socket: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + std::cout << "server ip: " << argv[1] << " port: " << argv[2] << std::endl; + std::cout << "Server started. Waiting for client connection..." + << std::endl; + + // 接受客户端连接 + SOCKET clientSocket = accept(serverSocket, nullptr, nullptr); + if (clientSocket == INVALID_SOCKET) { + std::cerr << "Failed to accept client connection: " << WSAGetLastError() + << std::endl; + closesocket(serverSocket); + WSACleanup(); + return 1; + } + + std::cout << "Client connected. Receiving data..." << std::endl; + + // 创建接收缓冲区 + char buffer[BUFFER_SIZE]; + size_t allLen = 0; + // 打开文件以保存接收的数据 + FILE* file = std::fopen(argv[3], "wb"); + if (!file) { + std::cerr << "Failed to open file for writing." << std::endl; + closesocket(clientSocket); + closesocket(serverSocket); + WSACleanup(); + return 1; + } + // 循环接收数据 + while (true) { + WSABUF dataBuffer{}; + dataBuffer.len = BUFFER_SIZE; + dataBuffer.buf = buffer; + + DWORD bytesReceived = 0; + DWORD flags = 0; + + // 接收数据 + if (WSARecv(clientSocket, &dataBuffer, 1, &bytesReceived, &flags, + nullptr, nullptr) == SOCKET_ERROR) { + std::cerr << "Failed to receive data: " << WSAGetLastError() + << std::endl; + break; + } + + // 检查接收到的数据大小 + if (bytesReceived > 0) { + // 将数据写入文件 + // 将数据写入文件 + std::fwrite(buffer, sizeof(char), bytesReceived, file); + allLen += bytesReceived; + std::cout << "already recv: " << allLen << "\n"; + } else if (bytesReceived == 0) { + // 客户端关闭连接 + std::cout << "Client disconnected." << std::endl; + break; + } + } + + // 关闭套接字和清理资源 + closesocket(clientSocket); + closesocket(serverSocket); + WSACleanup(); + std::fclose(file); + + return 0; +}