Multi-process-based network chat program

Source: Internet
Author: User
Tags epoll unix domain socket htons

Reference: Linux high-performance Server programming, game double

Program Description: This program uses the shared memory to synchronize between processes. Because it only reads shared memory at the same time, no locks are used. The function of this program is that the server listens to network connections. When a client connects, the server creates a sub-process to process the connection. Each sub-process is only responsible for communicating with its own client and its parent process. When the sub-process reads data from the client and puts the data in the shared memory, each sub-process has its own space in the shared memory, so no simultaneous writing occurs. The parent process is notified after it is put. The new data in the shared memory reaches, and the parent process notifies other sub-processes to read data and send the data to their clients, achieve the effect of group chat. This program is a good example for beginners of multi-process programming. Write it down to familiarize themselves with it.


Server code: added the-LRT option during compilation.

# Include <sys/socket. h> # include <netinet/in. h> # include <ARPA/inet. h> # include <assert. h> # include <stdio. h> # include <unistd. h> # include <errno. h> # include <string. h> # include <fcntl. h> # include <stdlib. h> # include <sys/epoll. h> # include <signal. h> # include <sys/Wait. h> # include <sys/Mman. h> # include <sys/STAT. h> # include <fcntl. h> # define user_limit 5 # define buffer_size 1024 # define fd_limit 65535 # define max_eve Nt_number 1024 # define process_limit 65536/* process required data for a client connection */struct client_data {sockaddr_in address; int connfd;/* FD of the client */pid_t PID; /* PID of the sub-process that processes the connection */INT pipefd [2];/* pipeline used to communicate with the parent process */}; int sig_pipefd [2]; // when a signal occurs, it is used for the parent process's own communication char * share_mem; int user_count = 0; // The number of current customers client_data * Users = 0; int * sub_process = 0; static const char * shm_name = "/my_assist_memory"; int maxevents = 100; bool stop_child = False; void setnonblock (int fd) {int flag = fcntl (FD, f_getfl); Assert (flag! =-1); fcntl (FD, f_setfl, flag | o_nonblock);} void addfd (INT epollfd, int FD) {epoll_event EE; EE. data. FD = FD; EE. events = epollin | epollet; epoll_ctl (epollfd, epoll_ctl_add, FD, & EE); setnonblock (FD);} void sig_handler (INT sig) {int save_errno = errno; int MSG = SIG; send (sig_pipefd [1], (char *) & MSG, 1, 0); errno = save_errno; // restore error value} void child_sig_handler (INT sig) {stop_child = true;} void addsig (INT Sig, void (* handl Er) (INT), bool restart = true) {struct sigaction SA; memset (& SA, '\ 0', sizeof (SA); SA. sa_handler = handler; If (restart) SA. sa_flags | = sa_restart; sigfillset (& SA. sa_mask); // What is the role? Assert (sigaction (SIG, & SA, null )! =-1);}/* sub-process's processing function. idx indicates the number of client connections processed by the sub-process, and users indicates the array of all client connection data, memory _ mem indicates the starting address of the shared memory */INT run_child (INT idx, client_data * users, char * 1__mem) {int connfd = users [idx]. connfd; int pipefd = users [idx]. pipefd [1]; int epollfd = epoll_create (100); // The event processing function assert (epollfd! =-1); addfd (epollfd, connfd); // communicates with the client addfd (epollfd, pipefd); // communicates with the parent process addsig (sigterm, child_sig_handler, false ); epoll_event events [maxevents]; int ret; while (! Stop_child) {int number = epoll_wait (epollfd, events, maxevents,-1); If (number <0 & errno! = Eintr) {printf ("epoll error \ n"); break;} int I; for (I = 0; I <number; I ++) {int sockfd = events [I]. data. FD; If (sockfd = connfd & (events [I]. events & epollin) // client sends data {memset (memory _mem + idx * buffer_size, '\ 0', buffer_size);/* reads client data to the corresponding read cache, the read cache is a segment of shared memory */ret = Recv (sockfd, share_mem + idx * buffer_size, BUFFER_SIZE-1, 0); If (Ret <0 & errno! = Eagain) {printf ("Recv error \ n"); stop_child = true;} else if (ret = 0) {printf ("client close \ n "); stop_child = true;} else {send (pipefd, (char *) & idx, sizeof (idx), 0); // tell the parent process, "I" received data}/* parent process notification "I" sent the data from the client to the client in charge */else if (sockfd = pipefd &&( events [I]. events & epollin) {int client = 0; ret = Recv (sockfd, (char *) & client, sizeof (client), 0 ); if (Ret <0 & errno! = Eagain) stop_child = true; else if (ret = 0) stop_child = true; else {send (connfd, 1__mem + client * buffer_size, buffer_size, 0 );}}}} close (connfd); close (pipefd); close (epollfd); Return 0;} int main (INT argc, char * argv []) {If (argc! = 3) {printf ("usage % s server_ip server_port \ n", basename (argv [0]); Return-1;} sockaddr_in server; server. sin_family = af_inet; inet_ton (af_inet, argv [1], & server. sin_addr); server. sin_port = htons (atoi (argv [2]); int listenfd = socket (af_inet, sock_stream, 0); Assert (listenfd! =-1); int opt = 1; int ret = setsockopt (listenfd, sol_socket, so_reuseaddr, & OPT, sizeof (OPT); Assert (ret = 0 ); ret = BIND (listenfd, (const sockaddr *) & server, sizeof (server); Assert (Ret! =-1); ret = listen (listenfd, 100); Assert (Ret! =-1);/* initialize the connection pool */user_count = 0; users = new client_data [user_limit + 1]; sub_process = new int [process_limit]; int I; for (I = 0; I <process_limit; ++ I) {sub_process [I] =-1;}/* epoll initialization */INT epollfd = epoll_create (100 ); assert (epollfd! =-1); addfd (epollfd, listenfd); // listener network connection port ret = socketpair (af_unix, sock_stream, 0, sig_pipefd); // when a signal occurs, used for the parent process's own communication assert (Ret! =-1); setnonblock (sig_pipefd [1]); // port 0 of the UNIX domain socket is used by the signal processing function addfd (epollfd, sig_pipefd [0]); // The main process listens to Port 1 of the UNIX domain socket/* sets the signal processing function */addsig (sigchld, sig_handler); addsig (sigpipe, sig_ign); addsig (SIGINT, sig_handler); addsig (sigterm, sig_handler);/* create shared memory for read cache of all client socket connections */INT shmfd = shm_open (shm_name, o_creat | o_rdwr, 0666 ); assert (shmfd! =-1); ret = ftruncate (shmfd, user_limit * buffer_size); // set the shmfd size assert (Ret! =-1); pai_mem = (char *) MMAP (null, user_limit * buffer_size, prot_read | prot_write, map_shared, shmfd, 0); Assert (pai_mem! = Map_failed); close (shmfd);/* enters epoll event loop */bool stop_server = false; bool terminate = false; epoll_event events [maxevents]; while (! Stop_server) {int number = epoll_wait (epollfd, events, maxevents,-1); If (number <0 & errno! = Eintr) {printf ("epoll error \ n"); break ;}for (I = 0; I <number; I ++) {int sockfd = events [I]. data. FD;/* new client connection */If (sockfd = listenfd) {sockaddr_in client; socklen_t clilen = sizeof (client); int connfd = accept (listenfd, (struct sockaddr *) & client, & clilen); If (connfd <0) {printf ("Accept error \ n"); continue;} If (user_count> = user_limit) {const char * info = "to initialize Users \ n"; printf ("% s \ n", Info); send (connfd, Info, Strlen (Info), 0); close (connfd); continue;}/* save data related to the user_count client connection */users [user_count]. address = client; users [user_count]. connfd = connfd; ret = socketpair (af_unix, sock_stream, 0, users [user_count]. pipefd); Assert (Ret! =-1); pid_t pid = fork (); If (PID <0) {close (connfd); continue;} If (pid = 0) // subprocess {close (sig_pipefd [0]); close (sig_pipefd [1]); close (users [user_count]. pipefd [0]); // The sub-process closes port 0 close (listenfd); close (epollfd); run_child (user_count, users, ipv_mem ); // The sub-process's processing function munmap (void *) handle _mem, user_limit * buffer_size); exit (0);} else // parent process {close (users [user_count]. pipefd [1]); // The parent process closes Port 1 close (connfd); addfd (epollfd, users [user_coun T]. pipefd [0]); users [user_count]. PID = PID; sub_process [pid] = user_count; user_count ++ ;}} /* process signal events */else if (sockfd = sig_pipefd [0] & events [I]. events & epollin) {int SIG; char signals [1024]; ret = Recv (sockfd, signals, sizeof (signals), 0); If (Ret <0 & RET! = Eagain) {printf ("Recv error \ n"); continue;} If (ret = 0) continue; for (I = 0; I <ret; ++ I) {Switch (signals [I]) {Case sigchld: // sub-process close {pid_t PID; int status; while (pid = waitpid (-1, & status, wnohang)> 0) {/* use the subprocess PID to obtain the number of the closed client connection */INT del_user = sub_process [pid]; sub_process [del_user] =-1; /* identify the data used by the del_user customer connection */epoll_ctl (epollfd, epoll_ctl_del, users [del_user]. pipefd [0], 0); close (users [del_user]. pi Pefd [0]);/* move the last client connection information to this location to ensure that ~ The direct connection to the user_count-1 is alive */users [del_user] = users [-- user_count]; sub_process [users [del_user]. PID] = del_user;} If (terminate & user_count = 0) stop_server = true; break;} case SIGINT: Case sigterm: // end the server process {printf ("kill all the child now \ n"); for (I = 0; I <user_count; ++ I) {pid_t pid = users [I]. PID; kill (PID, sigterm);} terminate = true; // here the stop_sever is not used to wait for all sub-processes to end break;} default: break ;}}} /* a child process enters the parent The program writes data */else if (events [I]. events & epollin) {int child; ret = Recv (sockfd, (char *) & Child, sizeof (child), 0); If (Ret <0 & errno! = Eagain) continue; else if (ret = 0) continue; printf ("read data from child accross PIPE \ n"); for (I = 0; I <user_count; I ++) {if (I! = Child) {printf ("send data to child accross PIPE \ n"); send (users [I]. pipefd [0], (char *) & Child, sizeof (child), 0) ;}}}close (listenfd); close (epollfd ); close (sig_pipefd [0]); close (sig_pipefd [1]); shm_unlink (shm_name); Delete [] users; Delete [] sub_process; return 0 ;}


Client code (relatively simple, without comments ):
#define _GNU_SOURCE 1#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <assert.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <poll.h>#include <fcntl.h>#define BUFFER_SIZE 64int main( int argc, char* argv[] ){    if( argc <= 2 )    {        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );        return 1;    }    const char* ip = argv[1];    int port = atoi( argv[2] );    struct sockaddr_in server_address;    bzero( &server_address, sizeof( server_address ) );    server_address.sin_family = AF_INET;    inet_pton( AF_INET, ip, &server_address.sin_addr );    server_address.sin_port = htons( port );    int sockfd = socket( PF_INET, SOCK_STREAM, 0 );    assert( sockfd >= 0 );    if ( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )    {        printf( "connection failed\n" );        close( sockfd );        return 1;    }    pollfd fds[2];    fds[0].fd = 0;    fds[0].events = POLLIN;    fds[0].revents = 0;    fds[1].fd = sockfd;    fds[1].events = POLLIN | POLLRDHUP;    fds[1].revents = 0;    char read_buf[BUFFER_SIZE];    int pipefd[2];    int ret = pipe( pipefd );    assert( ret != -1 );    while( 1 )    {        ret = poll( fds, 2, -1 );        if( ret < 0 )        {            printf( "poll failure\n" );            break;        }        if( fds[1].revents & POLLRDHUP )        {            printf( "server close the connection\n" );            break;        }        else if( fds[1].revents & POLLIN )        {            memset( read_buf, '\0', BUFFER_SIZE );            int len = recv( fds[1].fd, read_buf, BUFFER_SIZE-1, 0 );int i;    for(i = 0;i<len;i++)printf("%c",read_buf[i]);        }        if( fds[0].revents & POLLIN )        {            ret = splice( 0, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );            ret = splice( pipefd[0], NULL, sockfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );        }    }        close( sockfd );    return 0;}


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.