编写将客户端发送到服务器的UDP客户端。在下面的代码中,当服务器发回几个数据包时,程序的行为并不像我期望的那样。我希望逐个process()
处理任何传入的数据包,直到条目缓冲区变空。我认为问题与阻止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;
}
如何在调用recv(...)
之前检查缓冲区是否为空?
在这种情况下会出现问题:
cmd1
)中键入命令。pkt1
,pkt2
,pkt3
)。Enter
后,我希望收到这3个数据包,可能结果对应cmd1
和process()
所有这些数据包一个接一个。Enter
后,我收到了pkt1
!在向服务器发送另一个命令后,我将收到pkt2
,依此类推......!我知道我的代码不足以处理这个问题,所以,我的问题是如何处理它?</ strong>
注意:我使用netcat -L -p 9091 -u
作为UDP服务器
答案 0 :(得分:2)
在调用recv()
之前,使用select()
(有合适的超时时间)检查传入数据。
类似以下内容(非便携式代码)
#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 */
}
答案 1 :(得分:2)
我认为问题(你没有描述的不满意的行为)来自不同的来源。让我列出一些想法和评论c。/之前的说法:
(1)recvfrom()也会阻止。但是,您想要使用它。您的通信目前通过环回发送和接收,这对您的玩具程序来说很好(但是:见下文)。使用recv()接收UDP数据时,您不知道是谁发送的,因为套接字从未连接过()ed。使用recvfrom()为更严肃的程序中的一些最小错误检查做好准备
(2)当select()将程序暂停到i / o availibity时,它只会将套接字阻塞的任何问题都放到不同的级别。但这不是问题
(3)检查接收缓冲区是否为空,在recvfrom()中使用标志MSG_PEEK在适当的位置。它通常只用于处理稀缺的内存,但它应该可以完成这项工作。
(4)原因1为什么我相信你会看到你没有详细描述的问题: UDP数据报保留消息边界。这意味着recvfrom()将读取构成发送的任何消息的整个数据块。但是,如果您读入的缓冲区小于读取的数据,则会以静默方式丢弃任何剩余。所以请确保你有一个很大的缓冲区(理想情况下是65k)。
(5)原因2: 您收到发送到环回的任何数据。如果您当前也连接到某个网络(坐在互联网上),那么您捕获的内容可能实际上来自与您预期不同的来源。所以至少在休息阶段,断开连接。
阻止不应成为问题。干净编码时,您的基本逻辑是: Recvfrom()(阻止/等到准备好) 处理 如果缓冲区为空则查看 如果是的话退出 如果没有,则循环返回以接收更多,
你似乎想要这样做。由于你没有多线程,优化fie perfiormance或类似的,你不应该关心阻塞。如果您发现接收缓冲区太小,请使用
增加其大小optname SO_RCVBUF
的setsockopt()答案 2 :(得分:1)
iPhone有时会出错并且不允许我发表评论。谢谢,史蒂夫。这只是继续谈话。
我认为这意味着'取消'问题从这里开始'。部分答案,因为这仍然取决于我的第二个评论;这或多或少是预期的。假设服务器发送的三条消息已经排队,在您第一次按Enter后,您的数据包将被发送(从不阻止,因为sendto()不会阻止UDP),服务器接收(I假设,如上所述,回显并添加到FIFO接收缓冲区,您在其中读取了三个排队的消息。然后您的程序中有一个recv()接收第一个排队的消息,打印出来。您当前的逻辑是回到循环的顶部,期待另一个输入并等待它(所以这不会在套接字级别上被阻止,但是当你的程序请求输入时,例如简单地“输入”),然后转到第二个最初发送的消息(由服务器)并处理一个。再循环一次,并且所有三个都完成了。再次进入,并假设服务器回显你发送的内容,你应该开始接收输入的消息(如果你只输入enter,它可能是空的)。除了你杀了它之外,循环当前不会退出。