user502230
user502230

Reputation:

sockets question

i have a server and client classes but the problem is: when i make infinite loop to accept incoming connection i cant receive all the data received from the client while accepting the connections because accept blocks until the connection is accepted, my code:

    for (;;) 
    {
       boost::thread thread(boost::bind(&Irc::Server::startAccept, &s));
       thread.join();
       for (ClientsMap::const_iterator it = s.begin(); it != s.end(); ++it) 
       {
          std::string msg = getData(it->second->recv());
          std::clog << "Msg: " << msg << std::endl;
       }
    }

Upvotes: 0

Views: 181

Answers (3)

Ben Voigt
Ben Voigt

Reputation: 283793

You need either multiple threads or a call to select/poll to find out which connections have unprocessed data. IBM has a nice example here, which will work on any flavor of Unix, Linux, BSD, etc. (you might need different header files depending on the OS).

Right now you're starting a thread and then waiting for it immediately, which results in sequential execution and completely defeats the purpose of threads.

Upvotes: 3

Amy
Amy

Reputation: 1914

A good approach would be to create one thread that only accepts new connections. That's where you have a listener socket. Then, for every connection that gets accepted, you have a new connected socket, so you can spawn another thread, giving it the connected socket as a parameter. That way, your thread that accepts connections doesn't get blocked, and can connect to many clients very fast. The processing threads deal with the clients and then they exit.

I don't even know why need to wait for them, but if you do, you may deal with it in some other way, depending on the OS and/or libraries that you use (messages, signals etc can be used).

If you don't want to spawn a new thread for each connected client, then as Ben Voigt suggested, you can use select. That is another good approach if you want to make it single threaded. Basically, all your sockets will be in an array of socket descriptors and using select you will know what happened (someone connected, socket is ready for read/write, socket got disconnected etc) and act accordingly.

Here's one example Partial one, but it works. you just accept connections in the acceptConnections(), which will then spawn a separate thread for each client. That's where you communicate with the clients. It's from a windows code that i have lying around, but it's very easy to be reimplemented for any platform.

typedef struct SOCKET_DATA_ {
  SOCKET sd;
  /* other parameters that you may want to pass to the clientProc */
} SOCKET_DATA;

/* In this function you communicate with the clients */
DWORD WINAPI clientProc(void * param)
{
    SOCKET_DATA * pSocketData = (SOCKET_DATA *)param;

    /* Communicate with the new client, and at the end deallocate the memory for
       SOCKET_DATA and return.
    */

    delete pSocketData;
    return 0;
}

int acceptConnections(const char * pcAddress, int nPort)
{
    sockaddr_in sinRemote;
    int nAddrSize;
    SOCKET sd_client;
    SOCKET sd_listener;
    sockaddr_in sinInterface;
    SOCKET_DATA * pSocketData;
    HANDLE hThread;

    sd_listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (INVALID_SOCKET == sd_listener) {
        fprintf(stderr, "Could not get a listener socket!\n");
        return 1;
    }

    sinInterface.sin_family = AF_INET;
    sinInterface.sin_port = nPort;
    sinInterface.sin_addr.S_un.S_addr = INADDR_ANY;

    if (SOCKET_ERROR != bind(sd_listener, (sockaddr*)&sinInterface, sizeof(sockaddr_in))) {
        listen(sd_listener, SOMAXCONN);
    } else {
        fprintf(stderr, "Could not bind the listening socket!\n");
        return 1;
    }

    while (1)
    {
        nAddrSize = sizeof(sinRemote);
        sd_client = accept(sd_listener, (sockaddr*)&sinRemote, &nAddrSize);

        if (INVALID_SOCKET == sd_client) {
            fprintf(stdout, "Accept failed!\n");
            closesocket(sd_listener);
            return 1;
        }

        fprintf(stdout, "Accepted connection from %s:%u.\n", inet_ntoa(sinRemote.sin_addr), ntohs(sinRemote.sin_port));
        pSocketData = (SOCKET_DATA *)malloc(sizeof(SOCKET_DATA));

        if (!pSocketData) {
            fprintf(stderr, "Could not allocate memory for SOCKET_DATA!\n");
            return 1;
        }

        pSocketData->sd = sd_client;
        hThread = CreateThread(0, 0, clientProc, pSocketData, 0, &nThreadID);

        if (hThread == INVALID_HANDLE_VALUE) {
            fprintf(stderr, "An error occured while trying to create a thread!\n");
            delete pSocketData;
            return 1;
        }
    }

    closesocket(sd_listener);
    return 0;
}

Upvotes: 0

Marek Szanyi
Marek Szanyi

Reputation: 2328

Take a look here : http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio/examples.html

especially the HTTP Server 3 example, thats exactly what you are looking for , all you have to do is change that code a little bit for your needs :) and your done

Upvotes: 0

Related Questions