Linux平台C语言Socket编程练习之多进程服务器

0x00 要求

采用多进程并发服务器技术,服务器可以同时接受多个客户的请求。具体要求如下:

服务端

1
2
3
接收并显示与之连接的客户端的名称;
接收客户端发来的字符串,显示出来,并对字符串做反转处理,最后将处理后的字符串发回给客户。

客户端

1
2
3
4
5
6
7
根据客户输入的服务器IP地址,向服务器发起建立连接的请求;
接收客户输入的客户端名称,并把该客户端名称发给服务器;
接收客户输入的字符串,将字符串发送给服务器;
接收服务器发回的反转处理后的字符串并显示。继续接受客户输入的字符串,直到用户输入quit时退出。

0x01 代码

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 100
void pro_cli(int connfd,struct sockaddr_in client);
int main()
{
int listenfd;
int connfd;
socklen_t client_len;
pid_t pid;
struct sockaddr_in server;
struct sockaddr_in client;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket() failed.");
exit(1);
}
int opt=SO_REUSEADDR;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(listenfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("bind() failed.");
exit(1);
}
if(listen(listenfd,BACKLOG)==-1)
{
perror("listen() failed.");
exit(1);
}
client_len=sizeof(client);
while(1)
{
if(((connfd=accept(listenfd,(struct sockaddr *)&client,&client_len))==-1))
{
perror("accept() failed.");
exit(1);
}
if((pid=fork())>0)
{
close(connfd);
continue;
}
else if(pid==0)
{
close(listenfd);
pro_cli(connfd,client);
exit(0);
}
else
{
printf("fork() failed.");
exit(0);
}
close(listenfd);
}
return 0;
}
void pro_cli(int connfd,struct sockaddr_in client)
{
ssize_t send_num;
ssize_t recv_num;
char send_buf[MAXDATASIZE];
char recv_buf[MAXDATASIZE];
char client_name[MAXDATASIZE];
char wel_msg[]="[!] Welcome,you can input 'quit' to exit :) ";
printf("[!] You got a connection from client IP: <%s> PORT: <%d> \n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if((send(connfd,wel_msg,strlen(wel_msg),0))==-1)
{
perror("send welcome messages fail.");
exit(1);
}
if((recv_num=recv(connfd,client_name,MAXDATASIZE,0))==-1)
{
perror("recv() failed.");
close(connfd);
return;
}
if(recv_num==0)
{
printf("[!] Client disconnected.\n");
close(connfd);
}
client_name[recv_num-1]='\0';
printf("[*] Client name: <%s> \n",client_name);
while(1)
{
if((recv_num=recv(connfd,recv_buf,MAXDATASIZE,0))==-1)
{
perror("recv() error.");
exit(1);
}
recv_buf[recv_num-1]='\0';
printf("[*] Received string: '%s' from client: <%s> \n ",recv_buf,client_name);
size_t len=strlen(recv_buf);
for(int i=0; i<(len/2); ++i)
{
char tmp=recv_buf[i];
recv_buf[i]=recv_buf[len-1-i];
recv_buf[len-1-i]=tmp;
}
strcpy(send_buf,recv_buf);
printf("[>] Send reverse string: '%s' to client: <%s> \n",send_buf,client_name);
if((send_num=send(connfd,send_buf,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
if(!strcmp(send_buf,"tiuq"))
{
printf("[!] Client <%s> have disconnected! \n",client_name);
break;
}
}
close(connfd);
return;
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 1234
#define MAXDATASIZE 100
int main(int argc,char *argv[])
{
int sockfd;
ssize_t send_num;
ssize_t recv_num;
char wel_msg[MAXDATASIZE];
char cli_name[MAXDATASIZE];
char recv_buf[MAXDATASIZE];
char send_buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server;
if(argc!=2)
{
printf("Usage:%s <IP Adress>\n",argv[0]);
exit(1);
}
if((he=gethostbyname(argv[1]))==NULL)
{
printf("gethostbyname() failed.");
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() failed.");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr=*((struct in_addr *)he->h_addr);
if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
printf("connect() failed.");
exit(1);
}
if ((recv(sockfd,wel_msg,MAXDATASIZE,0))==-1)
{
perror("[!] Receive welcome message fail.");
exit(1);
}
wel_msg[sizeof(wel_msg)]='\0';
printf("%s\n", wel_msg);
printf("[*] Input client name:");
scanf("%s",cli_name);
if((send_num=send(sockfd,cli_name,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
while(1)
{
printf("[*] Input string:");
scanf("%s",send_buf);
send_buf[strlen(send_buf)]='\0';
if((send_num=send(sockfd,send_buf,MAXDATASIZE,0))==-1)
{
perror("send() error.");
exit(1);
}
if((recv_num=recv(sockfd,recv_buf,MAXDATASIZE,0))==-1)
{
perror("recv() error.");
exit(1);
}
printf("[>] Received reverse string:%s\n",recv_buf);
if(!strcmp(recv_buf,"tiuq"))
{
break;
}
}
close(sockfd);
return 0;
}

0x02 演示

服务端

多进程服务端.gif

客服端1

客户端1.gif

客户端2

客户端2.gif

0x03 心得

注意fork函数的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pid_t pid;
if((pid=fork())>0)/*产生子进程*/
{ /*父进程处理过程,父进程关闭父进程的已连接套接字描述符,跳出while循环继续侦听下一个客户的请求*/
close(connfd);
continue;
}
else if(pid==0)
{ /*子进程处理过程,子进程关闭子进程的套接字描述符,调用子进程处理函数pro_cli函数*/
close(listenfd);
pro_cli(connfd,client);/*传入子进程的已连接socket描述符和客户端的套接字地址结构*/
exit(0);/*子进程必须调用exit函数结束退出*/
}
else
{ /*fork函数调用失败结束处理*/
printf("fork() failed.");
exit(0);
}