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:
- User is typing a command in the client program (
cmd1). - Simultaneously, the server sends 3 packets to client (
pkt1,pkt2,pkt3). - After pressing
Enterin the client side, I expect to receive those 3 packets and probably the result corresponding tocmd1, andprocess()all of them one by one. - But after pressing
Enterin stage 3, I receivepkt1! and after sending another command to the server I will receivepkt2and 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
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