14.3 Socket 字符串分块传输

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

首先为什么要实行分块传输字符串,一般而言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 许可协议。转载请注明出处!

相关文章
|
19天前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
90 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
5月前
|
网络协议
逆向学习网络篇:通过Socket建立连接并传输数据
逆向学习网络篇:通过Socket建立连接并传输数据
55 0
|
5月前
|
监控 网络协议 Java
Java Socket编程 - 基于TCP方式的二进制文件传输
Java Socket编程 - 基于TCP方式的二进制文件传输
42 0
|
6月前
|
网络协议 Java 网络安全
【计算机网络】—— Socket通信编程与传输协议分析
【计算机网络】—— Socket通信编程与传输协议分析
|
6月前
|
网络协议 安全 Python
socket客户端和服务端,文件的传输
socket 实现,客户端和服务端,文件的传输
89 1
14.7 Socket 循环结构体传输
在上述内容中笔者通过一个简单的案例给大家介绍了在套接字编程中如何传递结构体数据,本章将继续延申结构体传输,在某些时候例如我们需要传输一些当前系统的进程列表信息,或者是当前主机中的目录文件,此时就需要使用循环结构体传输功能,循环传输结构体的关键点在于,客户端发送结构体数据之前需要通过一次通信来告诉服务端需要接收的次数,当服务端接收到次数时则可利用接收计数器依次循环接收数据直到客户端完整所有数据包的发送。
43 0
14.7 Socket 循环结构体传输
14.6 Socket 应用结构体传输
当在套接字编程中传输结构体时,可以将结构体序列化为字符串(即把结构体的所有成员打包成一个字符串),然后将字符串通过套接字传输到对端,接收方可以将字符串解析为结构体,然后使用其中的成员数据。这种方法通常被称为序列化(Serialization)和反序列化(Deserialization),本章中我们可以采用将一个结构体序列化为一个纯字符串,然后将该字符串通过套接字传输给对端,当对端收到后只需要将字节序强制转换为对等的结构体指针即可实现对该结构的解析。
63 0
14.6 Socket 应用结构体传输
Socket传输结构体数据注意事项
【1 背景】 在Socket通信中,要传输结构化的数据或者要进行协议数据传输的时候,发送端必须要构造结构体进行数据传输。 接收端也必须通过同样的结构体进行解析。 但Socket传输结构体数据时候,稍有不慎就会出现:1)解析数据出错;2)接收数据不完整;3)解析为乱码等的Bug。
278 0
Socket传输结构体数据注意事项
|
Python
(5)ESP32+Python使用Socket传输播放Bad Apple
ESP32+Python使用Socket传输播放Bad Apple
386 0
|
数据管理 数据处理
Socket传输结构体数据注意事项
在Socket通信中,要传输结构化的数据或者要进行协议数据传输的时候,发送端必须要构造结构体进行数据传输。 接收端也必须通过同样的结构体进行解析。但Socket传输结构体数据时候,稍有不慎就会出现:1)解析数据出错;2)接收数据不完整;3)解析为乱码等的Bug。
1037 0