如何在套接字编程中发送长度大于缓冲区的消息?

时间:2014-12-23 15:53:54

标签: c++ c sockets winsock

我正在使用C ++中的Winsock开发一个应用程序。我有一个200字节长的char数组,用于通过套接字发送字符串。

我的问题是当发送大于char数组的消息时,所以我决定以多个块发送它们,但我不知道该怎么做。

为了发送和接收数据,我使用常规send()recv()函数:

recv(client, buffer, 200, NULL);
send(client, buffer, 200, NULL);

更新

我有一个结构:

struct Pack
{
    unsigned int senderId;
    char str[200];
}

发送之前我将struct转换为char数组。

Pack pk;
strcpy_s(pk.str, 200, "Some text to send.\0");
pk.senderId = 1 // user id
char *buffer = (char*)pk;

如果字符串大小大于200,则strcpy_s()崩溃。

4 个答案:

答案 0 :(得分:9)

您在Beej's Guide to Network Programming中有一个示例:

<强> 7.3。处理部分send()s

  

请记得上面关于send()的部分,当我说的时候   send()可能不会发送你要求它的所有字节?那就是你想要的   它发送512个字节,但它返回412.发生了什么事   剩下100个字节?

     

好吧,他们还在你的小缓冲区等待被送出去。应有   对于你无法控制的情况,内核决定不发送   所有数据都在一个块中,现在,我的朋友,这取决于你   把数据拿出来。

     

您也可以编写这样的函数来执行此操作:

int sendall(int s, char *buf, int *len)
{
    int total = 0;        // how many bytes we've sent
    int bytesleft = *len; // how many we have left to send
    int n;

    while(total < *len) {
        n = send(s, buf+total, bytesleft, 0);
        if (n == -1) { break; }
        total += n;
        bytesleft -= n;
    }

    *len = total; // return number actually sent here

    return n==-1?-1:0; // return -1 onm failure, 0 on success
} 

编辑:正如@a​​lk所指出的那样,用int替换所有ssize_t - send()返回的类型

答案 1 :(得分:5)

如果您正在发送或接收UDP数据包,则无法绕过它;你需要一个足够大的缓冲区来容纳整个数据包。幸运的是,在大多数情况下,您不希望发送或接收大于1500字节的UDP数据包(因为这是以太网卡可以发送的最大数据包,而不会将其分成更小的数据包,通常您希望避免碎片,如果可能的)。

对于TCP,请记住您正在发送/接收字节流,而不是一系列单独的数据包,因此传递给send()和recv()的大小通常不会与大小相对应无论如何,网络数据包。这意味着调用send()6次,每次调用100个字节的数据,与使用600字节数据调用send()1次没有太大区别,依此类推。正如其他人所指出的那样,你必须仔细检查每个send()和recv()调用的返回值,看看实际发送或接收的字节数,因为发送/接收的数量可以(并且有时会小于您请求的字节数。您需要知道实际发送了多少字节才能知道哪些字节适合在下次调用时发送,并且您需要知道实际接收了多少字节才能知道您的接收中现在有多少字节有效 - 缓冲液中。

答案 2 :(得分:4)

您需要在两端进行缓冲。

当您send某事时,您无法确保所有缓冲区都会被发送。 send(2)系统调用可能会返回部分字节数

同样,当你的recv某事时,字节流可能是部分的。 recv(2)系统调用可能会返回部分字节数。

您可能需要一些事件循环(例如,高于poll(2))来混合发送和接收。

TCP / IP不保证给定的send(在发射器机器中)对应于一个recv(在接收机器中):字节流可能已被分成任意部分(例如通过路由器)。另请参阅this

答案 3 :(得分:2)

逐条发送消息。您只能发送与发送缓冲区大小一样大的块,因此您需要记住已发送的数量和剩余的数量;那么你可以使用循环来完成所需的操作:

int bufferSize = 200;
int messageLength = 442; // or whatever
int sendPosition = 0;

while (messageLength) {
    int chunkSize = messageLength > bufferSize ? bufferSize : messageLength;
    memcpy(buffer, message + sendPosition, chunkSize);
    chunkSize = send(client, buffer, chunkSize, NULL);
    messageLength -= chunkSize;
    sendPosition += chunkSize;
}

当然,您只需先从message发送,而不是首先向buffer发送不必要的副本,但我只是在这里说明这个概念。

您的接收代码可能需要在处理之前完整地汇编消息,但这实际上取决于您设计的协议。