用C ++连接到UDP Tracker

时间:2013-10-01 17:15:19

标签: c++ udp bittorrent tracker

我正在尝试使用下面的代码连接到UDP跟踪服务器,但我没有收到跟踪器的任何回复......

我通过这个链接收集了我的内容: http://xbtt.sourceforge.net/udp_tracker_protocol.html

我认为我得到了......但显然不是。代码执行正常,然后在调用RecvFrom时挂起。所以我猜我要么不发送正确的数据,要么就是把它发送到错误的地方......

struct ConnectionIdRequest_t {

    uint64_t connectionId;
    uint32_t action;
    int32_t transactionId;
} typedef ConnectionIdRequest;

const bool UdpTorrentTrackerComm::initiateConnection(const int amountUploaded, 
const int amountDownloaded, 
const int amountLeft) {

    struct sockaddr_in serverAddress, clientAddress;
    struct hostent * host;
    struct in_addr address;

    //Setup dummy client address
    clientAddress.sin_family = AF_INET;
    clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    clientAddress.sin_port = htons(0);

    //Setup server address
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(portNumber);

    //SETUP in_addr server address
    //If we have an IP
    if (trackerAddress) {

        if (isIp4Address(*trackerAddress)) {

            //retrieve hostname from ip address 
            if (inet_aton(trackerAddress->c_str(), &address)) {

                host = gethostbyaddr((const char *) &address, sizeof(address), AF_INET);
                trackerHostname = new std::string(host->h_name);
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }
    else {
        //retrieve ip address from hostname
        host = gethostbyname(trackerHostname->c_str());
        address.s_addr = ((struct in_addr *) host->h_addr_list)->s_addr;
        trackerAddress = new std::string(inet_ntoa(address));
    }
std::cout << *trackerAddress << std::endl;
    //Convert trackerAddress to network format
    if(!inet_aton(trackerAddress->c_str(), &serverAddress.sin_addr)) {
        return false;
    }

    int sockFd = -1;
    //Add IPv6 in the future
    if ((sockFd = Socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        return false;
    }

    //Bind my address to the socket
    if (Bind(sockFd, (struct sockaddr *) &clientAddress, sizeof(clientAddress)) == - 1) {
        return false;
    }

    std::cout << "SendTo\n";
    ConnectionIdRequest * idRequest = createConnectionIdRequest();
    if (SendTo(sockFd, idRequest, sizeof(*idRequest), 0, 
        (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
        return false;
    }
    timeRequestSent = clock();
std::cout << "Sent: " << idRequest->connectionId << "|||" << idRequest->action << "|||" << idRequest->transactionId << std::endl;
    std::cout << "RecvFrom\n";
    char buffer[3000];
    socklen_t serverAddressLength = sizeof(serverAddress);
    while(true) {
        if (RecvFrom(sockFd, buffer, 3000, 0, 
            (struct sockaddr *) &serverAddress, &serverAddressLength) == - 1) {
            break;
            std::cout << "breaking...\n";
        }
    }
    std::cout << "The buffer is: " << buffer << std::endl;
    Close(sockFd);

    return true;
}

ConnectionIdRequest * UdpTorrentTrackerComm::createConnectionIdRequest() {

    ConnectionIdRequest * idRequest = new ConnectionIdRequest;
    generatePeerId();
    idRequest->connectionId = htonll(0x41727101980);
    idRequest->action = htonl(CONNECT);
    idRequest->transactionId = htonl(*peerId);

    return idRequest;
}

编辑:好吧,我做了Arvid建议的一个改变,但这没有任何帮助。我正在经历并确保我将所有发送的字节转换为网络字节顺序...也许我错过了一些......

2 个答案:

答案 0 :(得分:1)

看起来你正在混淆交易ID和对等ID。它们是不同的。事务ID是您发送的cookie,以便将返回的数据包与正确的请求进行匹配。

看起来您还没有初始化connectionID。您必须在初始连接消息中将其设置为魔术64位数。 0x41727101980

您可以找到替代协议说明here

答案 1 :(得分:1)

我最终让它上班了。问题主要在于我没有转换所有需要转换为big endian(网络排序)的值,以及使用过时的函数(gethostbyname等)。

如果您想了解更多详情,请发表评论。

这是我可以与跟踪服务器建立链接的代码:

注意:serverAddress和clientAddress是类型的类字段:struct sockaddr_in

const bool UdpTorrentTrackerComm::initiateConnection() {

    //Setup dummy client address
    clientAddress.sin_family = AF_INET;
    clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    clientAddress.sin_port = htons(51413);

    //Setup server address
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons(portNumber);

    //SETUP in_addr server address
    //If we have an IP
    if (trackerAddress) {

        if (isIp4Address(*trackerAddress)) {

            //Convert human readable trackerAddress to network byte order ip address and place in serverAddress.sin_addr
            if (inet_pton(AF_INET, trackerAddress->c_str(), &(serverAddress.sin_addr))) {

                //retrieve hostname and service type from ip address        
                char hostBuffer[100], serviceBuffer[100];
                getnameinfo((struct sockaddr *) &serverAddress, sizeof(serverAddress), 
                    hostBuffer, sizeof(hostBuffer), 
                    serviceBuffer, sizeof(serviceBuffer), 
                    NI_NAMEREQD | NI_DGRAM);

                trackerHostname = new std::string(hostBuffer);
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }
    else {

        //Setup structs to be used in getaddrinfo
        struct addrinfo hints;
        struct addrinfo * result, * resultPointer;
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_DGRAM;
        hints.ai_flags = 0;
        hints.ai_protocol = 0;

        //Convert port number to string to pass to getaddrinfo
        std::stringstream ss;
        ss << portNumber;
        std::string portNumberString = ss.str();

        //retrieve ip address from hostname--------
        if (GetAddrInfo(trackerHostname->c_str(), portNumberString.c_str(), &hints, &result) != 0) {
            return false;
        }

        //Iterate over results for IP address V4 (ADD V6 later!)
        char ipBuffer[INET_ADDRSTRLEN];
        for (resultPointer = result; resultPointer != NULL; resultPointer = resultPointer->ai_next) {

            //If we have an IPv4 address
            if (resultPointer->ai_family == AF_INET) {

                //convert to presentation format and store in ipBuffer
                inet_ntop(AF_INET, &((struct sockaddr_in *) resultPointer->ai_addr)->sin_addr, ipBuffer, INET_ADDRSTRLEN);
            }
        }
        //Free result
        freeaddrinfo(result);

        //Convert ipBuffer to std::string and store in trackerAddress field
        trackerAddress = new std::string(ipBuffer);

        //Convert trackerAddress to network format
        if(!inet_pton(AF_INET, trackerAddress->c_str(), &serverAddress.sin_addr)) {
            return false;
        }

    }

    int sockFd = -1;
    //Add IPv6 in the future
    if ((sockFd = Socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        return false;
    }

    //Bind my address to the socket
    if (Bind(sockFd, (struct sockaddr *) &clientAddress, sizeof(clientAddress)) == - 1) {
        return false;
    }

    //Send a request to the tracker
    ConnectionIdRequest * idRequest = createConnectionIdRequest();
    if (SendTo(sockFd, idRequest, sizeof(*idRequest), 0, 
        (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) {
        return false;
    }
    timeRequestSent = clock();

    //Re-send until timeout.....
    ConnectionIdResponse idResponse;
    socklen_t serverAddressLength = sizeof(serverAddress);
    while((timeRequestSent - clock()) / 1000 < SECONDS_UNTIL_TIMEOUT) {

        //Response received!
        if (RecvFrom(sockFd, &idResponse, sizeof(idResponse), 0, 
            (struct sockaddr *) &serverAddress, &serverAddressLength) > 0) {
            break;
        }
    }

    //Set class fields that will persist
    activeSocket = sockFd;
    connectionId = ntohll(idResponse.connectionId);

    delete idRequest;

    return true;
}

两个结构的位置,ConnectionIdResponse和ConnectionIdRequest定义为:

    /* Struct used to send a request for a connectionId to the tracker server.*/
struct ConnectionIdRequest_t {

    uint64_t connectionId;
    uint32_t action;
    uint32_t transactionId;
} typedef ConnectionIdRequest;

/* Struct used in receipt of a request for a connectionId from the tracker server. */
struct ConnectionIdResponse_t {
    uint32_t action;
    uint32_t transactionId;
    uint64_t connectionId;
} typedef ConnectionIdResponse;