jeudi 30 juin 2016

std::map crash after erase when used on different calls

I have a simple TCP server socket handler which uses std::map to store the list of socketID and their associate server class pointer. I made this to have single handler to handle multiple servers.

Whenever I create a server socket, I add the socket ID and the respective server class pointer to the map list.

int c_socketInterface::openTCPServerConnection( char *ptrIPAddr, unsigned short port){
int nSocket = (socketDescriptor)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

printf("Opening TCP-Server connection at: %s:%d on Socket :%d", ptrIPAddr, port, nSocket);

if (-1 != nSocket) {
    struct sockaddr_in stSockAddr;
    memset(&(stSockAddr), '\0', sizeof(sockaddr_in));
    stSockAddr.sin_family = AF_INET;
    stSockAddr.sin_port = htons(port);
    stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    int nOptVal = 1;
    if (setsockopt(nSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&nOptVal, sizeof(nOptVal)) == -1) {
        if (0 == bind(nSocket, (struct sockaddr *) &stSockAddr, sizeof(struct sockaddr))) {
            if (-1 == listen(nSocket, 5)){
                printf("socketInterface: listen() failed: %ld\n", WSAGetLastError());
            }
        }
    }
}

return nSocket;

}

  1. After opening server connection, the listening socket ID is added to the map
  2. Once the client connects to the server, the accepted socket ID is also added to the same map list.(Hope listening socketId and acceptConnection socketId are not same)
  3. When the client sends some data, it is received on the accept connection socketId.
  4. When the client closes the socket, it is notified to the server via the accept connection socket.
  5. During this stage, the accept connection socket Id should be erased from the std::map list and the socket handler should continue to look for other any data on other sockets.

My std::map

typedef std::map<int, serverClass *> sockConnectionContainer;
sockConnectionContainer sockConnectionList;

Removing and Adding to the list:

void c_socketHandler::addCallbackFunc(int sockDesc, serverClass  *pa_poServer) {

sockConnectionList.insert(make_pair(sockDesc, pa_poServer));
isConnectionListChanged = true;

if (!isAlive()){
    this->start("Socket Handler"); //Start socket handler thread
}
resumeSelfSuspend();
}

void c_socketHandler::removeCallbackFunc(socketDescriptor sockDesc) {

for (sockConnectionContainer::iterator iter = sockConnectionList.begin(); iter != sockConnectionList.end(); ) {
    if (iter->first == sockDesc) {
        iter = sockConnectionList.erase(iter);
    }
    else {
        ++iter;
    }
}

isConnectionListChanged = true;
}

creating FDset:

int c_socketHandler::createFDSet(fd_set *fdSet) {
    intretVal = ZERO_VALUE;
    FD_ZERO(fdSet);

for (sockConnectionContainer::iterator iter = sockConnectionList.begin(); iter != sockConnectionList.end(); ++iter) {
    FD_SET(iter->first, fdSet);
    if(iter->first > retVal){
        retVal = iter->first;
    }
}

isConnectionListChanged = false;
return retVal;
}

socket handler thread:

void c_socketHandler::run(void) {
struct timeval tv;
fd_set anFDSet;
fd_set anFDSetMaster;

int nHighestFDID = 0;
int retval = 0;

FD_ZERO(&anFDSetMaster);

while (isAlive()){

    if (sockConnectionList.empty()){
        selfSuspend();
    }

    if (true == isConnectionListChanged){
        nHighestFDID = createFDSet(&anFDSetMaster);
    }
    anFDSet = anFDSetMaster;


    tv.tv_sec = 1; 
    tv.tv_usec = 1001;

    if (0 != nHighestFDID){
        retval = select(nHighestFDID + 1, &anFDSet, NULL, NULL, &tv);
    }

    if (retval > 0){

        for (sockConnectionContainer::iterator iter = sockConnectionList.begin(); iter != sockConnectionList.end(); ++iter) {
            serverClass *serverClassPtr = iter->second;
            int sockDesc = iter->first;

            if ((0 != FD_ISSET(sockDesc, &anFDSet)) && (0 != serverClassPtr)) {
                serverClassPtr->recvData(&sockDesc, 0)) {
            }
        }
    }
}
}

Issue here is the accept connection socketId sometimes stored as a last element and while erasing using the removeCallback function the iter points the end of the map. But when the function call gets completed, the iterator inside the socket handler thread run() gives an exception when doing ++iter.

how to solve this issue? Also I need to know should I need to remove the listening socket ID or the accept connection socket ID. In case, the client may connect again anytime. I didn't face any exception when closing the listening socket ID as the socket ID was lesser than the accept connection ID, so practically seeing it is not the end of the map.

Aucun commentaire:

Enregistrer un commentaire