套接字编程,检查条目UDP缓冲区是否为空?

发布于 2024-12-09 20:07:52 字数 2259 浏览 1 评论 0原文

我正在编写一个UDP客户端,它向服务器发送一个字符串,当服务器发回多个数据包时,程序的行为并不符合我的预期。我想通过 process() 一个接一个 处理任何传入数据包,直到条目缓冲区变空,但我认为存在与 的阻塞行为相关的问题接收

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <winsock.h>

using namespace std;

void process(const char *in, int size)
{
    fprintf(stdout, "%s\n", in);
}

int main()
{
    char quack_addr[] = "127.0.0.1";
    unsigned short quack_port = 9091;

    WSAData data;
    WSAStartup(MAKEWORD(2, 2), &data);

    sockaddr_in qserver;
    qserver.sin_family = AF_INET;
    qserver.sin_addr.s_addr = inet_addr(quack_addr);
    qserver.sin_port = htons(quack_port);
    SOCKET client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (client <= 0)
    {
        fprintf(stderr, "Error - Can not create socket.\n");
        exit(1);
    }

    while (true)
    {
        const int MAX = 1024;
        char sbuf[MAX];
        char rbuf[MAX];

        fprintf(stdout, ": ");
        fgets(sbuf, MAX, stdin);
        int slen = strlen(sbuf);

        int r = sendto(client,sbuf,slen,0,(sockaddr*)&qserver,sizeof(qserver));

        // Current code:
        // int rlen = recv(client, rbuf, MAX, 0);
        // if (rlen > 0)
        // {
        //     rbuf[rlen] = 0;
        //     process(rbuf, rlen);
        // }

        // Question starts here:
        //
        // While there is data in queue do:
        // {
        //    (break if there is no data)
        //    int rlen = recv(client, rbuf, MAX, 0);
        //    rbuf[rlen] = 0;
        //    process(rbuf, rlen);
        // }   
    }

    return 0;
}

在调用 recv(...) 之前如何检查缓冲区是否为空?

问题发生在这种情况下:

  1. 用户正在客户端程序中键入命令 (cmd1)。
  2. 同时,服务器向客户端发送 3 个数据包(pkt1pkt2pkt3)。
  3. 在客户端按 Enter 后,我期望收到这 3 个数据包,并且可能会收到与 cmd1 相对应的结果,以及 process() 所有他们一一。
  4. 但在第 3 阶段按 Enter 后,我收到 pkt1!在向服务器发送另一个命令后,我将收到 pkt2 等等......!

我知道我的代码不足以处理这个问题,所以,我的问题是如何处理它?<​​/strong>

注意:我正在使用 netcat -L -p 9091 -u< /code> 作为 UDP 服务器

I'm writing a UDP client that sends a string to a server, when the server sends back several packets, the behavior of the program is not as my expectation. I want to process any incoming packet by process() one by one until the entry buffer gets empty, but I think there is a problem related to blocking behavior of recv.

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <winsock.h>

using namespace std;

void process(const char *in, int size)
{
    fprintf(stdout, "%s\n", in);
}

int main()
{
    char quack_addr[] = "127.0.0.1";
    unsigned short quack_port = 9091;

    WSAData data;
    WSAStartup(MAKEWORD(2, 2), &data);

    sockaddr_in qserver;
    qserver.sin_family = AF_INET;
    qserver.sin_addr.s_addr = inet_addr(quack_addr);
    qserver.sin_port = htons(quack_port);
    SOCKET client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (client <= 0)
    {
        fprintf(stderr, "Error - Can not create socket.\n");
        exit(1);
    }

    while (true)
    {
        const int MAX = 1024;
        char sbuf[MAX];
        char rbuf[MAX];

        fprintf(stdout, ": ");
        fgets(sbuf, MAX, stdin);
        int slen = strlen(sbuf);

        int r = sendto(client,sbuf,slen,0,(sockaddr*)&qserver,sizeof(qserver));

        // Current code:
        // int rlen = recv(client, rbuf, MAX, 0);
        // if (rlen > 0)
        // {
        //     rbuf[rlen] = 0;
        //     process(rbuf, rlen);
        // }

        // Question starts here:
        //
        // While there is data in queue do:
        // {
        //    (break if there is no data)
        //    int rlen = recv(client, rbuf, MAX, 0);
        //    rbuf[rlen] = 0;
        //    process(rbuf, rlen);
        // }   
    }

    return 0;
}

How can I check if the buffer is empty or not, before calling recv(...) ?

The problem occurs in this scenario:

  1. User is typing a command in the client program (cmd1).
  2. Simultaneously, the server sends 3 packets to client (pkt1, pkt2, pkt3).
  3. After pressing Enter in the client side, I expect to receive those 3 packets and probably the result corresponding to cmd1, and process() all of them one by one.
  4. But after pressing Enter in stage 3, I receive pkt1! and after sending another command to the server I will receive pkt2 and so on ...!

I know my code is not enough to handle this issue, so, my question is how to handle it?

Note: I'm using netcat -L -p 9091 -u as UDP server

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

农村范ル 2024-12-16 20:07:52

我认为问题(您未描述的令人不满意的行为)来自不同的来源。让我列出一些想法和评论c./之前所说的:

(1)recvfrom()也块。但是,您想使用它。您的通信当前从环回发送和接收,这对于您的玩具程序来说很好(但是:请参见下文)。当使用recv()接收UDP数据时,你不知道是谁发送的,因为套接字从未被connect()ed。使用recvfrom() 为更严重的程序中的一些最小错误检查做好准备

(2),因为 select() 会将程序暂停到 I/O 可用性,它只会将套接字阻塞的任何问题提升到不同的级别。但这不是问题

(3)要检查接收缓冲区是否为空,在recvfrom()中的适当位置使用标志MSG_PEEK。它通常只用于处理稀缺内存,但它应该可以完成这项工作。

(4) 我相信您看到未更详细描述的问题的原因 1:
UDP 数据报保留消息边界。这意味着 recvfrom() 将读取构成发送的任何消息的整个数据块。但是,如果您读入的缓冲区小于读取的数据,则任何多余的内容都将被默默丢弃。因此,请确保您有一个大缓冲区(理想情况下为 65k)。

(5)原因2:
您会收到发送到环回的任何数据。如果您当前也连接到某个网络(卫星、互联网),那么您捕获的内容实际上可能来自与您预期不同的来源。所以至少在休息阶段,断开连接。

阻止应该不是问题。如果代码清晰,您的基本逻辑是:
Recvfrom()(阻塞/等待直到准备好)
过程
查看缓冲区是否为空
如果是则退出
如果没有,则返回以接收更多,

并且您当前似乎想要执行此操作。由于您不使用多线程、优化性能或类似功能,因此您不应该关心阻塞。如果您发现接收缓冲区太小,请使用

Setsockopt() for optName SO_RCVBUF增加其大小

I think the problems (unsatisfying behavior you do not describe) come from a different source. Let me just list some ideas and comments c./ what was said before:

(1) recvfrom() blocks too. However, you want to use it. Your communication currently sends and receives from loopback, which is fine for your toy program (but: see below). When receiving UDP data, with recv() you don't know who sent it, as the socket was never connect()ed. Use recvfrom() to prepare yourself for some minimal error checking in a more serious program

(2) as select() suspends the program to i/o availibity, it would only put any issue with your socket blocking to a different level. But this is not the problem

(3) to check is the receive buffer is empty, use flag MSG_PEEK in recvfrom() in an appropriate position. It's usually only used to deal with scarce memory, but it should do the job.

(4) reason 1 why I believe you see the issues you don't describe in more detail:
UDP datagrams preserve message boundaries. This means that recvfrom() will read in an entire chunk of data making up any message sent. However, if the buffer you read this into is smaller than the data read, any surplus will be silently discarded. So make sure you have a big buffer (65k something ideally).

(5) reason 2:
You receive any data sent to the loopback. If you're currently also connected to some net (sat, the Internet), what you catch might actually be from a different source than you expect. So at least in a resting phase, disconnect.

Blocking shouldn't be an issue. Your basic logic, when coded cleanly, is:
Recvfrom() (block/wait until ready)
Process
Peek if buffer empty
Exit if yes
Loop back to receive more if not,

and you seem to want to do this currently. As you don't multi-thread, optimize fie perfiormance, or similar, you shouldn't care about blocking. If you find your receive buffer too small, increase its size using

Setsockopt() for optName SO_RCVBUF

忘羡 2024-12-16 20:07:52

使用 select()(具有适当的超时)在调用 recv() 之前检查传入数据。

类似以下内容(不可移植代码)

#include <winsock2.h>

...

/* somewhere after your sendto, or your first recv */
fd_set recv_set;
timeval tv = {1, 0}; /* one second */
FD_ZERO(&recv_set);
FD_SET(client, &recv_set);
while (select(0, &recv_set, NULL, NULL, &tv) > 0)
{
    /* recv... */
    FD_SET(client, &recv_set); /* actually redundant, since it is already set */
}

Use select() (with a suitable timeout) to check for incoming data prior to calling recv().

Something like the following (nonportable code)

#include <winsock2.h>

...

/* somewhere after your sendto, or your first recv */
fd_set recv_set;
timeval tv = {1, 0}; /* one second */
FD_ZERO(&recv_set);
FD_SET(client, &recv_set);
while (select(0, &recv_set, NULL, NULL, &tv) > 0)
{
    /* recv... */
    FD_SET(client, &recv_set); /* actually redundant, since it is already set */
}
差↓一点笑了 2024-12-16 20:07:52

iPhone 有时会出现故障,不允许我发表评论。谢谢,史蒂夫。这只是继续谈话。

我认为这意味着“取消对‘问题从这里开始’的注释”。部分答案,因为这仍然取决于我的第二条评论;这或多或少比预期的要少。假设服务器要发送的三个消息已经排队,在您第一次按回车键后,您的数据包将被发送(永远不会被阻止,因为 sendto() 不会阻止 UDP),由服务器接收并且(我假设,见上面,回显并添加到 FIFO 接收缓冲区,其中您已经排队了三个消息,然后您的程序中有一个 recv() 接收第一个排队的消息,并将其打印出来。回到顶部循环,期待另一个输入并等待它(因此这不会在套接字级别上被阻止,而是当您的程序请求输入时,例如简单地“输入”),然后到达第二个最初发送的消息(由服务器)并处理再循环一次,所有三个都完成,并假设服务器回显您发送的内容,您应该开始接收您输入的消息(如果您只按回车,则当前循环可能为空)。除非你杀死它,否则不会退出。

iPhone sometimes bugs out and doesn't let me post comments. Thanks, Steve. This is just continuing the conversation.

I assume this means 'uncomment to 'question starts here'. Partial answer, as this still depends on my 2nd comment; this is more or less than what to expect. Assuming the three messages to be sent by the server are already queued up, after you hit enter for the first time, your packet is sent (never blocked as sendto() doesn't block for UDP), received by the server and (I assume, see above, echoed back and added to the FIFO receive buffer in which you alread have three messages queued up. You then have a recv() in your program which receives the first queued up message, printing it out. Your current logic goes back to top of loop, expect another input and waits for it (so this isn't blocked on a socket level, but as your program requests input, eg simply 'enter'), then comes to the second originally sent message (by the server) and processes that one. One more loop, and all three are done. Hitting enter again, and assuming the server echoes what you sent, you should start receiving your typed in messages (which might be empty if you only hit enter). The loop will currently not exit except you kill it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文