一、子进程继承父进程的普通文件描述符
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
char buf[1024]={0};
pid_t pid;
int ret=0;
int fd = open("./temp.txt", O_CREAT|O_TRUNC|O_RDWR, 0666);
if( fd == -1 ){
perror("open ./temp.txt");
return -1;
}
if ((pid = fork()) < 0) {
perror("fork");
return -1;
}
else if (pid == 0) { //child process
sleep(1);
ret = sprintf(buf, "child process pid:%d,parent pid:%d\n", getpid(), getppid());
write(fd, buf, ret);
close(fd);
exit(0);
}
else { //parent process
ret = sprintf(buf, "parent process pid:%d, child pid:%d\n", getpid(), pid);
write(fd, buf, ret);
close(fd);
}
waitpid(pid, NULL, 0);
return 0;
}
运行结果如下:
[root@lghvm001 multi_process]# gcc inherit_fd.c[root@lghvm001 multi_process]# ./a.out
[root@lghvm001 multi_process]# cat temp.txt
parent process pid:18028, child pid:18029
child process pid:18029,parent pid:18028
结论:子进程继承了父进程已经打开了的文件描述符。
二、子进程继承父进程的TCP Socket
写一个简单的TCP Server回射程序,即客户端给服务器端发一段字符串,服务器端将字符串原样返回。这里服务器端的Server程序采用主进程+工作进程的模式:主进程负责接收客户端的连接请求,然后fork一个子进程(也叫工作进程)来处理客户端的请求数据,即子进程接收客户端发来的字符串,然后将字符串原样返回给客户端。这里主要演示子进程继承父进程的TCP Socket,下面来看代码:
tcp_server.c
/* tcp_server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <signal.h>
#define MAXLINE 1024
#define BACKLOG 50
int main(int argc, char** argv)
{
int listenfd, connfd, n = 0;
struct sockaddr_in servaddr, cliaddr;
pid_t pid;
socklen_t clilen;
signal(SIGCHLD, SIG_IGN); //避免子进程成为僵尸进程
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //IPv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //自动填入本机的IP地址
/*
if( inet_pton(AF_INET, "172.23.1.180", &servaddr.sin_addr) <= 0){ // [将“点分十进制”ip-> 网络字节序“整数”ip]
printf("inet_pton error for %s\n","172.23.1.180");
return -1;
}
*/
servaddr.sin_port = htons(6666); //将端口号转换为网络字节序
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
if( listen(listenfd, BACKLOG) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
return -1;
}
printf("======waiting for client's connect requestion======\n");
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) == -1){
printf("accept socket error: %s(errno: %d)", strerror(errno),errno);
continue;
}
if ((pid = fork()) < 0) {
perror("fork");
return -1;
}
else if (pid == 0) { //child process
char buff[MAXLINE];
close(listenfd);
fprintf(stdout, "Connected, client addr: %s\n", inet_ntoa(cliaddr.sin_addr));
if((n = recv(connfd, buff, MAXLINE-1, 0)) < 0) {
printf("Failed to receive bytes from client\n");
return -1;
}
buff[n] = '\0';
fputs("recv msg from client: ", stdout);
fputs(buff, stdout);
while (n > 0) {
if (send(connfd, buff, n, 0) != n) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
if((n = recv(connfd, buff, MAXLINE-1, 0)) < 0) {
printf("Failed to receive bytes from client\n");
return -1;
}
buff[n] = '\0';
fputs(buff, stdout);
}
close(connfd);
exit(0);
}
else { //parent process
close(connfd);
continue;
}
}
return 0;
}
客户端代码tcp_client.c
/* tcp_client.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 1024
int main(int argc, char** argv)
{ int sockfd, n, received;
int len, bytes;
char recvline[MAXLINE], sendline[MAXLINE];
struct sockaddr_in servaddr;
/*if( argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}*/
memset(recvline, 0, MAXLINE);
memset(sendline, 0, MAXLINE);
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
return -1;
}*/
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n", strerror(errno),errno);
return -1;
}
printf("send msg to server: \n");
while(1) {
fgets(sendline, MAXLINE, stdin);
len = strlen(sendline);
if( send(sockfd, sendline, len, 0) != len) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
fputs("echo from server:\n", stdout);
received = 0;
while(received < len) {
bytes = 0;
if ((bytes = recv(sockfd, recvline, MAXLINE-1, 0)) == -1 ){
perror("recv");
return -1;
}
else if(bytes == 0) {
printf("recv fail:the server has closed the connection prematually!\n");
return -1;
}
received += bytes;
recvline[bytes] = '\0';
fputs(recvline, stdout);
}
fputs("\n", stdout);
}
close(sockfd);
return 0;
}
打开linux的命令行终端,编译运行tcp_server
另外再打开两个命令行终端,编译运行tcp_client
运行结果如下:
tcp_server
[root@lghvm001 multi_process]# gcc tcp_server.c -o tcp_server
[root@lghvm001 multi_process]# ./tcp_server
======waiting for client's connect requestion======
Connected, client addr: 255.127.0.0
recv msg from client: hi,this is client1.
Connected, client addr: 127.0.0.1
recv msg from client: hi, this is client2.
tcp_client1
[root@lghvm001 multi_process]# gcc tcp_client.c -o tcp_client
[root@lghvm001 multi_process]# ./tcp_client
send msg to server:
hi,this is client1.
echo from server:
hi,this is client1.
tcp_client2
[lgh@lghvm001 multi_process]$ ./tcp_client
send msg to server:
hi, this is client2.
echo from server:
hi, this is client2.
该示例有多少个客户端连接就会产生多少个子进程,目前有主进程,两个子进程:
[lgh@lghvm001 Desktop]$ ps -ef | grep tcp_server
root 18302 43605 0 14:08 pts/14 00:00:00 ./tcp_server
root 18309 18302 0 14:09 pts/14 00:00:00 ./tcp_server
root 18311 18302 0 14:09 pts/14 00:00:00 ./tcp_server
lgh 18313 18132 0 14:09 pts/22 00:00:00 grep tcp_server
[lgh@lghvm001 Desktop]$
若客户端连接太多,会导致多过的子进程,可以使用多路IO复用进行优化,在Linux下epoll是不二选择。