user2545592
user2545592

Reputation: 51

Linux TCP Server - listen on multiple ports in C++

I am having a problem with a tcp server. I would like to listen on multiple ports to respond to clients. It should be a kind of event based. Each port indicates another type of response. I read a lot about epoll, poll, select or multithreading. I tried to work with a lot of examples from books like Unix Network Programming. But probably I need a few trigger keywords. How could I start properly?

Hopefully my questions are easy to understand. Appreciate each answer!

TO NARROW IT DOWN THERE IS MY IDEA... I started thinking of this:

If I am have a "Server Manager" with a lot of Server, can I do it as follows?

CreateSockets(ServerList); CheckSockets(SocketList, master_set);

Within the Server Manager: 1) for loop to create all server sockets (functions: socket/setsockopt/ioctl/bind/listen)

void CreateSockets(map<int,ServerType> ServerList)
{
    fd_set        master_set;
    map<int,ServerType>::iterator it;
    map<int,int> SocketList;
    for (it= ServerList.begin();it!= ServerList.end();it++)
    {
        listen_sd = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sd < 0)
        {
            perror("socket() failed");
            exit(-1);
        }
        rc = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,
                (char *)&on, sizeof(on));
        if (rc < 0)
        {
            perror("setsockopt() failed");
            close(listen_sd);
            exit(-1);
        }
        rc = ioctl(listen_sd, FIONBIO, (char *)&on);
        if (rc < 0)
        {
            perror("ioctl() failed");
            close(listen_sd);
            exit(-1);
        }
        memset(&addr, 0, sizeof(addr));
        addr.sin_family      = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port        = ((*it).second->Port);
        rc = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));
        if (rc < 0)
        {
            perror("bind() failed");
            close(listen_sd);
            exit(-1);
        }
        rc = listen(listen_sd, 32);
        if (rc < 0)
        {
            perror("listen() failed");
            close(listen_sd);
            exit(-1);
        }
        SocketList.insert(make_pair(((*it).second->Port),listen_sd));
        FD_ZERO(&master_set);
        max_sd = listen_sd;
        FD_SET(listen_sd, &master_set);
    }
}

Next: 2) Some how wait for some events within the server manager (Select with a list of socket descriptors)

void CheckSockets(map<int,int> SocketList, fd_set master_set)
 {
    fd_set working_set;
    do
    {
        memcpy(&working_set, &master_set, sizeof(master_set));
        ready_descriptors = select(max_sd + 1, &working_set, NULL, NULL, NULL);

        if (ready_descriptors >0)
        {
            desc_ready = rc;
            for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
            {
                if (FD_ISSET(i, &working_set))
                {
                    desc_ready -= 1;
                    if (i == listen_sd)
                    {
                        do
                        {
                            new_sd = accept(listen_sd, NULL, NULL);
                            if (new_sd < 0)
                            {
                                //error
                            }
                            FD_SET(new_sd, &master_set);
                            if (new_sd > max_sd)
                                max_sd = new_sd;
                        } while (new_sd != -1);
                    }
                    else
                    {
                        do
                        {
                            //Go into server and recv and send ( Input Parameter = i)
                            CheckServer(i);
                        } while (TRUE);
                    }
                }
            }
        }
        else {endserver=true;}
    }while(endserver=true;)

}

3) Go into the server and process the question (recv/send)????

    void CheckServer( int sd)
 {
    rc = recv(sd, buffer, sizeof(buffer), 0);

    //some stuff in between

    rc = send(i, buffer, len, 0); 
 }

Could this work?

Some parts are used and changed from the IBM nonblocking IO source Code.


thank you for all your help. I was able to get something done, BUT one thing still dont work.

What I did until now:

1) The consrtuctor of the individual server includes the socket operation. 2) I am able to return the socket id and save it within the server manager. 3) The manager has a for loop which contains the select command to check for any event on the sockets. 4) if something happens, all affected socket will sequentially repsond.

My problem is:

It works fine if I am always connect and disconnect while i am requesting data from the server. When my client is configured in a way that the connection will be hold, everything is blocking, since my code is waiting for a disconnect.

Here are the code snippets for each part:

1)

       Server::Server() 
    {

    listen_sd = socket(AF_INET, SOCK_STREAM, 0);
    ret = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,(char *)&on, sizeof(on));
    ret = ioctl(listen_sd, FIONBIO, (char *)&on);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(Server_Port);
    ret = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));
    ret = listen(listen_sd, 32);
    Socket = listen_sd;
}

2)

Socket= new_Server->GetSocket();
SocketList.insert(make_pair(Socket,new_Server->ServerID));

3)

 while (TRUE)
    {
        FD_ZERO(&working_set);
        for (i=0;i < max_conn;i++) 
{
            if (SocketArray[i] >= 0) {FD_SET(SocketArray[i], &working_set);}
        }

        ret = select(max_sd+1, &working_set, NULL, NULL, NULL);

    desc_ready= ret;
        for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
        {

            if (FD_ISSET(i, &working_set)) //jeder Peer der was hat
            {
                desc_ready -= 1;
//delete all loops to get the correct object 
                        (Server).second->DoEvent(i);

            }
        }
    }

4)

new_sd = accept(new_sd, NULL, NULL);
    if (new_sd < 0)
    {
        if (errno != EWOULDBLOCK)
        {
            perror("  accept() failed");
        }
    }
do
{


    rc = recv(new_sd, buffer, sizeof(buffer), 0);
//edit datastream and create response
            rc = send(new_sd, buffer, len, 0);
        if (rc < 0)
        {
            perror("  send() failed");
            close_conn = TRUE;
            break;
        }

    }while (TRUE);

I just delete the errorhandling dor listen/bind etc. just to shorten the code here... Originally it is in there.

Upvotes: 3

Views: 9227

Answers (1)

Manoj Pandey
Manoj Pandey

Reputation: 4666

Roughly here are the steps: You can have multiple TCP servers (aka server sockets) listen for each port. Next, you can use a select() and pass file descriptors for each of these server sockets. If you get a connection on any of these, then select would return a read event and mark the fd of the server socket that has the connection. YOu would need to call accept() on that server fd.

YOu cannot make a single TCP socket listen on multiple ports.

Upvotes: 3

Related Questions