linux c实现的服务器与客户端模型(使用TCP,多进程)

时间:2021-05-10 14:58:10

以下是服务端的代码:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 #include <netinet/in.h>
  7 #include <arpa/inet.h>
  8 #include <unistd.h>
  9 #include <errno.h>
 10 #include <signal.h>
 11 #include <sys/socket.h>
 12 
 13 #define EXIT_CODE 1
 14 
 15 void handler (int sig);
 16 
 17 int main () 
 18 {
 19     int server_fd;
 20     int client_fd;
 21     int result;
 22     struct sockaddr_in addr_server;
 23     struct sockaddr_in addr_client;
 24     int len_server;
 25     int len_client;
 26     char *message = "this message comes from aliyun\n";
 27     pid_t pid_child;
 28     struct sigaction action;
 29 
 30     /*
 31      *when the child processes exit,
 32      *it will product a SIGCHLD signal,
 33      *the parent process will catch it,
 34      *and run handler function to recycle resource.
 35      */
 36      action.sa_handler = handler;
 37      sigemptyset (&action.sa_mask);
 38      action.sa_flags = 0;
 39 
 40      if (sigaction (SIGCHLD,&action,NULL) == -1) {
 41          perror ("sigaction");
 42          exit (EXIT_CODE);
 43      }
 44 
 45     /*create a socket*/
 46     server_fd = socket (AF_INET,SOCK_STREAM,0);
 47     if (server_fd == -1) {
 48         perror ("socket");
 49         exit (EXIT_CODE);
 50     }
 51 
 52     if (setsockopt (server_fd,
 53                 SOL_SOCKET,
 54                 SO_REUSEADDR,
 55                 &result,
 56                 sizeof (result)) < 0) {
 57         perror ("setsockopt");
 58         exit (EXIT_CODE);
 59     }
 60     
 61     /*set ip address and port*/
 62     addr_server.sin_family = AF_INET;
 63     addr_server.sin_addr.s_addr = htonl (INADDR_ANY);
 64     addr_server.sin_port = htons (9375);
 65     
 66     /*bind a socket with address*/
 67     len_server = sizeof (addr_server);
 68     if (bind (server_fd,
 69                 (struct sockaddr *) &addr_server,
 70                 len_server) == -1) 
 71     {
 72         perror ("bind");
 73         exit (EXIT_CODE);
 74     }
 75 
 76     /*create pending queue and set pending number*/
 77     if (listen (server_fd,20) == -1) {
 78         perror ("listen");
 79         exit (EXIT_CODE);
 80     }
 81     
 82     while (1) {
 83         len_client = sizeof (addr_client);
 84         /*
 85          *the system call accept will be interrupted by signal in linux,
 86          *and the erron will be set to EINTR,
 87          *so if the system call accept runs failed,
 88          *we should recall it inorder to clear the effect of signal.
 89          */
 90 
 91          /*
 92           *in this program, when a child process returns,
 93           *the child process will send a signal named SIGCHLD to parent process
 94           *and the signal parent process received will affect the system call accept
 95           */
 96         while (1) {
 97             client_fd = accept (server_fd,
 98                 (struct sockaddr *) &addr_client,
 99                 &len_client);
100             if (client_fd == -1 && errno == EINTR) {
101                 continue;
102             } else if (client_fd > 0) {
103                 break;
104             } else {
105                 perror ("connect");
106                 exit (EXIT_CODE);
107             }
108         }
109         
110         pid_child = fork ();
111         if (pid_child > 0) {
112             /*this is parent*/
113             sleep (5);
114             close (client_fd);
115             continue;
116         } else {
117             /*this is child*/
118             char buffer[4096];
119             int connect_fd = client_fd;
120             struct sockaddr_in *p_addr = (struct sockaddr_in *) malloc (sizeof (addr_client));
121             memcpy (p_addr,&addr_client,len_client);
122             result = read (connect_fd,buffer,4095);
123             if (result == -1) {
124                 printf ("error in reading data\n");
125                 exit (EXIT_CODE);
126             } else if (result == 0) {
127                 printf ("there is no data comes\n");
128                 buffer[0] = '\0';
129             } else {
130                 printf ("client says:%s\n",buffer);
131                 buffer[0] = '\0';
132             }
133 
134             int count = 5;
135             while (count --) {
136                 write (connect_fd,"welcome to aliyun\n",18);
137                 sleep (1);
138             }
139 
140             printf ("complete!\n");
141             printf ("child process %d exit\n",getpid ());
142             close (connect_fd);
143             break;
144         }
145     }
146 
147     return 0;
148 }
149 
150 void handler (int sig) 
151 {
152     pid_t pid;
153     if ((pid = waitpid (0,NULL,0)) == (pid_t) -1) {
154         perror ("wait");
155         exit (EXIT_CODE);
156     } else {
157         printf ("SIGCHLD HANDLER:already recycle child process:%d\n",pid);
158     }
159 }
160 
161             
162             

以下是客户端代码:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <arpa/inet.h>
 4 #include <sys/socket.h>
 5 #include <netinet/in.h>
 6 #include <sys/types.h>
 7 #include <unistd.h>
 8 #include <errno.h>
 9 #include <string.h>
10 
11 #define EXIT_CODE 1
12 
13 int main () 
14 {
15     int sockfd;
16     int len = 0;
17     struct sockaddr_in address;
18     int result = 0;
19     char ch = 'A';
20     char buffer[4096];
21 
22     printf ("this is only a test between fedora and aliyun\n");
23 
24     /*create a socket*/
25     sockfd = socket (AF_INET,SOCK_STREAM,0);
26     if (sockfd == -1) {
27         perror ("socket");
28         exit (EXIT_CODE);
29     }
30     
31     /*set up ip address and communication port*/
32     address.sin_family = AF_INET;
33     address.sin_addr.s_addr = inet_addr ("127.0.0.1");
34     address.sin_port = htons (9375);
35     
36     /*get communication with server port*/
37     len = sizeof (address);
38     if (connect (sockfd,(struct sockaddr*) &address,len) < 0) {
39         perror ("connect");
40         exit (EXIT_CODE);
41     }
42     
43     /*display having connected with server*/
44     printf ("%s\n","connected with 127.0.0.1");
45     printf ("Now please input your message!\n");
46     
47     /*communicate with server*/    
48     /*
49     while (1) {
50 
51         if (fgets (buffer,4095,stdin) == NULL) {
52             buffer[0] = '\0';
53             fflush (stdin);
54             printf ("an error occcured while input characters!\n");
55             printf ("please input again\n");
56         } else {
57             break;
58         }
59     } */
60     strcpy (buffer,"hello aliyun");
61     write (sockfd,buffer,strlen (buffer));
62 
63     while (1) {
64         result = read (sockfd,buffer,4095);
65         if (result == -1) {
66             printf ("server port has closed connection\n");
67             break;
68         } else if (result == 0) {
69            break;
70         } else {
71             printf ("msg from 127.0.0.1:%s\n",buffer);
72         }
73     }
74     close (sockfd);
75     printf ("complete!\n");
76     return 0;
77 }

写了一个极简的Makefile:
client:client.c 
    gcc -g client.c -o client
server:server.c
    gcc -g server.c -o server

 

但是在执行的时候,可能有以下问题:

1.首先执行./server

2.然后执行./client& ./client & ./client& 

虽然服务程序创建的子进程已经退出,但是客户端无法返回到termial中,有哪位大神知道这是为什么,请指示。

在编写此代码主要收获:

1.子进程结束的时候会产生SIGCHLD信号,父进程要捕捉此信号,执行waitpid操作,否则会产生僵尸进程,而且越来越多。

2.accept是慢系统调用,会被信号中断,并且设置errno为EINTR,代码中需要排除accept因为进程捕捉到信号而被中断的情况。

 

 

代码中还有什么问题,请各位看官多多指教。