【问题标题】:Sending huge array through socket通过套接字发送巨大的数组
【发布时间】:2015-05-23 12:10:40
【问题描述】:

我正在尝试通过 C 中的 TCP 套接字从服务器向客户端发送一个 100.000.000 字节(可能更多)的 char 数组。

我就是这样做的:

char *array;  // global array malloc'd (SIZE)

//#######################
//    server code
//#######################
int i;
int SIZE = 100000000

for (i = 0; i < SIZE; i = i + 4){  
    write(id, &array[i], 4);   // write 4 bytes every time
}

//#######################
//    client code
//#######################
int i;
int SIZE = 100.000.000
for (i = 0; i < SIZE; i = i + 4)
    read(id, array + i, 4);    // read 4 bytes

问题:

1) 当我尝试发送更多字节时,传输出现问题。例如,如果我将 4 更改为 100,它会显示“破管”。为什么会这样?

2) 我知道这不是一种“安全”的读/写方式,因为我没有检查 read() 和 write() 返回值。我怎样才能做到这一点?

3) 我必须使用 htonl() 和 ntohl() 函数吗?

【问题讨论】:

  • 通常情况下,这种问题可以通过 2^x 的块大小(大约 0x1000)或根据 tcp/ip 帧(如 1460)来解决。但是您的代码看起来不完整且不一致。为什么你使用不同的语法&amp;array[i]array + i,它们的工作方式是相同的?并且你在接收之前正确地malloc 数组吗?
  • const unsigned long 用于 SIZE 或更好的const size_t(虽然不可行,int 可能只有 16 位,size_t 也是,但无论如何你都会失败,因为 size_t 保证保持最大值。数组的允许索引)。或者,更好的是,使用 #define 来生成适当的常量。如果要更改SIZE,则不应使用大写,因为这通常用于#defines(宏)和枚举常量。请注意,C 中的 constC++ 存在功能差异(如果您更熟悉后者)。

标签: c sockets


【解决方案1】:
 #include<stdlib.h>
 #include<stdio.h>
 #include<string.h>
 #include <sys/types.h>          
 #include <sys/socket.h>   

 //in @param
 //@param fd the socket file descriptor
 //@param array an array of data source to write to send to the connected client 
 //@param SIZE the size of data source to send to the client
 //@param sz_emit the size of data to send in one loop step
 //out @param
 //total length of data emited to the client

 int write_to_client(int fd, char* array, int SIZE, int sz_emit)
 {
   //#######################
   //    server code
   //#######################
   int i=0, sz=0;
   for(i = 0; i < SIZE; i += sz_emit )
   {  
       while(sz_emit-sz)
       { 
         sz+=write(id, array+i+sz, sz_emit-sz);
       }
       sz = 0;
   }
   return i;
 }

//#######################
//    client code
//#######################
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>          
#include <sys/socket.h>

//in @param
//@param fd is the file descriptor of the socket to read from
//@param SIZE the size of datas you want to read from the socket
//@param sz_received the size of byte to read in one loop step
//@param length, the length of data received
//@param read_err if 0 no error if -1 an error occurs use errno from #include <errno.h> to know more about that error 
//out @param
//a pointer to an array of size SIZE containing the data readed
char* receive_from_server(int fd, int SIZE, int sz_received, int* length, int* read_err)
{
  *read_err = 0;
  int i = 0, sz = 0, rt = 0, count=0;
  char *array = (char *)malloc(SIZE);
  memset(array, 0, SIZE);  
  for (i = 0; i < SIZE; i += sz_received)
    {
      while(sz_received-sz)
      { 
        rt = read(id, array + i + sz, sz_received-sz);
        if(rt==-1)
        {
          *read_err=rt;
          printf("an error occurs\n");
          goto l;
        }
        if(!rt)goto l;
        sz+=rt;
        count += sz;   
      }
      sz = 0;
    }
  l: *length = count;
  return array;
}

用法:

//server side
int SIZE = 100000000;
char array_to_send[SIZE]={'r'};
int sz_data_emited = write_to_client(sock, array_to_send, SIZE, 4);
printf("how many byte data emited:%d\n", sz_data_emited);

//client side
int SIZE = 100000000, length = 0, read_err=0;
char*array_received = NULL;
array_received = receive_from_server(sock, SIZE, 4, &length, &read_err);
if(!read_err)printf("get some datas\n");
// free array_received when finished...free(array_received)

一些注意事项:

当您要传输多字节实体时需要注意字节顺序,例如短、整数、长、utf-16 等,但如果您的数据是 utf-8 或 ascii 文本,则不需要它.

【讨论】:

  • 谢谢,它成功了。如果你愿意,可以编辑你的帖子,它有一些小错误;)
  • 您忽略了read() 返回的值,并假设它是正数。您还必须测试另外两种可能性。
  • 推荐的 sz_emit 值是多少?用于发送 png 图像全高清?
【解决方案2】:

我认为 Narcisse Doudieu Siewe 的回答有一些错误。我想it fails when SIZE isn't multiplicity of sz_emit。 例如,我们以 8 个字节的块发送 20 个字节,而最后一个数据块(或数据包)的长度为 4 个字节。如果我们尝试发送最后 8 个字节的块并且只剩下 4 个字节,while 循环将是无限的,因为 while(8 - 4),它永远不会达到 sz = 8,因为下一次发送只会增加 0。所以我这样写修改(未测试,我将很快测试并编写考虑到这个边界条件的第二种方法)。

/**
 * @param sock_fd - the file descriptor of the socket to write (send) data to
 * @param packetLength - the size of data to send in one packet
 * @param data - binary data to send (unsigned char array)
 * @param dataLength - the size of all binary data to send
 * @return  - status code SUCCESS or FAILURE
 */
result_t send_binary(const sock_fd_t sock_fd, const size_t packetLength, const unsigned char *data, const size_t dataLength) {

    ssize_t leftPacketLength = 0;
    ssize_t offset = 0;
    ssize_t sentPacketLength = 0;

    // send each packet of data in the loop
    for(int leftDataLength=dataLength; leftDataLength>0; leftDataLength -= packetLength) {

        leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
        while(leftPacketLength > 0) {
            sentPacketLength = send(sock_fd, data + offset, leftPacketLength, 0);
            if(sentPacketLength < 0) {
                fprintf(stderr, "%s: Error while sending data to the socket.\n", __func__);
                perror(errno);
                return FAILURE;
            }
            offset += sentPacketLength;
            leftPacketLength -= sentPacketLength;
        }
    }

    if(offset != dataLength)
        return FAILURE;

    return SUCCESS;
}

/**
 * @param sock_fd - the file descriptor of the socket to read (recieve) data from
 * @param packetLength - the size of data to recieve in one packet
 * @param data - binary data received (unsigned char array) - previously allocated
 * @param dataLength - the size of all binary data received - previously defined
 * @return - status code SUCCESS or FAILURE
 */
result_t recv_binary(const sock_fd_t sock_fd, const size_t packetLength, unsigned char *data, const size_t dataLength) {

    ssize_t leftPacketLength = 0;
    ssize_t offset = 0;
    ssize_t recvedPacketLength = 0;


    for(int leftDataLength=dataLength; leftDataLength > 0; leftDataLength -= packetLength) {

        leftPacketLength = (leftDataLength > packetLength) ? packetLength : leftDataLength;
        while(leftPacketLength > 0) {
            recvedPacketLength = recv(sock_fd, data + offset, leftPacketLength, 0);
            if(recvedPacketLength < 0) {
                fprintf(stderr, "%s: Error while receiving data from the socket.\n", __func__);
                perror(errno);
                return FAILURE;
            }
            offset += recvedPacketLength;
            leftPacketLength -= recvedPacketLength;
        }
    }

    if(offset != dataLength)
        return FAILURE;

    return SUCCESS;
}

在传输实际的二进制数据之前,还需要通过套接字发送/接收二进制数据的大小。需要知道我们需要读取多少字节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2012-07-12
    相关资源
    最近更新 更多