14.3 Socket 字符串分块传输

socket,字符串,分块,传输 · 浏览次数 : 5

小编点评

**分块传输字符串的目的是** - 将字符串切割成多个小数据块,以减少数据传输的成本和提高效率。 - 降低网络通信 overhead,因为数据块大小通常比原始字符串小,从而减少了多个数据包的处理。 **分块传输的步骤:** 1. **获取字符串长度:**读取客户端发送的字符串长度,将其转换为整数类型。 2. **创建数据块:**使用字符串长度创建数据块,每个数据块大小为 8192 字节。 3. **发送数据块:**循环遍历数据块,将每个数据块发送到服务端。 4. **接收数据块:**服务端接收数据块,并将其连接在一起,形成完整的数据包。 5. **处理数据包:**将接收到的数据包处理,并将其显示给用户。 **客户端实现的代码:** ```c++ #include int main() { // 初始化 Winsock WSADATA WSAData; SOCKET sock, msgsock; struct sockaddr_in ClientAddr; // 创建套接字 if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR) { ClientAddr.sin_family = AF_INET; ClientAddr.sin_port = htons(9999); ClientAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sock = socket(AF_INET, SOCK_STREAM, 0); bind(sock, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr)); listen(sock, 10); } // 接收数据包数量 char recv_count[1024] = { 0 }; recv(msgsock, recv_count, 1024, 0); // 发送数据包次数 std::cout << "发包次数: " << count << std::endl; // 发送数据包 for (int x = 0; x < count; x++) { std::cout << "发送数据包: " << Split[x] << std::endl; send(sock, Split[x], strlen(Split[x]), 0); } // 关闭套接字 closesocket(sock); WSACleanup(); return 0; } ``` **运行结果:** ``` 发包次数: 8 发送数据包: hello lyshark hello lyshark hello lyshark ```

正文

首先为什么要实行分块传输字符串,一般而言Socket套接字最长发送的字节数为8192字节,如果发送的字节超出了此范围则后续部分会被自动截断,此时将字符串进行分块传输将显得格外重要,分块传输的关键在于封装实现一个字符串切割函数,将特定缓冲区内的字串动态切割成一个个小的子块,当切割结束后会得到该数据块的个数,此时通过套接字将个数发送至服务端此时服务端在依次循环接收数据包直到接收完所有数据包之后在组合并显示即可。

14.3.1 服务端实现

对于服务端而言只需要做两步,首先等待客户端上线,当客户端上线后则服务端会通过recv()接收当前客户端内有多少数据包,当获取到该数据包数量后再以此数量作为循环条件,循环接收数据包并在接收后通过strcat将数据包进行连接,最终合并为一个缓冲区,并输出即可;

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>

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

int main(int argc, char* argv[])
{
    WSADATA WSAData;
    SOCKET sock, msgsock;
    struct sockaddr_in ServerAddr;

    if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
    {
        ServerAddr.sin_family = AF_INET;
        ServerAddr.sin_port = htons(9999);
        ServerAddr.sin_addr.s_addr = INADDR_ANY;

        sock = socket(AF_INET, SOCK_STREAM, 0);
        bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
        listen(sock, 10);
    }

    msgsock = accept(sock, (LPSOCKADDR)0, (int*)0);

    // 接收需要获取的次数
    char recv_count[1024] = { 0 };
    recv(msgsock, recv_count, 1024, 0);
    std::cout << "收包次数: " << recv_count << std::endl;
                
    // 循环收包,并将数据包放入到此变量中
    char szBuffer[8192] = { 0 };

    for (int x = 0; x < atoi(recv_count); x++)
    {
        char Split[128] = { 0 };
        recv(msgsock, Split, 100, 0);

        // std::cout << "收到数据包: " << Split << std::endl;
        strcat(szBuffer, Split);
    }

    std::cout << "完整数据包: " << szBuffer << std::endl;
    closesocket(msgsock);
    closesocket(sock);
    WSACleanup();
    return 0;
}

14.3.2 客户端实现

我们将一段长字符串进行切割每次发送100个字符,该功能的实现首先依赖于Cat函数,该函数通过传入待切割字符串指针,切割偏移以及长度,即可实现分割字符串,如下代码中通过调用Cat(szBuffer, x, 99)切割字符串,这里我们每次剪切100个字符,并将剪切后的字符依次存储到Split[y]这个缓冲区内,接着通过调用strlen()依次计算出当前有多少个100字符行,并在计算结束后首先向服务端发送数据包的数量,当服务端接收到数量后会进入等待模式,此时客户端只需要通过循环的方式发送数据包即可,当然此段代码也存在问题,没有对数据包进行严格的验证,此处读者可自行完善;

#include <WinSock2.h>
#include <Windows.h>
#include <iostream>

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

char* Cut(char* buffer, int offset, int length)
{
    char Split[100] = { 0 };

    // 每100个字符切割一次
    memset(Split, 0, 100);
    strncpy(Split, buffer + offset, length);
    return Split;
}

int main(int argc, char* argv[])
{
    WSADATA WSAData;
    SOCKET sock;
    struct sockaddr_in ClientAddr;

    if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
    {
        ClientAddr.sin_family = AF_INET;
        ClientAddr.sin_port = htons(9999);
        ClientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

        sock = socket(AF_INET, SOCK_STREAM, 0);
        int Ret = connect(sock, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr));
        if (Ret == 0)
        {
            char szBuffer[8192] = "hello lyshark hello lyshark hello lyshark ";
            char Split[100][1024] = { 0 };

            // 每次剪切100个字符
            for (int x = 0, y = 0; x < strlen(szBuffer); x += 99)
            {
                char* ref = Cut(szBuffer, x, 99);
                strcpy(Split[y], ref);
                y += 1;
            }

            // 计算当前有多少个100字符行
            int rows = sizeof(Split) / sizeof(Split[0]);
            int count = 0;
            for (int x = 0; x < rows; x++)
            {
                if (strlen(Split[x]) != 0)
                {
                    count += 1;
                }
            }

            // 将发送数据包次数发送给服务端
            std::cout << "发包次数: " << count << std::endl;
            char send_count[1024] = { 0 };
            sprintf(send_count, "%d", count);
            send(sock, send_count, strlen(send_count), 0);

            // 循环发送数据包
            for (int x = 0; x < count; x++)
            {
                std::cout << "发送数据包: " << Split[x] << std::endl;
                send(sock, Split[x], strlen(Split[x]), 0);
            }
        }
    }
    closesocket(sock);
    WSACleanup();
    Sleep(5000);
    return 0;
}

运行上述程序片段,读者可看到如下图所示的输出效果,此处只需要分割8个数据包即可完成数据的发送;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/1bd4f28f.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与14.3 Socket 字符串分块传输相似的内容:

14.3 Socket 字符串分块传输

首先为什么要实行分块传输字符串,一般而言`Socket`套接字最长发送的字节数为`8192`字节,如果发送的字节超出了此范围则后续部分会被自动截断,此时将字符串进行分块传输将显得格外重要,分块传输的关键在于封装实现一个字符串切割函数,将特定缓冲区内的字串动态切割成一个个小的子块,当切割结束后会得到该数据块的个数,此时通过套接字将个数发送至服务端此时服务端在依次循环接收数据包直到接收完所有数据包之后

Python 潮流周刊#48:Python 3.14 的发布计划

本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景:帮助所有读者精进 Python 技术,并增长职业和副业的收入。 本期分享了 12 篇文章,11 个开源项目,赠书 5 本《图解TCP/IP(第6版

[转帖]理解平均负载

https://www.jianshu.com/p/beb0b9f590d4 uptime命令 [test@localhost bin]$ uptime 22:02:14 up 3:34, 2 users, load average: 0.00, 0.01, 0.05 #22:02:14 //当前时

[转帖]Batch mode

https://help.eclipse.org/latest/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fbatch.html&cp%3D54_3_14 Memory Analyzer can be operated in batch

NET 8 预览版 2 亮点是Blazor

.NET 团队在2023年3月14日发布了.NET 8预览版2,博客文章地址:https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-2/, 亮点是了对Blazor的一些改进。Blazor 方面获得了一个高性能网格组件Qui

[转帖]手把手教你在QEMU上运行RISC-V Linux

https://kernel.0voice.com/forum.php?mod=viewthread&tid=3080 嵌入式Linux内核 发布于 2023-3-15 14:44:37 阅读 46只看楼主 今天教大家在QEMU模拟器上运行一个RISC-V Linux 一个RISC-V Linux包

时间片 线程切换 指令周期 流水线 TPS的初步了解

时间片 线程切换 指令周期 流水线 TPS的初步了解 情况说明 Redis 单线程提供服务, 可以支撑十万级别的TPS 通过以个非常简单的测试 redis-benchmark -c 50 -n 50000 ping Intel 8369HB 3.3Ghz 14万TPS 阿里 倚天710 2.7Ghz

1.3 Metasploit 生成SSL加密载荷

在本节中,我们将介绍如何通过使用`Metasploit`生成加密载荷,以隐藏网络特征。前一章节我们已经通过`Metasploit`生成了一段明文的ShellCode,但明文的网络传输存在安全隐患,因此本节将介绍如何通过生成SSL证书来加密ShellCode,使得网络特征得到隐藏,从而提高后门的生存能力和抵抗网络特征分析的能力。ShellCode 网络特征加密我们采用的是SSL(Secure Soc

3年Java阿里跳字节的面试心得总结

中厂->阿里->字节,成都->杭州->成都 系列文章目录和关于我 0.前言 笔者在不足两年经验的时候从成都一家金融科技中厂跳槽到杭州阿里淘天集团,又于今年5月份从杭州淘天跳槽到成都字节。自认为自己在面试这方面有一点心得,处于记录和分享的目的便有了此文,此文纯主观,也许对3年社招的同学有所帮助。 本文

记一次简单的存储过程和Pivot行转列

首先我很讨厌写存储过程,其次我很讨厌 没办法,主要是需要进行 行转列,项目经理说可以用Pivot。我不是很精通sql,但是我会百度呀~ pivot需要有确定的列名。那我这个项目里面没办法确定,最后问了gpt,使用动态sql(我以前也没用过),不过效果是我想要的,于是乎,改成存储过程吧。 简单的存储过