Process Model server and model server
Process Model server-side cultivation mainly includes the following realm
1. Each process corresponds to a connection
2. Pre-create a certain number of processes. When connected, take a process to deal with it. I personally call it a static process pool.
3. create a certain number of processes first. When connected, take a process to deal with them. If there is no process, try to create a new process. When there are too many processes, close some processes, this is a dynamically adjusted process pool model.
4. Combined use with other models, such as thread models and IO reuse Models
This article proposes the solution of the first and second realm.
This article includes the first and second points, and the last two points involve a little more knowledge points. The application is unavailable for the time being.
Process-connection: fork processes each connection to process the connection. After the process is completed, the child process is exited.
Advantages: simple and simple
Disadvantages: inefficient, concurrent access, and a large number of connections.
For such models, the code is relatively easy. The server side is as follows:
# Include <stdio. h> # include <string. h> # include <stdlib. h> # include <netinet/in. h> # include <sys/socket. h> # include <errno. h> # define MAX_BUF 1024int setup (char * ip, int port) {/* variable */int sock_fd, connect_fd; struct sockaddr_in server, client; int ret; /* socket */sock_fd = socket (PF_INET, SOCK_STREAM, 0); if (sock_fd <0) {perror ("socket failed"); exit (1);} server. sin_family = PF_INET; // server. Sin_addr.s_addr = INADDR_ANY; server. sin_port = htons (port); if (inet_ton (PF_INET, ip, & server. sin_addr) <0) {perror ("inet_ton"); exit (1);}/* bind */ret = bind (sock_fd, (struct sockaddr *) & server, sizeof (server); if (ret <0) {perror ("bind failed"); exit (1) ;}/ * listen */if (listen (sock_fd, 5) <0) {perror ("listen failed \ n"); exit (1);} return sock_fd;} // Process Model void process_mode (int sock_fd, int Connect_fd) {char buff [MAX_BUF]; int ret =-1; pid_t pid; pid = fork (); if (pid <0) {perror ("fork error "); exit (errno);} // sub-process else if (pid = 0) {close (sock_fd); if (ret = recv (connect_fd, buff, sizeof (buff ), 0) <0) {perror ("recv"); exit (1);} else if (ret = 0) printf ("read end \ n "); else {fprintf (stderr, "receive message % s retval: % d \ n", buff, ret) ;}close (connect_fd); exit (0) ;} total_count ++; c Lose (connect_fd);} int main (int argc, char ** argv) {int connect_fd; if (argc! = 3) {fprintf (stderr, "usage <ip> <port>"); exit (-1) ;}// setup int sock_fd = setup (argv [1], atoi (argv [2]); printf ("network setup successfully. \ nip: % s port: % s \ n ", argv [1], argv [2]);/* accept */while (1) {connect_fd = accept (sock_fd, (struct sockaddr *) NULL, NULL); process_mode (sock_fd, connect_fd);} return 0 ;}
It is worth noting that after fork, the originally bound socket should be closed, and the parent process should close the connection socket, which is the impact of fork. disabling the descriptor will not cause resource destruction, as long as the descriptor reference is not 0
To overcome the inefficiency of the above model, you can fork a certain number of processes in advance. When the connection is established, you do not have to fork again. After the customer request is processed, it is not exited, instead, wait for the request. This generates a static process pool.
Static thread pool implementation is relatively simple.The difficulty is to prevent group alarms during design. The so-called surprise group is similar to a group of pigeons eating, and you ran all the pigeons. Locks are required to prevent group alarms.
This type of model has the advantage of avoiding the reduction in efficiency caused by fork.
The disadvantage is that the efficiency is not high enough. When there are insufficient processes in the process pool, the number of processes in the Pool cannot be dynamically adjusted. When there are few connections, there are too many processes in the pool, which is also a waste.
The specific implementation is as follows:
# Include <stdio. h> # include <string. h> # include <stdlib. h> # include <netinet/in. h> # include <sys/socket. h> # include <errno. h> # include <signal. h> # include <fcntl. h> # define MAX_BUF 1024/* Static pool */typedef struct static_pool {int nchild; pid_t * pids;} spool;/* Server structure */typedef struct Server {} Server, * pServer; spool pool;/* start */int setup (char * ip, int port) {/* variable */int sock_fd, connect_fd; struct sockaddr _ In server, client; int ret;/* socket */sock_fd = socket (PF_INET, SOCK_STREAM, 0); if (sock_fd <0) {perror ("socket failed "); exit (1);} server. sin_family = PF_INET; // server. sin_addr.s_addr = INADDR_ANY; server. sin_port = htons (port); if (inet_ton (PF_INET, ip, & server. sin_addr) <0) {perror ("inet_ton"); exit (1);}/* bind */ret = bind (sock_fd, (struct sockaddr *) & server, sizeof (server); if (ret <0) {perror ("bind failed"); exit (1);}/* listen */if (listen (sock_fd, 5) <0) {perror ("listen failed \ n"); exit (1);} return sock_fd;} // signal int int3 interrupt void sig_call_back (int signo) {int I; for (I = 0; I <pool. nchild; I ++) kill (pool. pids [I], SIGTERM); while (wait (NULL)> 0); if (errno! = ECHILD) {perror ("wait"); exit (errno);} exit (0);} // encapsulate the lock operation, reference: Unix Network Programmingstruct flock lock_it, unlock_it; int lock_fd =-1;/* initialization information */void my_lock_init (char * pathname) {char lock_file [1024]; strncpy (lock_file, pathname, sizeof (lock_file )); lock_fd = mkstemp (lock_file); if (lock_fd <0) {perror ("mkstemp"); exit E = SEEK_SET; lock_it.l_start = 0; lock_it.l_len = 0; rows = F_UNLCK; rows = SEEK_SET; unlock_it.l_start = 0; rows = 0;}/* Lock wait */void my_lock_wait () {int rc; while (rc = fcntl (lock_fd, F_SETLKW, & lock_it) <0) {if (errno = EINTR) continue; else {perror ("fcntl"); exit (errno) ;}}/ * release lock */void my_lock_release () {if (fcntl (lock_fd, F_SETLKW, & unlock_it) <0) {perr Or ("fcntl"); exit (errno) ;}/ * process the request. This field is empty */void process (int connect_fd) {}/* Waiting For request */void child_loop (int sock_fd) {int connect_fd; socklen_t client_len; struct sockaddr_in client; memset (& client, 0, sizeof (client )); while (1) {client_len = sizeof (struct sockaddr_in); my_lock_wait (); connect_fd = accept (sock_fd, (struct sockaddr *) & client, & client_len ); printf ("process % d deal with connnector \ n", getp Id (); process (connect_fd); close (connect_fd); my_lock_release () ;}/ * generates sub-processes, the sub-process receives and processes the request */int make_child (int sock_fd) {pid_t pid; if (pid = fork ()> 0) return pid; child_loop (sock_fd );} /* pre-fork */void preprocess (int sock_fd, int n) {int I = 0; pool. nchild = n; pool. pids = (pid_t *) malloc (sizeof (pid_t) * n); if (pool. pids = NULL) {perror ("malloc"); exit (-1) ;}// Aliwa my_lock_init ("/tmp/lock. XXXXXX "); For (I = 0; I <n; I ++) pool. pids [I] = make_child (sock_fd);} int main (int argc, char ** argv) {if (argc! = 4) {fprintf (stderr, "usage <ip> <port> <process num>"); exit (-1 );} // setup int sock_fd = setup (argv [1], atoi (argv [2]); printf ("network setup successfully. \ nip: % s port: % s \ n ", argv [1], argv [2]); preprocess (sock_fd, atoi (argv [3]); signal (SIGINT, sig_call_back); for (;) pause (); return 0 ;}
The code is not difficult, but there are a lot of notes. One is locking, and the other is killing all child processes at the end of the parent process to avoid orphan processes.
The client example used for testing is applicable to both of the above two models.
#include <stdio.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <netdb.h>#include <netinet/in.h>#include <sys/socket.h>#define LEN(str) (sizeof(char)*strlen(str))void connect_server(char *ip, int port){ /* variable */ int sock_fd; struct sockaddr_in server; char buff[1024]; int ret; /* socket */ sock_fd = socket(PF_INET, SOCK_STREAM, 0); if(sock_fd < 0) { perror("socket failed"); exit(1); } server.sin_family = PF_INET; server.sin_port = htons(port); if(inet_pton(PF_INET, ip, &server.sin_addr) < 0){ perror("inet_pton"); exit(1); } /* connect */ if((ret = connect(sock_fd, (struct sockaddr*)&server, sizeof(server)) )< 0){ perror("connect failed"); exit(1); } /* send buff */ sprintf(buff, "Hello World"); if(( ret = send(sock_fd, buff, LEN(buff), 0)) < 0){ perror("send"); exit(1); } printf("send msg\n"); /* close */ close(sock_fd);}int main(int argc, char **argv){ int i; if(argc < 4){ perror("usage<ip><port><connect count>"); exit(-1); } for(i = 0; i< atoi(argv[3]); i++) connect_server(argv[1], atoi(argv[2]) ); return 0;}