1

私は bittorrent クライアントを作成しており、tcp 接続を介して複数のトラッカーに接続する必要があります。これを行うために、以下に示す winsock ラッパー クラスを作成しました。

class trackerSocket{
public:
    ~trackerSocket();

    int trackerInitialize(string address);
    int trackerSend(string getParams);
    int trackerRecv();

    be_node *responseDict;
    bool working;

private:
    string address;
    string port;
    string protocol;
    string page;
    SOCKET ConnectSocket;

    int parseAnnounce(string announce);
    int parseTrackerResponse(string response);
};

プログラムは、新しい trackerSocket クラスを変数に割り当てることから始めます。この関数で trackerInitialize 関数が呼び出され、成功した場合、クラスはベクトルにプッシュされ、すべての作業トラッカーが格納されます。trackerInitialize 関数は次のとおりです。

int trackerSocket::trackerInitialize(string announce){
    WSADATA wsaData;
    int iResult;

    working = true;

    iResult = parseAnnounce(announce);
    if(iResult != 0){
        working = false;
        return iResult;
    }

    //Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(iResult != 0){
        return 1;
    }

    struct addrinfo *result = NULL,
                    *ptr  = NULL,
                    hints;

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    //Resolve the server address and port
    iResult = getaddrinfo(address.c_str(), port.c_str(), &hints, &result);
    if(iResult != 0){
        WSACleanup();
        return 1;
    }

        ConnectSocket = INVALID_SOCKET;

    //Attempt to connect to the first address returned by
    //the call to getaddrinfo
    ptr = result;

    do{
        //Create a socket for connecting to the server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if(ConnectSocket == INVALID_SOCKET){
            ptr = ptr->ai_next;
            continue;
        }

        //Connect to server
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if(iResult != SOCKET_ERROR){
            break;
        } else {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            ptr = ptr->ai_next;
        }
    } while(ptr != NULL);

    freeaddrinfo(result);
    if(ConnectSocket == INVALID_SOCKET){
        working = false;
        WSACleanup();
        return 1;
    }

    return 0;
}

次に、プログラムはコードを実行して、トラッカーに送信されるメッセージを生成します。ベクター内の各トラッカー クラスに対して、trackerSend 関数がメッセージと共に呼び出されます。trackerSend 関数は次のとおりです。

int trackerSocket::trackerSend(string getParams){
    int iResult;

    ostringstream os;
    os << "GET " << page << getParams << " HTTP/1.1\r\n"
        << "Host: " << address << "\r\n"
        << "Accept: text/html\r\n"
        << "\r\n";

    string sendBuf = os.str();

    //Send tracker request
    iResult = send(ConnectSocket, sendBuf.c_str(), strlen(sendBuf.c_str()), 0);
    if(iResult == SOCKET_ERROR){
        working = false;
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    return 0;
}

プログラムが実行されるたびに、send 関数はトラッカーごとに -1 を返します。WSAGetLastError() 関数を呼び出すと、10093 が返されます。このエラーの msdn 定義は次のとおりです。

成功した WSAStartup はまだ実行されていません。アプリケーションが WSAStartup を呼び出していないか、WSAStartup が失敗しました。アプリケーションが、現在アクティブなタスクが所有していないソケットにアクセスしている (つまり、タスク間でソケットを共有しようとしている) か、WSACleanup が呼び出された回数が多すぎる可能性があります。

WSACleanup が何度も呼び出されたことがわからないので、ソケットが現在アクティブなタスクによって所有されていないと推測できます (それが何を意味するのかわかりません)。誰でも問題を確認できますか?

メインプログラムのコードの一部を次に示します (上記で説明しました)。

//Store tracker URL's in vector
vector<trackerSocket> trackers;
trackerSocket *temptracker = new trackerSocket();
iResult = temptracker->trackerInitialize(announce);
if(iResult == 0){
    trackers.push_back(*temptracker);
}
if(announcelist != NULL){
    i = 0;
    while(announcelist[i]){
        if(strcmp(announcelist[i]->val.l[0]->val.s, announce.c_str()) != 0){
            temptracker = new trackerSocket();
            iResult = temptracker->trackerInitialize(announcelist[i]->val.l[0]->val.s);
            if(iResult == 0){
                trackers.push_back(*temptracker);
            }
        }
        i++;
    }
}

//Check that at least one of the tracker URL's was valid
if(trackers.size() == 0){
    printf("None of the tracker URL's provided were valid.\n");
    return 1;
}

//Generate some required values
string peerid = genPeerID();
string peerport = "12345";
int uploaded = 0;
int downloaded = 0;

//Work out how many bytes are left to download
int left = 0;
if(singlefile){
    left = length;
} else {
    for(i = 0; i < filesinfo.size(); i++){
        left += filesinfo[i].length;
    }
}

//Send GET Request to tracker
i = 0;
ostringstream os;
string getParams;
string response;
os << "info_hash=" << infohash << "&peer_id=" << peerid << "&port=" << peerport <<
    "&uploaded=" << uploaded << "&downloaded=" << downloaded << "&event=started";

getParams = os.str();
do{     
    iResult = trackers[i].trackerSend(getParams);
    if(iResult != 0){
        printf("trackerSend %d failed: %d\n", i, iResult);
        i++;
        continue;
    }
} while(i < trackers.size());
4

2 に答える 2

2

コードから、送信時に WSACleanup を呼び出すべきではありません。1 つのトラッカーが送信に失敗した場合、内部カウンターが減少し、0 に達すると、システムは呼び出していない新しい WSAStartup を必要とします...

WSAStartup (アプリケーションの開始時など) を 1 回だけ呼び出し、WSACleanup を 1 回だけ呼び出すことをお勧めします。(例:応募終了)

于 2012-07-25T13:28:53.597 に答える
1

コードから休憩を取った後、問題を解決することができました。問題はこのSOの質問と回答で説明されていると思います:

オブジェクトの C++ ベクトルとオブジェクトへのポインターのベクトル

C++ のベクトルについて知っておくべきことは、オブジェクトのクラスのコピー演算子を使用して、オブジェクトをベクトルに入力できるようにする必要があるということです。デストラクタが呼び出されたときに自動的に割り当て解除されたこれらのオブジェクトにメモリ割り当てがあった場合、それは問題を説明する可能性があります。オブジェクトはベクトルにコピーされてから破棄されました。

オブジェクト クラスに、割り当てられたバッファを指すポインタがある場合、このオブジェクトのコピーは同じバッファを指します (デフォルトのコピー オペレータを使用する場合)。デストラクタがバッファの割り当てを解除すると、コピー デストラクタが呼び出されるときに元のバッファの割り当てが解除されるため、データは使用できなくなります。

new/destroy を介して要素の寿命を制御し、ベクトル関数は要素へのポインターのみをコピーするため、ポインターを使用する場合、この問題は発生しません。

trackerSocket クラスの単なるコピーではなく trackerSocket クラスへのポインターを格納するようにベクターを変更し、ベクター内の trackerSocket 関数への呼び出しを変更した後 (. to ->)、問題は正常に修正されました。ここに私が行った変更があります:

年:

vector<trackerSocket> trackers;
trackers.push_back(*temptracker);
trackers[i].trackerSend(getParams);

新しい:

vector<trackerSocket*> trackers;
trackers.push_back(temptracker);
trackers[i]->trackerSend(getParams);
于 2012-08-05T21:59:56.897 に答える