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

[转帖]已经被废弃的 tcp_tw_recycle

最近准备自己动手部署测试kubernetes集群,注备写一个 hands on 的手册。突发奇想将 centos 原有的内核从3.10更新到了4.14版本,并执行一些常规的优化操作。没有想到在修改了 sysctl.conf 里面的一些参数,希望能对新的 kubernetes 性能有所帮助。 当我在其

.NET周报 【5月第2期 2023-05-14】

国内文章 XUnit数据共享与并行测试 https://www.cnblogs.com/podolski/p/17388602.html 在单元或者集成测试的过程中,需要测试的用例非常多,如果测试是一条一条过,那么需要花费不少的时间。从 V2 开始,默认情况下 XUnit 自动配置并行(参考资料),

Spring Boot 3.1中如何整合Spring Security和Keycloak

在今年2月14日的时候,Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器。其中包括Spring Security和Spring Boot的适配器,这意味着今后Keycloak团队将不再提供针对Spring Security和Spring Boot的集成方案。但是,如此强大的Ke