在c++中使用UDP进行简单的选择实现

Simple select implementation with UDP in c++

本文关键字:简单 选择 实现 UDP c++      更新时间:2023-10-16

我正试图使用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)。我在没有选择方法的情况下成功地发送到了服务器。

您的代码存在一些问题。

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

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

  3. 您在尝试使用select()。在发送数据包之前,您正在查询套接字是否可写(出站缓冲区中有空间),然后假设如果send()成功,则数据包没有被丢弃。send()只是告诉数据包是否被放入了套接字的出站缓冲区,而不是数据包是否真的被传输了。

如果UDP数据包被丢弃,套接字API中没有任何内容可以警告您。检测丢弃数据包的唯一方法是设计协议,以便接收器必须发回对接收到的每个数据包的确认。然后你可以做一些更像这样的事情:

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;

查看普通FTP(TFTP),以获取使用ack传输数据的协议示例。

以确定分组是否已经被丢弃。

我想你不明白选择是为了什么。应用程序查明对等方是否接收到UDP数据包的唯一方法是对等方是否对数据包做出响应,并且应用程序是否接收到响应。