TCP Concurrent server, one child process per client

Source: Internet
Author: User
Tags socket error htons

After reading "UNIX Network programming: Volume One", I feel that the author is really a Master of Unix programming. And for me personally, after reading a technical book, must still have to re-write the program (the exchange of content), review the contents of the book (approximate structure, or thought, the same), otherwise, you can not really understand and grasp the point.

Okay, what I brought today is the first of the server models, and also the most basic and most commonly used model –tcp concurrent servers, one child per client process.

A brief introduction: TCP Concurrent Server, each client a child process, that is, the concurrent server calls fork to derive a child process to process each child process, so that the server can serve multiple customers at the same time, one customer per process. The only limit to the number of customers is the limit on how many child processes the operating system can have concurrently with the user ID that runs the server on its behalf.

Specifically to our needs, our client sends an instruction that the server receives. If the service-side requirements are met, then the time is sent back to the client. The requirements are simple and our focus is on the server model.

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 functionSignal (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)//Do not block, return child PID{//Prevent several sub-processes at the same time killed, plus while, know that all killed child processing is done        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);

Notice the big loop here, each time the loop starts with accept, that is, from the connected queue to return one of them, if the return succeeds, print IP, show which IP to connect.

The Inet_ntoa () function is used here, which is a function specifically 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 only reduces the number of references to LISTENFD, and the parent process has a reference.

And then Do_child (CONNFD), here is our custom function to handle the connection, notice that CONNFD is passed to the function. We are talking about this function later.

If you can return from the Do_child () function, print it again, and a certain IP has left.

The last is close (CONNFD), and then exit (0) exits, where close (CONNFD), can be omitted, the child process exits, it will turn off its own open descriptor.

Notice that we also have a signal processing function, Sig_child, as follows:

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

Here our handler function is to print out the PID of the left Subprocess, as shown in the note, in this form:
while (PID = Waitpid ( -1, &stat, Wnohang) > 0) can prevent several sub-processes at the same time killed, plus while, know that all killed child is processed

Here, the main server model has been shown, and now let's look at the main do_child () function that handles client connections.

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 (custom)}Else{//If not, return to Gettime Command Error                snprintf(Buff,sizeof(Buff),"Gettime Command Error"); Writen (SOCKFD, Buff,strlen(buff)); }        }    }}

The processing here is very simple, as I noted in the comments, comparing the first few strings, is not Gettime or Gettime, is the return time, otherwise return 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, the custom function, is used here.
is actually write a considerable number of buff, while preventing 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 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 parameters 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); }}

This is actually read the standard input, sent to the server, and read blocking, and so on after the server sends back, the server sent back to the data to print to the standard output.

In addition, there is a registered signal processing function signal function, previously mentioned, but no code is given.
I directly here in the UNIX network programming signal to take over, 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 model of the server has been completed. All the code has been exposed and tested. You can try it on your own Linux and see if it's OK.

In addition, here is a simple processing request for client and server query times. You can also make other requests for processing. For example, you can communicate with each other, use multi-threading, or try to send files. Just depend on yourself. And, you just need to modify the Do_child () and the STR_CLI () function, and the others can not be modified unless you have other needs.

Finally, the problem of this model has to be talked about. Mainly for each customer fork a sub-process compared to consume CPU time, hundreds of or thousands of of the customer is no problem, but now the daily TCP connections are millions of, there will be problems. Of course, this model is a good choice if the system load is light.

Well, this is all of our blog today, you understand? If I have the wrong place, you are welcome to point out, if you feel good, you 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.