2

我目前在一些代碼中有一個很奇怪的問題。一個變量在其聲明後似乎沒有問題,之後會被破壞,並導致訪問衝突(基本上指針仍指向相同的地方,但內存似乎未分配)。我很確定這個問題與多線程有關,但我不知道它是什麼,因爲我對多線程是很陌生的。指針在多線程函數聲明後立即釋放

下面是代碼:

#include "Firewall.h" 
#include <Ws2tcpip.h> 

Firewall::Firewall(void) 
{ 
} 


Firewall::~Firewall(void) 
{ 
} 

void Firewall::parseFile(string filePath) 
{ 
    XMLNode xMainNode=XMLNode::openFileHelper(filePath.c_str(),"firewall"); 

    // Filtrage 
    XMLNode nodeFiltrage = xMainNode.getChildNode("filtrage"); 
    XMLNode currentNode; 

    for(int i=0; i < nodeFiltrage.nChildNode();i++) 
    { 
     currentNode = nodeFiltrage.getChildNode(i); 

     string nom = currentNode.getName(); 

     if(nom == "permettre") 
      mapFiltrage_.insert(pair<int,bool>(atoi(currentNode.getAttribute().lpszValue), true)); 

     else if(nom == "bloquer") 
      mapFiltrage_.insert(pair<int,bool>(atoi(currentNode.getAttribute().lpszValue), false)); 
    } 

    // Redirection 

    XMLNode nodeRedirection = xMainNode.getChildNode("redirection"); 
    XMLNode currentSubNode; 

    for(int i = 0; i < nodeRedirection.nChildNode(); i++) 
    { 
     currentNode = nodeRedirection.getChildNode(i); 
     currentSubNode = currentNode.getChildNode("source"); 

     SourceDestination source((string)currentSubNode.getAttribute("adresse"), atoi(currentSubNode.getAttribute("port"))); 

     currentSubNode = currentNode.getChildNode("destination"); 
     SourceDestination destination((string)currentSubNode.getAttribute("adresse"), atoi(currentSubNode.getAttribute("port"))); 

     mapRedirection_.insert(pair<SourceDestination, SourceDestination>(source,destination)); 

     pair<SourceDestination, SourceDestination> test; 
    } 


} 

void Firewall::initialiser() 
{ 
    std::map<int, bool>::iterator iterFiltrage = mapFiltrage_.begin(); 
    HANDLE handleThread; 

    std::string tempFiltrage = "localhost"; 
    thread_arg arg; 

    // Parcours et lancement des connexions de filtrage 
    while(iterFiltrage != mapFiltrage_.end()) 
    { 
     arg.port = (*iterFiltrage).first; 
     arg.host = tempFiltrage; 
     arg.objRef = this; 

     handleThread = CreateThread(NULL, 0, listenThread, &arg, 0, NULL); 
     listeThread_.push_back(handleThread); 

     iterFiltrage++; 
    } 

    // Parcours et lancement des connexions de redirection 
    std::map<SourceDestination, SourceDestination>::iterator iterRedirection = mapRedirection_.begin(); 

    while(iterRedirection != mapRedirection_.end()) 
    { 
     // Éviter la duplication inutile des sockets 
     if(mapFiltrage_.find((*iterRedirection).first.Port()) == mapFiltrage_.end()) 
     { 
      arg.host = (*iterRedirection).first.Host(); 
      arg.port = (*iterRedirection).first.Port(); 
      arg.objRef = this; 

      handleThread = CreateThread(NULL, 0, listenThread, &arg, 0, NULL); 
      listeThread_.push_back(handleThread); 
     } 

     iterRedirection++; 
    } 
} 


DWORD WINAPI Firewall::listenThread(LPVOID lpParam) 
{ 
    thread_arg* temp = (thread_arg*)lpParam; 
    Firewall* firewallRef = temp->objRef; 

    return firewallRef->runThread(lpParam); 
} 

DWORD Firewall::runThread(LPVOID lpParam) 
{ 
    thread_arg* infosSocket = (thread_arg*)lpParam; 

    // Créer le socket et l'attacher à la source 
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); 

    if(sock == INVALID_SOCKET) 
    { 
     cout << "Erreur de creation de socket" << endl; 
     return EXIT_FAILURE; 
    } 

    //Recuperation de l'adresse locale 
    hostent *thisHost; 
    const char* test = infosSocket->host.c_str(); 
    thisHost=gethostbyname(test); 
    char* ip; 
    ip=inet_ntoa(*(struct in_addr*) *thisHost->h_addr_list); 

    SOCKADDR_IN sin; 
    sin.sin_addr.s_addr = inet_addr(ip); 
    sin.sin_family = AF_INET; 
    sin.sin_port = htons(infosSocket->port); 



    if(bind(sock, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR) 
    { 
     cout << "Erreur de binding" << endl; 
     return EXIT_FAILURE; 
    } 

    // Contexte du client 
    SOCKADDR_IN csin; 
    SOCKET csock; 
    socklen_t crecsize = sizeof(csin); 

    listeSocket_.push_back(sock); 
    listeSocket_.push_back(csock); 

    // Écouter sur le port 
    if(listen(sock, 5) == SOCKET_ERROR) 
    { 
     cout << "Erreur de listen" << endl; 
     return EXIT_FAILURE; 
    } 

    //csock = accept(sock, (SOCKADDR*)&csin, &crecsize); 

    return EXIT_SUCCESS; 
} 

void Firewall::quitter() 
{ 
    // Fermer les sockets 
    vector<SOCKET>::iterator iter1 = listeSocket_.begin(); 

    while(iter1 != listeSocket_.end()) 
    { 
     closesocket((*iter1)); 
     iter1++; 
    } 

    // Fermer les threads 

    vector<HANDLE>::iterator iter2 = listeThread_.begin(); 

    while(iter2 != listeThread_.end()) 
    { 
     TerminateThread((*iter2), EXIT_SUCCESS); 
     CloseHandle((*iter2)); 
    } 
} 

非常感謝。

回答

1

你的問題是這樣的代碼:

thread_arg arg; 

loop(...) 
{ 
    arg = ...; 
    handleThread = CreateThread(..., &arg, ...); 
} 

每個線程都從這裏開始接收相同thread_arg實例的地址。然後,爲了啓動下一個線程,再次在先前啓動的線程的腳下修改該實例。作爲一種補救措施,創建一個結構來保存必要的參數(主機,端口,this)和HANDLE。將此結構存儲在std :: list中,然後將相應元素的地址傳遞給CreateThread()。

你的代碼還有另一個問題,你應該檢查返回值。如果您知道所有明顯的錯誤都已被檢測到,那麼在某些代碼上尋求幫助會更好。爲此,使用例外是最容易的。後的CreateThread(),它應該可能被beginthread()代替,添加這些行:

if(handleThread == NULL) 
    throw std::runtime_error("CreateThread() failed"); 

在第二步驟中,創建專用異常類,從runtime_error衍生,其持有的win32錯誤代碼(參見GetLastError函數( )),並在異常消息中包含文本錯誤描述(請參閱FormatString())。這可能聽起來像很多代碼,但你只寫了一次,你可以在很多地方重複使用它。

最後,你的quitter()有兩個問題。第一個是無限循環。假設你不需要關閉它們後把手,試試這個來代替:

for(; listeThread_.empty(); listeTread_.pop_back()) 
{ 
    TerminateThread(listeThread_.back(), EXIT_SUCCESS); 
    CloseHandle(listeThread_.back()); 
} 

你可以寫爲while循環,太多,但我個人更喜歡一個for循環,如果迭代次數基本上是固定的。當然,你仍然需要檢查TerminateThread()和CloseHandle()的返回值。第二個問題是TerminateThread()是一個不好的主意,因爲你可能會終止線程,但仍然沒有完成。在網絡上搜索「terminatethread harmful」。在這裏,你所能做的就是等待它結束使用WaitForSingleObject()。

+0

非常感謝提示。也感謝您抽出寶貴時間來看看其他代碼,並指出我犯的一些其他錯誤。 – Djeezus 2013-04-09 15:49:06