fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。
fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。
下面展示了一个返回本地时间给客户的简单客户-服务器模型,其中服务端为了并发处理来自客户的请求会调用fork创建一个子线程,
会复制文件描述符,父子进程相同的描述符指向同一个file结构体,close()一次时,引用计数由2变为1,
只有close()两次之后,服务端才会真正关闭。
客户
服务器
接收到时间后,10S后客户端才会关闭。
下面展示了一个返回本地时间给客户的简单客户-服务器模型,其中服务端为了并发处理来自客户的请求会调用fork创建一个子线程,
会复制文件描述符,父子进程相同的描述符指向同一个file结构体,close()一次时,引用计数由2变为1,
只有close()两次之后,服务端才会真正关闭。
#ifndef MY_NET_H
#define MY_NET_H
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#define MAXLINE 4096
#define SA struct sockaddr
#define LISTENEQ 10
/*err_quit*/
void err_quit(const char* err_string)
{
printf("%s\n", err_string);
exit(-1);
}
/*err_sys*/
void err_sys(const char* err_string)
{
perror(err_string);
exit(-1);
}
/*Socket*/
int Socket(int domain, int type, int protocol)
{
int sockfd = socket(domain, type, protocol);
if (sockfd == -1)
err_sys("socket error");
return sockfd;
}
/*Inet_pton*/
int Inet_pton(int af, const char *src, void *dst)
{
int r;
if ((r = inet_pton(af, src, dst)) <= 0)
err_sys("inet_pton error");
return r;
}
/*Connect*/
int Connect(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen)
{
int r;
if ((r = connect(sockfd, addr, addrlen)) == -1)
err_sys("connect error");
return r;
}
/*Bind*/
int Bind(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen)
{
int r;
r = bind(sockfd, addr, addrlen);
if (r == -1)
err_sys("bind error");
return r;
}
/*Listen*/
int Listen(int sockfd, int backlog)
{
int r;
r = listen(sockfd, backlog);
if (r == -1)
err_sys("listen error");
return r;
}
/*Accept*/
int Accept(int sockfd,
struct sockaddr *addr,
socklen_t *addrlen)
{
int r;
r = accept(sockfd, addr, addrlen);
if (r == -1)
err_sys("accept error");
return r;
}
/*Close*/
int Close(int fd)
{
int r = close(fd);
if (r == -1)
err_sys("close error");
return r;
}
/*Read*/
int Read(int fd, void *buf, size_t count)
{
int r;
r = read(fd, buf, count);
if (r == -1)
err_sys("read error");
return r;
}
/*Write*/
int Write(int fd, const void *buf, size_t count)
{
int r;
r = write(fd, buf, count);
if (r == -1)
err_sys("write error");
return r;
}
#endif
客户
#include "net.h"
main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("2个参数\n");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9997);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
while ((n = Read(sockfd, recvline, MAXLINE)) != 0)
{
recvline[n] = 0;
fputs(recvline, stdout);
}
printf("server closed\n");
exit(0);
}
服务器
#include "net.h"
main(int argc, char **argv)
{
int listenfd, connfd, n, i;
struct sockaddr_in servaddr;
char buf[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9997);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
Listen(listenfd, LISTENEQ);
while (1)
{
connfd = Accept(listenfd, (SA*)NULL, NULL);
if (fork() == 0) //child
{
Close(listenfd);
ticks = time(NULL);
snprintf(buf, sizeof(buf), "%.24s\r\n",
ctime(&ticks));
for(i=0; i<strlen(buf); i++)
Write(connfd, &buf[i], 1);
sleep(10);
Close(connfd);
printf("child closed connfd\n");
exit(0);
}
Close(connfd);
printf("parent closed connfd\n");
}
}
接收到时间后,10S后客户端才会关闭。