我正在尝试使用 c 中的 select 方法进行 UDP 传输。我看过一些指南 (Beej),但我无法理解这种方法。我想做的是从客户端向服务器发送一个 UDP 数据包,并使用 select 方法来确定该数据包是否已被丢弃。所以我需要做的是,每当我从客户端向服务器发送数据包时,都使用 select 作为计时器。我有以下代码:
SOCKET s;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000; //used for time interval
fd_set writefds;
FD_ZERO(&writefds);
//FD_SET(s, &writefds); without this I get timed out
select(s, NULL, &writefds, NULL, &tv); //timer
sendto(s, szbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)); //send content
if (FD_ISSET(s, &writefds))
cout << "sent" << endl;
else
cout << "timed out" << endl;
这每次都会给出超时结果。这是否告诉我数据包正在超时,或者这个结果是否与我的数据包传输完全无关?也许我还需要包括其他标志?感谢反馈,因为我对这种方法有点困惑。
PS:szbuffer、BUFFER_SIZE、sa_in等之前已经定义好了。这只是我将要发送的缓冲区,以及我要发送到的大小和地址 (sa_in)。不用select方法就成功发送到服务器了。
最佳答案
您的代码存在一些问题。
您按原样将套接字值传递给
select()
的第一个参数,但如果您实际上需要传递s+1
正在监视单个套接字。该参数必须设置为正在监视的最高套接字值+1。在 Windows 上,该参数被完全忽略,但在其他平台上实际使用它,因此您必须在非 Windows 平台上指定一个适当的值,其中套接字只是文件描述符数组的索引。该参数代表该数组中select()
无法访问的最高索引。您注释掉了告诉
select()
要监视哪个套接字的代码行。你需要把那条线放回去。您正在滥用
select()
来尝试执行操作。您在发送数据包之前查询套接字是否可写(在其出站缓冲区中有空间),然后假设如果send()
成功则数据包没有被丢弃。send()
仅告诉您数据包是否已放入套接字的出站缓冲区,而不是数据包是否实际传输。
如果 UDP 数据包被丢弃,套接字 API 中没有任何警告。检测丢失数据包的唯一方法是设计协议(protocol),使接收方必须发回对收到的每个数据包的确认。然后你可以做更多类似的事情:
SOCKET s;
...
if (sendto(s, szbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)) > 0)
{
cout << "sent" << endl;
struct timeval tv;
// .5s is too short a time to wait for a reply, bump it up a bit higher
//tv.tv_sec = 0;
//tv.tv_usec = 500000;
tv.tv_sec = 5;
tv.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(s, &readfds);
int ret = select(s+1, &readfds, NULL, NULL, &tv);
if (ret > 0)
{
// socket has pending data to read
if (recvfrom(s, rcvbuffer, BUFFER_SIZE, 0, (struct sockaddr*)&sa_in, sizeof(sa_in)) >= 0)
{
// todo: verify the packet is an acknowledgement
// of the packet sent above and not something else...
cout << "ack received" << endl;
}
else
{
cout << "error reading" << endl;
}
}
else if (ret == 0)
{
cout << "timed out waiting for ack" << endl;
// todo: resend the same packet again, or abort the transfer
}
else
{
cout << "error selecting" << endl;
}
}
else
cout << "error sending" << endl;
看Trivial FTP (TFTP)有关使用 acks 传输数据的协议(protocol)的示例。
关于c++ - 在 C++ 中使用 UDP 的简单选择实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26821047/