A classic example of concurrent access to a server using a multiple process approach.
Program Implementation function:
1. The client reads a line of text from the standard input and sends it to the server.
2. The server receives the text sent by the client and returns it to the client as is.
3. The client receives the incoming text from the server, outputs it to the standard output, and then continues with the above steps.
Server-side procedures: After a listener socket is established, waits for a client to connect, receives a connection, creates a child process to communicate with the client, and the main process continues to wait for the other client to connect. The code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define Serv_port 1113
#define LISTENQ 32
#define Maxline 1024
/*** Connection processing function ***/
void Str_echo (int fd);
Int
Main (int argc, char *argv[]) {
int LISTENFD,CONNFD;
pid_t Childpid;
Socklen_t Clilen;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
if ((LISTENFD = socket (af_inet, sock_stream,0)) ==-1) {
fprintf (stderr, "Socket Error:%sna", Strerror (errno));
Exit (1);
}
/* Server-side FILLED SOCKADDR structure * *
Bzero (&servaddr, sizeof (SERVADDR));
servaddr.sin_family = af_inet;
SERVADDR.SIN_ADDR.S_ADDR = htonl (Inaddr_any);
Servaddr.sin_port = htons (Serv_port);
/* Bundle LISTENFD Descriptor * *
if (bind (LISTENFD, struct sockaddr*) (&servaddr), sizeof (struct sockaddr)) ==-1) {
fprintf (stderr, "Bind Error:%sna", Strerror (errno));
Exit (1);
}
/* Monitor LISTENFD Descriptor * *
if (Listen (listenfd,5) ==-1) {
fprintf (stderr, "Listen Error:%sna", Strerror (errno));
Exit (1);
}
for (;;) {
Clilen = sizeof (CLIADDR);
/* The server is blocked until the client program establishes the connection * *
if ((Connfd=accept (LISTENFD, (struct sockaddr*) (&cliaddr), &clilen)) ==-1) {
fprintf (stderr, "Accept Error:%sna", Strerror (errno));
Exit (1);
}
After a client has established a connection
if ((Childpid = fork ()) = = 0) {/* Child process/* *
Close (LISTENFD); /* Close the listening socket * *
Str_echo (CONNFD); /* Request to process this client * *
Exit (0);
}
Close (CONNFD);//The parent process closes the connection socket and continues waiting for the arrival of the other connection. * *
}
}
void Str_echo (int sockfd) {
ssize_t N;
Char Buf[maxline];
Again
while ((n = Read (SOCKFD, buf, maxline)) > 0)
Write (SOCKFD, buf, N);
if (N < 0 && errno = = eintr)/is interrupted, reentrant
Goto again;
else if (n < 0) {//Error
fprintf (stderr, "read Error:%sna", Strerror (errno));
Exit (1);
}
}
Client process: Create a connection socket, initiate a connection request to the server, establish the connection, wait for the standard input, after the input completes, sends the input content to the server, then receives the server to send over the content, and will receive the content output to the standard output. The code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define Serv_port 1113
#define Maxline 1024
void Str_cli (FILE *fp, int sockfd);
Int
Main (int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc!= 2) {
fprintf (stderr, "Usage:tcpcli na");
Exit (0);
}
if ((Sockfd=socket (af_inet,sock_stream,0)) ==-1) {
fprintf (stderr, "Socket Error:%sna", Strerror (errno));
Exit (1);
}
/* Customer program to populate the service side of the information * *
Bzero (&servaddr,sizeof (SERVADDR));
Servaddr.sin_family=af_inet;
Servaddr.sin_port=htons (Serv_port);
if (Inet_pton (Af_inet, argv[1], &servaddr.sin_addr) <= 0) {
fprintf (stderr, "Inet_pton Error:%san", Strerror (errno));
Exit (1);
}
/* Client initiated connection request/*
If Connect (sockfd, (struct sockaddr *) (&SERVADDR), sizeof (struct sockaddr)) ==-1) {
fprintf (stderr, "Connect Error:%san", Strerror (errno));
Exit (1);
}
STR_CLI (stdin, SOCKFD); /* Do it all/*
Exit (0);
}
void
STR_CLI (FILE *fp, int sockfd)
{
int nbytes=0;
Char Sendline[maxline],recvline[maxline];
while (Fgets (Sendline, Maxline, FP)!= NULL) {//read one row from standard input
Write (SOCKFD, Sendline, strlen (sendline));//Send the line to the server
if ((Nbytes=read (SOCKFD, recvline, maxline) = = 0) {//Read data from the server from the SOCKFD
fprintf (stderr, "Str_cli:server terminated Prematurelyn");
Exit (1);
}
Recvline[nbytes]= ';
Fputs (Recvline, stdout);
}
}
Run Result:
1. Start the server-side program first.
viidiot@ubuntu$./dissrv & (running in the background)
2. Start a client
viidiot@ubuntu$./DISCLI 127.0.0.1
hello,world! (Client-entered content)
hello,world! (Content returned by the server side)
3. Input command Netstat-at View TCP connection, you can find that the server side and the client has established a connection, while the server's main process is still listening on the 1113 port, waiting for the arrival of other connections.
viidiot@ubuntu$ Netstat–at
TCP 0 0 *:1113 *:* LISTEN
TCP 0 0 localhost.localdom:1113 localhost.localdo:57430 established
TCP 0 0 localhost.localdo:57430 localhost.localdom:1113 established
The client enters EOF to end the communication.
But when we turn on multiple clients to connect to the server to communicate, and then end the communication by pressing EOF, it's interesting to discover from the background that there are a lot of zombie processes. As shown below:
viidiot@ubuntu$ ps-a
PID TTY Time CMD
19403 PTS/1 00:00:00 Dissrv
19405 PTS/1 00:00:00 Dissrv
19423 PTS/1 00:00:00 Dissrv
19434 PTS/1 00:00:00 Dissrv
19441 PTS/1 00:00:00 Dissrv
The reason for the large number of zombie processes is that when the server child process terminates, a SIGCHLD signal is sent to the parent process. But in our code, the signal is not captured, and the default action of this signal is ignored, so in order to avoid the zombie process, we need to capture sigchld to clear the zombie process.