【问题标题】:Sending buffer length and buffer over socket in c在c中通过套接字发送缓冲区长度和缓冲区
【发布时间】:2010-11-09 23:44:17
【问题描述】:

我正在尝试使用 C 将字符串发送到服务器应用程序,但遇到了障碍。我对网络编程还很陌生,我认为我的代码是错误的树。

消息应该是消息长度 + 消息,并且在另一端由 python 服务器解包(buf 是原始传入数据):

msg_len_bytes = buf[0:4]
msg_len = struct.unpack("!L", msg_len_bytes)[0]

!表示网络字节顺序,L 表示无符号长。

发送常规字符串相当简单。 发送(袜子,消息,strlen(消息),0);

但是添加消息长度我无法完全掌握。到目前为止,这是我的客户的代码:

struct msgstruct {
        uint32_t length;
        char send_data[4096];
};

int main()

{
    int sock;
    struct msgstruct message;
    char data[4096] = "<MOP><test/></MOP>";

    for ( int i = 0; i < strlen(data); i++ ) {
      message.send_data[i] = data[1];
    }

    struct hostent *host;
    struct sockaddr_in server_addr;

    unsigned long buflen = sizeof(message.send_data);
    uint32_t bufsend = htonl(buflen);

    message.length = bufsend;

    host = gethostbyname("127.0.0.1");

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        exit(1);
    }

    server_addr.sin_family = AF_INET;     
    server_addr.sin_port = htons(12998);   
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&(server_addr.sin_zero),8); 


    if (connect(sock, (struct sockaddr *)&server_addr,
                sizeof(struct sockaddr)) == -1) {
        perror("Connect");
        exit(1);
    }

    if(send(sock, message.length + message.send_data, sizeof(message), 0) == -1){
        printf("\nSocket error.");
        exit(1);
    }
    return 0;
}

我尝试了一些变体,但总是以套接字错误告终。是因为我在发送的第二个争论中混合了类型吗?如果我尝试发送结构,我会收到编译错误。

【问题讨论】:

  • 如果您对所提供的答案感到满意,请选择一个(我的!希望如此)作为带有复选标记的“正确答案”。值得 15 代表。指向我,并使这个问题在以后的参考中更有用。

标签: c sockets


【解决方案1】:

错误出现在这段代码中:

send(sock, message.length + message.send_data, sizeof(message), 0)

发送的原型是:

ssize_t send(int s, const void *buf, size_t len, int flags);

注意参数2是一个指针。 在您的代码中,您已将其作为长度(类型 uint32_t)添加到缓冲区(类型 char*)。 这种加法将产生一个 char*, (pointer-to-char) 但一个指向不可预测且无意义的内存区域的指针。

要获得指向缓冲区的指针,您需要:

send(sock, &message, sizeof(message), 0)

请注意,由于填充问题,获取结构的地址并不可移植或始终不可取。但在典型的 32 位架构上,这应该没问题。

这将从消息结构开始发送数据,但发送 4100 (4096+4) 字节! 我不认为你打算发送那么多。第三个参数表示要发送多少字节,应该设置为:

sizeof(uint32_t) + strlen(data);  // 4-byte Integer + Length of the data "<MOP><test/></MOP>"

请注意,这包含数据的 Null-Terminator,但您的初始 for 循环也没有复制 Null-Terminator

(如果你想要空终止符,让你的初始 for 循环转到 strlen(data)+1,并使用 strlen(data)+1其他地方也是如此)。

理想情况下,您应该将 strlen(data) 缓存到一个局部变量,而不是过多地调用它。 (您还在初始 for 循环中反复调用 strlen)。

您的最终陈述将如下所示:

if(send(sock, &message, sizeof(uint32_t)+strlen(data), 0) == -1){
    printf("\nSocket error.");
    exit(1);
}

试试看,然后告诉我结果如何。

【讨论】:

  • 嗯,当我这样做时,似乎会向服务器发送大量垃圾(并驱动我的 PC 扬声器失控)。
  • unwind 也说了。你有一个 data[1] 你应该有 data[i]!
  • 你说得很对,谢谢!这是一个相当尴尬的错误。现在服务器打印几个字节的垃圾(我希望的消息长度),消息本身(之前是 MMMMMMMMMMM),然后是几 k 看似随机的位(我不是空终止吗?或者我应该设置 char 缓冲区等于字符串的长度?)。
  • 谢谢abelenky!这似乎发送了正确的信息。由于某种原因,除非我发送至少 1024 字节的数据,否则它不会将消息视为已接收,但我只是假设这是服务器代码中的某个问题。
  • @directedition:似乎是 Nagle 的算法阻止您发送小数据包——尝试禁用它(例如我的答案)。
【解决方案2】:

将数据复制到message 实例的循环已损坏,它有一个1,它应该有一个i

send() 的第二个参数应该是指向要发送的第一个字节的指针。相反,你给它一个大端数的总和(如果你的平台不是大端,那将是非常错误的)和一个随机数组基地址。这是错误的。

你需要:

if(send(sock, &message, sizeof message.length + strlen(data), 0) == -1) {

还有一些其他的困惑,你似乎“想要”总是发送完整的 4K,而不仅仅是必要的数量。

【讨论】:

    【解决方案3】:

    您可以使用 2 个后续发送:

    send(sock, &message.length, sizeof(message.length), 0);
    send(sock, message.send_data, message.length*sizeof(char), 0);
    

    或者更好地使用前 4 个字节作为消息长度来准备缓冲区:

    char buff[MAX_BUFF] = "";
    int  len_disp = sizeof(message.length);
    memcpy(buff, &message.length, len_disp);
    memcpy(&buff[len_disp], &message.length, message.length*sizeof(char));
    send(sock, buff, message.length*sizeof(char) + len_disp, 0);
    

    编辑: 对于小消息评论 - 禁用 Nagle 算法。

    BOOL bNagleEnabled = FALSE;
    if(setsockopt(sAccept, IPPROTO_TCP, TCP_NODELAY, (char *)&bNagleEnabled, sizeof(BOOL)))
    {
      ReportError("Setting TCP_NODELAY socket option failed");
      return -2;
    }
    

    【讨论】:

    • +1 对长度和数据使用单独的发送。如果编译器想在长度和缓冲区之间插入数据,这一点尤其重要。
    • 真的没有图书馆可以为你做这件事吗?如果没有,我正在制作一个,除了可变长度数量作为大小标题...
    【解决方案4】:

    基本上所有东西都应该通过套接字变成缓冲区并发送/接收。 所以通常我做什么:

     //Send
     memcpy(buffer, &structure_u_defined, sizeof(structure_u_defined));
     send(&sock, buffer,...);// lol I am using a public pc
    
     //receive
     recv(&sock, buffer,...);
     memcpy(&structure_u_defined_for_receive, buffer, sizeof(buffer));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-08
      • 1970-01-01
      • 1970-01-01
      • 2014-05-10
      • 2013-02-16
      • 1970-01-01
      • 2014-08-19
      • 2023-03-31
      相关资源
      最近更新 更多