c++ - 在 C++ 中使用 UDP 的简单选择实现

标签 c++ sockets select networking

我正在尝试使用 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方法就成功发送到服务器了。

最佳答案

您的代码存在一些问题。

  1. 您按原样将套接字值传递给 select() 的第一个参数,但如果您实际上需要传递 s+1正在监视单个套接字。该参数必须设置为正在监视的最高套接字值+1。在 Windows 上,该参数被完全忽略,但在其他平台上实际使用它,因此您必须在非 Windows 平台上指定一个适当的值,其中套接字只是文件描述符数组的索引。该参数代表该数组中 select() 无法访问的最高索引。

  2. 您注释掉了告诉 select() 要监视哪个套接字的代码行。你需要把那条线放回去。

  3. 您正在滥用 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/

相关文章:

c++ - Lamba 依赖模板依赖值作为该模板构造函数的默认值在 MSVC 上失败

c++ - 如何正确使用malloc和free memory?

sockets - Bluez 核心干扰 HCI 套接字命令

java - 如何知道我们是否收到了带有 DatagramPacket 的内容?

javascript - 如何在第一个组合框更改后获取第二个组合框值(jquery)

sql - 0 在升序排序时排在最后

c++ - 使用 gtest 进行 Arduino 单元测试

Java:服务器/客户端 -> 客户端/客户端

mysql - 如果在另一个表上匹配,则排除一个表中的行 - MySql

c++ - 设计线程安全类时避免嵌套调用时的死锁