C++ 中的 Bittorrent 客户端,在非阻塞套接字上连接到对等点总是超时

Posted

技术标签:

【中文标题】C++ 中的 Bittorrent 客户端,在非阻塞套接字上连接到对等点总是超时【英文标题】:Bittorrent Client in C++, Connecting to Peer on Non-Blocking socket always times out 【发布时间】:2013-11-19 19:58:05 【问题描述】:

编辑::

所以我在下面接受的答案实际上并不是问题。我通过wireshark 验证了对等点确实通过TCP 传输以进行torrent 下载。所以我应该可以连接,但所有尝试都超时......


所以我正在用 C++ 制作一个 bittorrent 客户端,并且我正在使用 BSD 套接字库进行所有网络通信。我有一些代码可以通过 TCP 连接到对等方,但每次尝试都会超时。我 100% 确定对等点对我要下载的文件有效,我开始在传输中下载文件,并且正在连接相同的对等点。

这是我的连接代码,第一部分只是将一堆对等点添加到向量中,以便我可以对其进行迭代并尝试每个对等点:

(注意“所有大写的系统调用只是用于错误处理目的的包装函数。那里没有发生任何有趣的事情。)

    char * HOST;
    uint16_t PORT;

    std::vector<char *> all_ips;
    std::vector<uint16_t> all_ports;

    all_ips.push_back("213.112.225.102");
    all_ports.push_back(18715);

    uint32_t i = 0;
    for (; i < all_ips.size(); i++) 

        HOST = all_ips[i];
        PORT = all_ports[i];

        struct sockaddr * saddr;
        struct sockaddr_in addr;
        struct addrinfo hints, * ai,  * it;
        char strportnum[25];

        memset(&hints, '\0', sizeof(hints));
        hints.ai_flags = AI_ADDRCONFIG;
        hints.ai_socktype = SOCK_STREAM;

        snprintf(strportnum, 10, "%d", PORT);


        GetAddrInfo(HOST, strportnum, &hints, &ai);

        for (it = ai; it != NULL; it = it->ai_next) 

            if ((sockFd = Socket(AF_INET, SOCK_STREAM, 0)) != -1) 

                saddr = ai->ai_addr;
                saddr->sa_family = AF_INET;


                int res; 
                long arg; 
                fd_set myset; 
                struct timeval tv; 
                int valopt; 
                socklen_t lon; 

                // Set non-blocking 
                if( (arg = fcntl(sockFd, F_GETFL, NULL)) < 0)  
                    fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno)); 
                    exit(0); 
                 
                arg |= O_NONBLOCK; 
                if( fcntl(sockFd, F_SETFL, arg) < 0)  
                    fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno)); 
                    exit(0); 
                 

                // Trying to connect with timeout 
                res = Connect(sockFd, saddr, sizeof(*saddr)); 
                if (res < 0)  

                    if (errno == EINPROGRESS)  

                        fprintf(stderr, "EINPROGRESS in connect() - selecting\n"); 

                        do  

                            //Set timeouts
                            tv.tv_sec = 20; 
                            tv.tv_usec = 0; 

                                FD_ZERO(&myset); 
                            FD_SET(sockFd, &myset); 

                            res = Select(sockFd + 1, NULL, &myset, NULL, &tv); 

                            if (res < 0 && errno != EINTR)  
                                fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno)); 

                                break;
                             
                            else if (res > 0)  

                                // Socket selected for write 
                                lon = sizeof(int); 
                                if (getsockopt(sockFd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0)  
                                    fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno)); 


                                    break;
                                 

                                // Check the value returned... 
                                if (valopt)  
                                    fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)); 


                                    break;
                                 

                                break; 
                             
                            else  
                                fprintf(stderr, "Timeout in select() - Cancelling!\n"); 

                                break;
                              
                         while (1); 
                     
                    else  
                        fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno)); 

                        break;
                     
                 
                // Set to blocking mode again... 
                if( (arg = fcntl(sockFd, F_GETFL, NULL)) < 0)  
                fprintf(stderr, "Error fcntl(..., F_GETFL) (%s)\n", strerror(errno)); 

                        break;
                 

                arg &= (~O_NONBLOCK); 

                if(fcntl(sockFd, F_SETFL, arg) < 0)  
                    fprintf(stderr, "Error fcntl(..., F_SETFL) (%s)\n", strerror(errno)); 

                        break;
                     
            
        
        freeaddrinfo(ai);
    

我一直使用这个网站作为非阻塞套接字的指南: http://developerweb.net/viewtopic.php?id=3196

【问题讨论】:

当您尝试连接到 localhost 服务时会发生什么?例如。用 netcat 启动一个监听器,然后用你的代码连接? 您是否正在尝试使用 TCP 连接到期望与“说”UDP 的端口? 供参考:en.wikipedia.org/wiki/Micro_Transport_Protocol 我要做的事情:使用 strace -v 查看系统调用的确切参数。使用 tcpdump/wireshark 查看发送/接收的数据包。通过这种方式可以轻松检测到诸如未进行正确字节交换之类​​的琐碎问题。 您应该找到一个可以连接的标准客户端程序。 Telnet 或 netcat 都可以。然后你开始观察使用wireshark的工作连接,以及你的代码的非工作连接。比较工作包和非工作包的字段,试着找出它们之间的区别。 【参考方案1】:

我强烈假设您的程序尝试使用 TCP 连接的端口使用 UDP。后者成为洪流通信的共同基础。

参考请看这两个链接:

uTorrent transport protocol Micro Transport Protocol

【讨论】:

因此,自从接受了这个答案后,我意识到这些对等方也接受 TCP 连接(在禁用 uTP (microTP) 的传输上通过 Wireshark 明确地发现了这一点)......所以我仍然有这个问题...任何进一步的见解? @Ethan:但您肯定不会使用最初问题中提到的端口和主机来执行此操作,不是吗? 使用这个主机。我刚刚启动了传输和wireshark,并专门查看了我列出的确切主机的TCP流量(顺便说一下,谁的正常运行时间惊人)。上面的所有代码都是一样的......我不明白为什么如果传输可以,它不应该通过 TCP 连接。 "使用这台主机"和端口? 好的,我收回,传输使用不同的端口...尝试使用该端口连接在我的客户端上是成功的!!!但是接收失败...我是否需要循环直到有可用数据,使用 select 进行检查?

以上是关于C++ 中的 Bittorrent 客户端,在非阻塞套接字上连接到对等点总是超时的主要内容,如果未能解决你的问题,请参考以下文章

RPC远程协议之Thrift非阻塞多线程处理

在非启动项目中使用 lib 文件,在 c++ 解决方案中

未从 bittorrent 对等握手接收到任何数据

使用 SOCKS 时的 BitTorrent 本地对等点发现

如何连接到从 bittorrent 获得的对等点

BitTorrent 跟踪器宣布问题