Reputation: 51
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
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