TCP Concurrent server, one child process per client

Source: Internet
Author: User
Tags socket error htons

Today I bring the server model first, which is the most frequently used model of the most basic one –tcp concurrent server, one child process per client.

First of all: TCP concurrent server, each client a child process, concurrent server calls fork to derive a child process to process each sub-process, so that the server can serve multiple customers at the same time, each process a customer.

The only limit to the number of customers is the limit on how many child processes the operating system can have at the same time as the user ID that executes the server on its behalf.

In detail to our needs, our client sends an instruction that the server receives. Assume that the service-side requirements are met. Send the time back to the client.

The demand is very easy and our focus is on the model of the server.

Okay, look at the code:
This is the main code for the server (SERV.C):

#include "pub.h"#define LISTENQ 1024x768voidSig_child (intSigno);//serv <port>intMainintargcChar**ARGV) {intLISTENFD,CONNFD; pid_t Childpid;intOn =1;structSockaddr_in servaddr, cliaddr; Socklen_t Clilen;Char*ptr;if(ARGC! =2)    {fprintf(stderr,"Usage:serv <port>\n");Exit(1); }if(LISTENFD = socket (af_inet, Sock_stream,0)) <0) {perror ("Serv socket error");Exit(-1); }memset(&AMP;SERVADDR,0,sizeof(SERVADDR));    servaddr.sin_family = af_inet; Servaddr.sin_port = htons (Atoi (argv[1])); SERVADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);//SET REUSABLESetSockOpt (LISTENFD, Sol_socket, SO_REUSEADDR, &on,sizeof(on));if(Bind (LISTENFD, (structSOCKADDR *) &servaddr,sizeof(SERVADDR)) <0) {perror ("Serv bind error");Exit(-1); }if(Listen (LISTENFD, Listenq) <0) {perror ("Serv Listen error");Exit(-1); }//Signal processing function    //This is mainly to deal with zombie processSignal (SIGCHLD, sig_child);//The SIGCHLD signal is generated at the end of each sub-process and is ignored by default     for( ; ; ) {Clilen =sizeof(CLIADDR);if(CONNFD = Accept (LISTENFD, (structSOCKADDR *) &cliaddr, &clilen) <0)        {if(errno = = eintr)Continue;Else{Perror ("Serv Accept Error");Exit(-1); }} ptr = Inet_ntoa (CLIADDR.SIN_ADDR);//cliaddr.sin_addr don't have to take an address        fprintf(STDOUT,"%s has connected\n", PTR);if((Childpid = fork ()) = =0)//child{//Note this is another processClose (LISTENFD);//child process shutdown LISTENFDDo_child (CONNFD); ptr = Inet_ntoa (CLIADDR.SIN_ADDR);//cliaddr.sin_addr don't have to take an address            fprintf(STDOUT,"%s has disconnected\n", PTR); Close (CONNFD);Exit(0); } close (CONNFD);//Parent process shutdown CONNFD} close (LISTENFD);Exit(0);}voidSig_child (intSigno) {pid_t pid;intStat while(PID = Waitpid (-1, &stat, Wnohang)) >0)//non-clogging. Back to Child PID{//Prevent a few sub-processes at the same time killed, plus while, know to handle the full killed child        fprintf(STDOUT,"%d terminated\n", PID);    Fflush (stdout); }return;}

Look at the code for this little piece of connection:

sizeof(cliaddr);if((connfd = accept(listenfd, (struct0){    if(errno == EINTR)        continue;    else    {           perror("serv accept error");        exit(-1);    }}ptr = inet_ntoa(cliaddr.sin_addr);//cliaddr.sin_addr不用取地址fprintf(stdout,"%s has connected\n", ptr);

Pay attention to our big loop here. Each cycle starts with an accept, which is the return of one of the connected queues. If the return succeeds, print the IP and show which IP is connected.

The Inet_ntoa () function is used here, which is a function specially prepared for IPV4.

You can also use the Inet_pton () function, which is a common function of IPV4 and IPV6.

Take a look at the following small paragraph:

if0)//child        {        //注意这里是另一个进程            close(listenfd);    //子进程关闭listenfd            do_child(connfd);            ptr = inet_ntoa(cliaddr.sin_addr);//cliaddr.sin_addr不用取地址            fprintf(stdout,"%s has disconnected\n", ptr);            close(connfd);            exit(0);        }

Here is the real fork out of a sub-process.

First Close (LISTENFD), note that close here does not really close listenfd, which is simply a reduction of LISTENFD's one reference count, the parent process another reference.

Then there is Do_child (CONNFD), which is the function we define ourselves to handle the connection. Note that you have passed CONNFD to this function.

We are talking about this function later.

Suppose you can return from the Do_child () function and print it again, one IP has left.

The last is close (CONNFD), then exit (0) exits, here Close (CONNFD), can omit, the child process exits. Will turn off the descriptive descriptors that you have opened.

Notice that we have another signal processing function here, sig_child. For example, see the following:

void sig_child(int signo){    pid_t pid;    int stat;while((pid = waitpid(-10)  //不堵塞。返回child pid{//防止同一时候有几个子进程killed。加while,知道处理全然部killed child    fprintf(stdout,"%d terminated\n",pid);    fflush(stdout);}    return;}

We have previously registered this signal processing function,
Signal processing functions
Signal (SIGCHLD, sig_child); A SIGCHLD signal is generated when each child process terminates, and the default is to ignore

Our processing function here is to print out the PID of the child process left. It's like staring at what you see. In such a form:
while (PID = Waitpid ( -1, &stat, Wnohang)) > 0) is able to prevent a few sub-processes at the same time killed, plus while, know handling the total part killed child

Here, the basic server model has been shown, and now we look at the basic do_child () function that handles the client connection.

Look at the code:

#include "pub.h"voidDo_child (intSOCKFD) {time_t mytime;CharBuff[maxline];intN for( ; ; ) {if(n = read (SOCKFD, Buff,sizeof(buff))) <=0)        {if(N <0&& errno = = eintr)Continue;Else if(n = =0){//hand over to the outside of the main loop processing (to print certain leave)                 Break; }Else{Perror ("Child Read error");Exit(-1); }        }Else{//Compare the first few strings. is not gettime, is the return time. Otherwise, return Gettime Command Error            if((strncmp(Buff,"GETTIME",7) ==0) || (strncmp(Buff,"GetTime",7) ==0) {MyTime = time (NULL);snprintf(Buff,sizeof(Buff),'%s ', CTime (&mytime)); Writen (SOCKFD, Buff,strlen(buff));//It's best to use writen (define yourself)}Else{//If not, return to Gettime Command Error                snprintf(Buff,sizeof(Buff),"Gettime Command Error"); Writen (SOCKFD, Buff,strlen(buff)); }        }    }}

The processing used here is very easy, as I said in my gaze, comparing the first few strings. is not Gettime or Gettime, is the return time, otherwise returns Gettime Command Error

Look here:

mytime = time(NULL);snprintfsizeof"%s"strlen(buff));//这里最好用writen(自己定义)

The current time is printed in the form of a string, in times () and CTime ().

Note that the writen () function is used here. Define the function yourself.
In fact, write a considerable number of buff, at the same time to prevent signal interruption

Look at the code for the Writen function:

#include "pub.h"intWriten (intSOCKFD,Const Char*buff,intN) {intNleft = n;intNcount =0;Const Char*ptr = buff; while(Nleft >0)    {if((ncount = write (SOCKFD, PTR, nleft)) <=0)        {if(errno = = eintr) ncount =0;//call again            Else                return-1;        } nleft-= ncount;    PTR + = ncount; }returnN-nleft;}

Next, we wrote a test-tested client (CLIENT.C):
Look at the code:

#include "pub.h"//client <ip> <port>intMainintargcChar**ARGV) {intSOCKFD;structSockaddr_in servaddr;intNif(ARGC! =3)    {fprintf(stderr,"usage: <ip> <port> \ n");Exit(1); }if(SOCKFD = socket (af_inet, Sock_stream,0)) <0) {perror ("Client socket Error");Exit(-1); }memset(&AMP;SERVADDR,0,sizeof(SERVADDR));    servaddr.sin_family = af_inet; Servaddr.sin_port = htons (Atoi (argv[2])); Inet_aton (argv[1], &servaddr.sin_addr);if(Connect (SOCKFD, (structSOCKADDR *) &servaddr,sizeof(SERVADDR))) <0) {perror ("Client connect Error");Exit(-1); } strcli (SOCKFD);Exit(0);}

Here is the simple client code, passing in the number of connections to connect the IP and port, the function to send the request is STR_CLI ().

Look at the STR_CLI function:

#include"Pub. hThe Voidstrcli (int sockfd){CharSendbuff[MAXLINE], Recvbuff[MAXLINE];While (fgets (Sendbuff, sizeof (Sendbuff), stdin) ! = NULL){writen (SOCKFD, Sendbuff, strlen (sendbuff));if (read (SOCKFD, Recvbuff, sizeof (Recvbuff)) = = 0)            fprintf (stderr,"server has terminated\n");fputs (Recvbuff, stdout); }}

The fact here is to read the standard input and send it to the server. and read blocked, and so on after the server sends back. Prints the data sent back by the server to the standard output.

In addition, another register signal processing function signal function, previously mentioned. But the code is not given.
I just took the signal from UNIX network programming here. can be used directly.
Also take a look, signal.c:

/ * include signal * /#include "pub.h"Sigfunc *Signal(intSigno, Sigfunc *func) {structSigaction Act, oact;    Act.sa_handler = func;    Sigemptyset (&act.sa_mask); Act.sa_flags =0; if (Signo = = SIGALRM) {#ifdef Sa_interruptAct.sa_flags |= Sa_interrupt;/ * SunOS 4.x * /#endif}Else{#ifdef Sa_restartAct.sa_flags |= Sa_restart;/ * SVR4, 44BSD * /#endif} if (Sigaction (Signo, &act, &oact) <0)return(Sig_err);return(Oact.sa_handler);}/ * END signal * /Sigfunc *signal (intSigno, Sigfunc *func)/* for our signal () function */{Sigfunc *sigfunc; if (Sigfunc =Signal(Signo, Func)) = = Sig_err) {perror ("Signal Error"); Exit1); }return(Sigfunc);}

At this point, our first server model is complete. All the code has been out of the sun. and after testing. You can try it on your own Linux. Look at the line.

In addition, here is a simple client and server query time processing request. You can also make other requests for processing. For example, two-party communication is possible. Use multithreading, or try to send files, all can.

Just up and you just need to change the Do_child () and STR_CLI () functions. The rest will not change unless you have other needs.

At last. We have to talk about the problem with this model. The main thing is to fork a child process for each customer is more CPU time, hundreds of or thousands of of the customer is not a problem, but today's daily TCP connections are millions. There's going to be a problem here. Of course, assuming the system load is light, such a model is a good choice.

All right. This is the whole content of our blog today, do you understand? Suppose I have something wrong, and I welcome you to point out. Suppose to think well. can also praise Oh.

TCP Concurrent server, one child process per client

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.