14.7 Socket 循环结构体传输

简介: 在上述内容中笔者通过一个简单的案例给大家介绍了在套接字编程中如何传递结构体数据,本章将继续延申结构体传输,在某些时候例如我们需要传输一些当前系统的进程列表信息,或者是当前主机中的目录文件,此时就需要使用循环结构体传输功能,循环传输结构体的关键点在于,客户端发送结构体数据之前需要通过一次通信来告诉服务端需要接收的次数,当服务端接收到次数时则可利用接收计数器依次循环接收数据直到客户端完整所有数据包的发送。

在上述内容中笔者通过一个简单的案例给大家介绍了在套接字编程中如何传递结构体数据,本章将继续延申结构体传输,在某些时候例如我们需要传输一些当前系统的进程列表信息,或者是当前主机中的目录文件,此时就需要使用循环结构体传输功能,循环传输结构体的关键点在于,客户端发送结构体数据之前需要通过一次通信来告诉服务端需要接收的次数,当服务端接收到次数时则可利用接收计数器依次循环接收数据直到客户端完整所有数据包的发送。

14.7.1 服务端实现

多条结构体的传输方式与单条从原理上一致,只是多条结构体在传输时需要提前告知服务端我需要分几次将结构体传输给对方,因为数据包最大单次可发送8192字节,所以如果结构过多则需要分批次进行传输,如下是服务端实现代码片段,在代码中首先我们接收客户端发来的循环次数,该次数是一个字符串类型的,为了能用于循环体内,需要通过atoi(count)将其转换为一个整数,接着就是在循环体内不断地调用recv函数接收数据包,直到循环结束为止。

#include <iostream>
#include <winsock2.h>

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

typedef struct
{
   
   
  char HostName[32];
  char Buffer[32];
}message;

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

  WSAStartup(MAKEWORD(2, 0), &WSAData);
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock == INVALID_SOCKET)
  {
   
   
    std::cout << "创建套接字失败" << std::endl;
  }

  struct sockaddr_in ServerAddr;
  ServerAddr.sin_family = AF_INET;
  ServerAddr.sin_port = htons(9999);
  ServerAddr.sin_addr.s_addr = INADDR_ANY;

  auto res = bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
  if (res == SOCKET_ERROR)
  {
   
   
    std::cout << "绑定失败" << std::endl;
  }

  res = listen(sock, 10);
  if (res == SOCKET_ERROR)
  {
   
   
    std::cout << "侦听失败" << std::endl;
  }

  SOCKET msgsock;

  msgsock = accept(sock, (LPSOCKADDR)0, (int*)0);
  if (msgsock != INVALID_SOCKET)
  {
   
   
    // 接收循环次数
    char count[32] = {
   
    0 };
    int recv_count_flag = recv(msgsock, count, sizeof(count), 0);
    if (recv_count_flag != 0)
    {
   
   
      // 得到需要循环接收的次数
      int index = atoi(count);
      std::cout << "总共循环接收: " << count << " 次" << std::endl;

      for (int x = 0; x < index; x++)
      {
   
   
        char recv_buf[4096] = {
   
    0 };

        // 循环输出接收结果
        int recv_flag = recv(msgsock, recv_buf, sizeof(recv_buf), 0);
        if (recv_flag != 0)
        {
   
   
          // 接收到结构,强制类型转换
          message* msg = (message*)recv_buf;

          std::cout << "用户名: " << msg->HostName << "数据: " << msg->Buffer << std::endl;

          // 发送成功标志
          send(msgsock, "success", 7, 0);
        }
      }
    }
  }

  closesocket(sock);
  WSACleanup();
  return 0;
}

14.7.2 客户端实现

相对于服务端而言,客户端首先需要准备好一个待发送结构体链表,此处通过使用vector<message>的方式接收结构体链表,并通过sprintf()函数将循环次数由整数格式化为字符串,并将次数发送给服务端,当服务端接收到发送次数后会等待客户端向其发送对应数量的结构体,此时客户端只需要send循环发送即可。

#include <iostream>
#include <vector>
#include <winsock2.h>

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

using namespace std;

typedef struct
{
   
   
  char HostName[32];
  char Buffer[32];
}message;

message msg;

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

  WSAStartup(MAKEWORD(2, 0), &WSAData);
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock == INVALID_SOCKET)
  {
   
   
    std::cout << "创建套接字失败" << std::endl;
  }

  struct sockaddr_in ClientAddr;
  ClientAddr.sin_family = AF_INET;
  ClientAddr.sin_port = htons(9999);
  ClientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

  auto res = connect(sock, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr));

  if (res == SOCKET_ERROR)
  {
   
   
    std::cout << "链接失败." << std::endl;
  }

  // 模拟元素填充
  std::vector<message> vect;

  for (int x = 0; x < 10; x++)
  {
   
   
    message ptr;
    // 填充参数
    sprintf(ptr.HostName, "lyshark %d", x);
    sprintf(ptr.Buffer, "hello lyshark %d", x);
    vect.push_back(ptr);
  }

  // 发送循环次数
  char count[32] = {
   
    0 };

  // 整数转为字符串
  sprintf(count, "%d", vect.size());

  int send_count_flag = send(sock, count, sizeof(count), 0);

  if (send_count_flag != 0)
  {
   
   
    std::cout << "发送循环次数: " << count << std::endl;

    // 循环发送数据
    for (int x = 0; x < vect.size(); x++)
    {
   
   
      char send_buf[4096] = {
   
    0 };

      // 发送字节序
      memcpy(send_buf, &vect[x], sizeof(message));
      int send_flag = send(sock, send_buf, sizeof(send_buf), 0);
      if (send_flag != 0)
      {
   
   
        char recv_buf[32] = {
   
    0 };
        recv(sock, recv_buf, sizeof(recv_buf), 0);
        std::cout << "发送完成,接收状态码: " << recv_buf << std::endl;
      }
    }
  }

  closesocket(sock);
  WSACleanup();
  return 0;
}

至此读者可分别编译并运行服务端与客户端,此时会看到如下图所示的结构体输出;

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

目录
相关文章
|
5月前
|
网络协议 安全 Python
socket客户端和服务端,文件的传输
socket 实现,客户端和服务端,文件的传输
43 1
|
7月前
14.6 Socket 应用结构体传输
当在套接字编程中传输结构体时,可以将结构体序列化为字符串(即把结构体的所有成员打包成一个字符串),然后将字符串通过套接字传输到对端,接收方可以将字符串解析为结构体,然后使用其中的成员数据。这种方法通常被称为序列化(Serialization)和反序列化(Deserialization),本章中我们可以采用将一个结构体序列化为一个纯字符串,然后将该字符串通过套接字传输给对端,当对端收到后只需要将字节序强制转换为对等的结构体指针即可实现对该结构的解析。
26 0
14.6 Socket 应用结构体传输
|
7月前
|
存储
14.3 Socket 字符串分块传输
首先为什么要实行分块传输字符串,一般而言`Socket`套接字最长发送的字节数为`8192`字节,如果发送的字节超出了此范围则后续部分会被自动截断,此时将字符串进行分块传输将显得格外重要,分块传输的关键在于封装实现一个字符串切割函数,将特定缓冲区内的字串动态切割成一个个小的子块,当切割结束后会得到该数据块的个数,此时通过套接字将个数发送至服务端此时服务端在依次循环接收数据包直到接收完所有数据包之后在组合并显示即可。
32 0
14.3 Socket 字符串分块传输
|
Python
(5)ESP32+Python使用Socket传输播放Bad Apple
ESP32+Python使用Socket传输播放Bad Apple
355 0
Socket传输结构体数据注意事项
【1 背景】 在Socket通信中,要传输结构化的数据或者要进行协议数据传输的时候,发送端必须要构造结构体进行数据传输。 接收端也必须通过同样的结构体进行解析。 但Socket传输结构体数据时候,稍有不慎就会出现:1)解析数据出错;2)接收数据不完整;3)解析为乱码等的Bug。
251 0
Socket传输结构体数据注意事项
|
数据管理 数据处理
Socket传输结构体数据注意事项
在Socket通信中,要传输结构化的数据或者要进行协议数据传输的时候,发送端必须要构造结构体进行数据传输。 接收端也必须通过同样的结构体进行解析。但Socket传输结构体数据时候,稍有不慎就会出现:1)解析数据出错;2)接收数据不完整;3)解析为乱码等的Bug。
973 0
|
XML JSON 网络协议
消息(2)——联机下的数据传输,socket传输字串及复杂类型
数据传输是个很大的概念。我不是从理论的角度去记录随笔,只是把互联传输中的概念简化到消息的位面。以便于对分布传输中的消息进行一下剖析,加深印象。   对处于脱机状态下的终端来说,数据的传输可以通过中间介质进行中介传输。
793 0
|
19天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。