简体   繁体   English

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

[英]How to send messages with larger length than the buffer in socket programming?

I'm developing an application using Winsock in C++. 我正在使用C ++中的Winsock开发一个应用程序。 I have a 200-byte-length char array for sending strings over the socket. 我有一个200字节长的char数组,用于通过套接字发送字符串。

My problem is when sending messages which are larger than the char array, so I decided to send them in multiple chunks but I have no idea how to do it. 我的问题是当发送大于char数组的消息时,所以我决定以多个块发送它们,但我不知道该怎么做。

For sending and receiving the data, I'm using the regular send() and recv() functions: 为了发送和接收数据,我使用常规的send()recv()函数:

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

Update 更新

I have a struct: 我有一个结构:

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

before sending I convert the struct to char array. 在发送之前我将struct转换为char数组。

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

If the string size if larger than 200 the strcpy_s() crashes. 如果字符串大小大于200则strcpy_s()崩溃。

You have an example in Beej's Guide to Network Programming : 你在Beej的网络编程指南中有一个例子:

7.3. 7.3。 Handling Partial send()s 处理部分send()

Remember back in the section about send(), above, when I said that send() might not send all the bytes you asked it to? 请记住上面关于send()的部分,当我说send()可能不会发送你要求它的所有字节时? That is, you want it to send 512 bytes, but it returns 412. What happened to the remaining 100 bytes? 也就是说,你希望它发送512个字节,但它返回412.剩下的100个字节发生了什么?

Well, they're still in your little buffer waiting to be sent out. 好吧,他们还在你的小缓冲区等待被送出去。 Due to circumstances beyond your control, the kernel decided not to send all the data out in one chunk, and now, my friend, it's up to you to get the data out there. 由于你无法控制的情况,内核决定不将所有数据发送到一个块中,而现在,我的朋友,由你来获取数据。

You could write a function like this to do it, too: 您也可以编写这样的函数来执行此操作:

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
} 

EDIT: As pointed out by @alk, replace all those int s with ssize_t - The type returned by send() 编辑:正如@alk指出,更换所有那些int与S ssize_t -通过返回的类型send()

If you're sending or receiving UDP packets, then there is no way around it; 如果您正在发送或接收UDP数据包,那么就无法绕过它; you'll need a buffer big enough to hold the entire packet. 你需要一个足够大的缓冲区来容纳整个数据包。 Fortunately, in most cases you don't want to send or receive UDP packets bigger than around 1500 bytes anyway, (since that is the largest packet an Ethernet card can send without fragmenting it into smaller packets, and generally you want to avoid fragmentation if possible). 幸运的是,在大多数情况下,您不希望发送或接收大于1500字节的UDP数据包(因为这是以太网卡可以发送的最大数据包而不会将其分成更小的数据包,并且通常您希望避免碎片,如果可能)。

For TCP, keep in mind that you are sending/receiving a stream of bytes, rather than a series of individual packets, and that so the sizes you pass to send() and recv() will not generally correspond to the sizes of the network packets anyway. 对于TCP,请记住您正在发送/接收字节流,而不是一系列单独的数据包,因此传递给send()和recv()的大小通常不会对应于网络的大小然后包。 That means that calling send() 6 times, each with 100 bytes of data, isn't much different from calling send() 1 time with 600 bytes of data, and so on. 这意味着调用send()6次,每次调用100个字节的数据,与使用600字节数据调用send()1次没有太大区别,依此类推。 As others have noted, you do have to carefully check the return value of each send() and recv() call to see how many bytes were actually sent or received, since the number sent/received can (and will) sometimes be smaller than the number of bytes you requested. 正如其他人所指出的那样,你必须仔细检查每个send()和recv()调用的返回值,以查看实际发送或接收的字节数,因为发送/接收的数量有时可能(并且会)小于您请求的字节数。 You need to know how many bytes were actually sent in order to know which bytes are appropriate to send on the next call, and you need to know how many bytes were actually received in order to know how many bytes are now valid in your receive-buffer. 您需要知道实际发送了多少字节才能知道哪些字节适合在下次调用时发送,并且您需要知道实际接收了多少字节才能知道您的接收中现在有多少字节有效 - 缓冲。

You need buffering on both ends. 你需要在两端缓冲。

When you send something, you are not assured that all your buffer would be sent. 当您send ,您无法确保将发送所有缓冲区。 The send(2) syscall may return a partial byte count . send(2)系统调用可能返回部分字节计数

Likewise, when your recv something, the byte stream may be partial. 同样,当你的recv东西,字节流可能是部分的。 The recv(2) syscall may return a partial byte count. recv(2)系统调用可能返回部分字节计数。

You probably need some event loop (eg above poll(2) ) to mix both sending and receiving. 您可能需要一些事件循环(例如,在poll(2)之上)来混合发送和接收。

TCP/IP does not guarantee that a given send (in the emitter machine) correspond to one recv (in the receiving machine): the byte stream might have been chunked into arbitrary pieces in between (eg by routers). TCP / IP不保证给定的send (在发射器机器中)对应于一个recv (在接收机器中):字节流可能已被分块成中间的任意部分(例如,通过路由器)。 See also this . 另请参见

Send the message piece by piece. 逐条发送消息。 You can only send pieces as large as the send buffer size, so you need to remember how much you have sent and how much remains; 您只能发送与发送缓冲区大小一样大的块,因此您需要记住已发送的数量和剩余的数量; then you can use a loop to do what is required: 然后你可以使用循环来做所需的事情:

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;
}

Of course you could simply send from message instead of making a needless copy to buffer first, but I am just illustrating the concept here. 当然,您可以简单地从message而不是首先将不必要的副本发送到buffer ,但我只是在这里说明这个概念。

Your receiving code would then possibly need to assemble the message in its entirety before being able to process it, but that really depends on the protocol you have designed. 然后,您的接收代码可能需要在处理之前完整地汇编消息,但这实际上取决于您设计的协议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM