1

I am trying to design an echo server which has concurrently feature. It means, Server for each client, it create a parent and child processes. It is for a game server and each client play separately. I have come up with following code but I have no Idea why each time there is a message from client to server it starts to create a new process and start from

for(;;){ // Run forever.

As I said I think I must have one process for each client. I expect every process to remain in HandleTCPClient until client close its socket

Other issue is where can I initial my datas so each children process share it with itself.

#include "wrappers.h"   // socket wrapper fns
#include <sys/wait.h>   // for waitpid()

#define RCVBUFSIZE 32   // Size of receive buffer

void HandleTCPClient(int ClntSocket);
extern "C" void SigChldHandler( int Signum );

int i = 0;

int main(int argc, char *argv[])
{
    int ServSock;                    // Socket descriptor for server
    int ClntSock;                    // Socket descriptor for client
    unsigned short EchoServPort;     // Server port
    sockaddr_in EchoServAddr;        // Local address
    sockaddr_in EchoClntAddr;        // Client address
    pid_t ProcessID;                 // Process ID from fork()
    unsigned int ChildProcCount = 0; // Number of child processes



    EchoServPort = SERV_TCP_PORT;;  // First arg:  local port

    // Create socket for incoming connections
    ServSock = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    // Construct local address structure
    memset((char*)&EchoServAddr, 0, sizeof(EchoServAddr));   /* Zero out structure */
    EchoServAddr.sin_family = AF_INET;                /* Internet address family */
    EchoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    EchoServAddr.sin_port = htons(EchoServPort);              /* Local port */

    // Bind to the local address
    Bind(ServSock, (sockaddr*)&EchoServAddr, sizeof(EchoServAddr));

    // Mark the socket so it will listen for incoming connections
    Listen(ServSock, 5);

    signal(SIGCHLD, SigChldHandler); // for preventing zombies

    for(;;){ // Run forever

        // Set the size of the in-out parameter
        socklen_t ClntLen = sizeof(EchoClntAddr);

        // Wait for a client to connect
        ClntSock = Accept(ServSock, (sockaddr*) &EchoClntAddr,&ClntLen);


        //Startin point of new new player to server


        // ClntSock is connected to a client!
        printf("Handling client %s\n", inet_ntoa(EchoClntAddr.sin_addr));

        // Fork child process and report any errors
        if ((ProcessID = fork()) < 0){
            perror("fork() failed");
            exit(1);
        }
        if (ProcessID == 0){   // If this is the child process
            close(ServSock);   // Child closes (deallocates) its parent socket descriptor
            HandleTCPClient(ClntSock);
            exit(1);           // Child process terminates
        }
        printf("With child process: %d\n", (int)ProcessID);
        close(ClntSock);       // Parent closes (deallocates) its child socket descriptor
        ChildProcCount++;      // Increment number of outstanding child processes
    }
    // NOT REACHED
}

void HandleTCPClient(int ClntSocket){
    i++;
    cout<<"Start of handling"<<endl;
    cout<<"i="<<i<<endl;

    char EchoBuffer[RCVBUFSIZE];        // Buffer for echo string
    int RecvMsgSize;                    // Size of received message

    // Receive message from client
    if((RecvMsgSize = recv(ClntSocket, EchoBuffer, RCVBUFSIZE, 0)) < 0){
        perror("recv() failed"); exit(1);
        cout<<"Error"<<endl;
    }

    // Send received string and receive again until end of transmission
    while(RecvMsgSize > 0){ // zero indicates end of transmission

        // Echo message back to client
        if(send(ClntSocket, EchoBuffer, RecvMsgSize, 0) != RecvMsgSize){
            cout<<"Error"<<endl;
            perror("send() failed"); exit(1);
        }
        // See if there is more data to receive
        if((RecvMsgSize = recv(ClntSocket, EchoBuffer, RCVBUFSIZE, 0)) < 0){
            cout<<"Error"<<endl;
            perror("recv() failed"); exit(1);
        }
    }

    close(ClntSocket);    /* Close client socket */
    cout<<"End of handling"<<endl;
}


extern "C" void SigChldHandler( int Signum ){
// Catch SIGCHLD signals so child processes don't become zombies.
    pid_t pid;
    int stat;
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0 );
    return;
}

Output for three messages form client to server:

Handling client 127.0.0.1
With child process: 40830
Start of handling
i=1
Handling client 127.0.0.1
With child process: 40831
Start of handling
i=1
Handling client 127.0.0.1
With child process: 40832
Start of handling
i=1
Handling client 127.0.0.1
With child process: 40833
Start of handling
i=1
End of handling
End of handling
End of handling
End of handling

As you can see it creates three processes and when I close the program it will close socket for each process!!!

> Edit2 Client side is abstracted:

int main()
{
  int Sockfd;
  sockaddr_in ServAddr;
  char ServHost[] = "localhost";
  hostent *HostPtr;
  int Port = SERV_TCP_PORT;
  //int BuffSize = 0;

  //Connection
  // get the address of the host
  HostPtr = Gethostbyname(ServHost);

  if(HostPtr->h_addrtype !=  AF_INET){
     perror("Unknown address type!");
     exit(1);
  }

  memset((char *) &ServAddr, 0, sizeof(ServAddr));
  ServAddr.sin_family = AF_INET;
  ServAddr.sin_addr.s_addr = ((in_addr*)HostPtr->h_addr_list[0])->s_addr;
  ServAddr.sin_port = htons(Port);

//Do some operation

  while(!loop){

      // open a TCP socket
      Sockfd = Socket(AF_INET, SOCK_STREAM, 0);

      // connect to the server
      Connect(Sockfd, (sockaddr*)&ServAddr, sizeof(ServAddr));


//Prepare message to send server


       // write a message to the server
       write(Sockfd, data, sizeof(data));
           int Len = read(Sockfd, data, 522);


//work on the message from server

     }
     close(Sockfd);
}
Bernard
  • 4,240
  • 18
  • 55
  • 88
  • Based on your latest edit, the server seems to be working fine. How about the client? It appears to be sending its messages to ServSock instead of ClntSock, are you sure it's using the corresponding socket on its side instead of just opening a new connection? Client code would be useful. – lgvidal May 22 '14 at 16:03
  • Thanks for your attention. I have uploaded client side as well. – Bernard May 22 '14 at 16:11
  • Well, there you go. Look at that client loop. It asks for a new socket and connects it before each write/read. Using multiple writes and reads for the same connection wouldn't create a new process on the server. – lgvidal May 22 '14 at 16:16

2 Answers2

1

Your client is creating a new socket and connecting it before each write/read, not using the already connected one multiple times. The client should create a socket, connect it to the server and then perform as many write/reads as needed, without creating a new connection.

The server correctly treats each new connection as a new client, and forks to handle it.

Regarding sharing data between forked processes, you could use shared memory as described here.

Community
  • 1
  • 1
lgvidal
  • 329
  • 2
  • 10
  • 1
    Yeah, me neither. Flagged it for moderation. An additional note regarding socket use, remember that each call to _socket()_ will ask the SO for a new socket, not binded or connected to anywhere. Anything that must happen in the same connection must be using the same socket. A single process can have many connections with a single host. – lgvidal May 22 '14 at 17:38
1

The client calls socket and connect for every message it writes

while (...) {
    socket(...);
    connect(...); /* here the server forks a new child process */
    write(...);
}

If you want to avoid that, you must move the connection before the loop

socket(...);
connect(...); /* here the server forks a new child process */
while (...) {
    write(...);
}
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198