进程池目的和流程
1.1 进程间传递文件描述符(难点)
① 初始化 socketpair 类型描述符 | |||
---|---|---|---|
② sendmsg 发送描述符 | readv 和 writev | 定义结构体struct msghdr | 为结构体申请空间 |
③recvmsg 接收文件描述符 | |||
实例1:进程间传递文件描述符实现 |
1.2 进程池工作流程
进程池流程小节 |
---|
1.3 主要数据结构
1.4 进程池代码编写流程
第一步:
1. 基本框架—创建5个子进程 | ||
---|---|---|
头文件 | 创建子进程makeChild的封装 | 线程池主函数 |
2. 创建5个子进程,初始化其数据,创建DEBUG开关测试打印子进程ID和管道对端ID | ||
头文件: | 创建子进程makeChild的封装 | 线程池主函数 |
第二步
3. 创建子进程,初始化其数据,打印ID测试,启动子进程接收和发送 | ||||
---|---|---|---|---|
头文件: | sendFd,recvFd 发送接受描述符封装 | 创建子进程makeChild封装 | 进程池主函数: | |
4. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket,并开启监听 | ||||
头文件: | sendFd,recvFd 发送接受描述符封装 | tcpInit 初始化socket,并开启监听封装 | 创建子进程makeChild封装 | 进程池主函数: |
第三步:
5. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket开启监听,注册监听每一个子进程管道对端 | |||||
---|---|---|---|---|---|
头文件: | sendFd, recvFd发送接收描述符封装 | tcpInit 初始化socket,并开启监听封装 | 注册监听封装epollInAdd | 创建子进程makeChild封装 | 进程池主函数 |
第四步:
6. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket开启监听,注册监听每一个子进程管道对端,模拟发送,标记忙碌 | |||||
---|---|---|---|---|---|
头文件 | sendFd,recvFd 发送接收描述符封装 | tcpInit 初始化socket,并开启监听封装 | 注册监听封装epollInAdd | 创建子进程makeChild封装 | 主函数进程池 |
1.5 子进程采用变长结构体发送文件(难点)
7. 使用小火车协议,完成进程池父子进程小文件(固定文件名)传输 | |||||||
---|---|---|---|---|---|---|---|
头文件: | tranFile 给客户端发文件 | sendFd,recvFd 发送接收描述符 | 初始化socket,并开启监听封装 | 注册监听epollInAdd | 创建子进程,初始化数据结构 | 进程池主函数 | 客户端 |
结果 |
目的: 实现多个客户端同时下载文件
流程: 客户端连接服务器,连接成功后传输文件,传输完毕服务器断开连接
1.1 进程间传递文件描述符(难点)
第一步,初始化 socketpair 类型描述符
int socketpair(int domain,int type,int protocol,int sv[]);
作用:创造一对未命名的、相互连接的套接字。
参数:
第一个参数 domain
表示协议族,目前为 AF_LOCAL
第二个参数 type
表示类型,既可以是 SOCK_STREAM,又可以是 SOCK_DGRAM
(当参数指定为 SOCK_STREAM 时,得到的结果为流管道,它是全双工的,两个描述符即可读又可写)
第三个参数只能为0;
第四个参数用于保存创建的套接字对;
返回值:
如果函数成功,则返回0,创建好的套接字分别是sv[0] 和 sv[1];
否则返回-1,错误码保存于errno中。
例:
int fds[2];
socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
读端fds[0] (sv[0]),写端fds[1] (sv[1])
第二步:sendmsg 发送描述符
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
作用:系统调用,用于发送消息到另一个套接字
使用的 sockfd 即 sockpair 初始化的描述符 fds[1];
readv 和 writev
readv和writev函数用于在一次函数调用中读、写多个非连续缓冲区(结构体)。有时也将这两个函数称为散布读(scatter read)和聚集写(gather write)。
.
#include
.
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
size_t writev(int filedes, const struct iovec *iov, int iovcnt);
.
这两个函数的第二个参数是指向iovec
结构数组的一个指针:
struct iovec {
…void *iov_base; /* starting address of buffer */
…size_t iov_len; /* size of buffer */
};
.
例:使用wirtev写入文件
int main(int argc,char* argv[])
{
…ARGS_CHECK(argc, 2);
…int fd = open(argv[1], O_RDWR);
…ERROR_CHECK(fd, -1, "open");
…struct iovec iov[2];
…char buf1[10] = "hello";
…iov[0].iov_base = buf1;
…iov[0].iov_len = 5;
…char buf2[10] = "world";
…iov[1].iov_base = buf2;
…iov[1].iov_len = 5;
…writev(fd, iov, 2);
…close(fd);
…return 0;
}
sendmsg 关键是初始化 msghdr 结构体
struct msghdr {
void *msg_name; // optional address 没用
socklen_t msg_namelen; // size of address 没用
struct iovec *msg_iov; // scatter/gather array 没用
size_t msg_iovlen; // # elements in msg_iov 没用
void *msg_control; // ancillary data, see below 关键,即下面的 cmsghdr 结构体地址
size_t msg_controllen; // ancillary data buffer len cmsghdr 结构体的长度
int msg_flags; // flags (unused) 没用
};
定义结构体
struct msghdr msg;
bzero();
iovec 必须赋值
Cmsg 构造结构体 cmsghdr
man cmsg 得到如下信息:
(变长结构体)
struct cmsghdr {
socklen_t cmsg_len; // data byte count, including header
int cmsg_level; // originating protocol
int cmsg_type; // protocol-specific type
/* followed by unsigned char cmsg_data[]; (不一定,传递描述符的个数)*/
};
首先*定义 struct cmsghdr cmsg 指针
cmsg_len 中存取 cmsghdr 结构体的长度,通过 CMSG_LEN 进行计算,我们传递的 fd 的大小为整型四个字节,所以
Int len = CMSG_LEN(sizeof(int));
然后为结构体申请空间:
cmsg = (struct cmsghdr *)calloc(1,len);
Cmsg->cmsg_len = len;
cmsg->cmsg_level = SOL_SOCKET;
Cmsg->cmsg_type = SCM_RIGHTS;
int *fdptr;
fdptr= (int *) CMSG_DATA(cmsg); //其实是自己申请空间大小加12字节
*fdptr = fd;
最后就可以通过 sendmsg 来发送文件描述符
第三步: Recvmsg 接收文件描述符,接收的 msghdr 结构体初始化和 sendmsg 几乎完全一致,区别如下:
*fd = *fdptr;
实例1:进程间传递文件描述符实现
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
int main(int argc, char* argv[])
{
int fds[2];
int ret;
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); //全双工管道
ERROR_CHECK(ret, -1, "socketpair");
if(!fork())
{
close(fds[1]);
int fd;
recvFd(fds[0], &fd);
printf("I am child, %d\n", fd);
char buf[128] = {0};
read(fd, buf, sizeof(buf));
printf("I am child, buf = %s\n", buf);
}else{
close(fds[0]);
int fd = open("file", O_RDWR);
printf("I am parent, fd = %d\n", fd);
sendFd(fds[1], fd);
wait(NULL);
}
return 0;
}
1.2 进程池工作流程
如上图所示,首先我们通过父进程创建了很多个子进程,每个子进程与父进程直接都有一条全双工的管道,父进程是我们的代理。
当 1 号客户端请求连接下载文件时,父进程接收到请求,产生 new_fd,并把 new_fd 发送给非忙碌的子进程 a,由子进程 a 将文件传输给 1 号客户端。这时 2 号客户端请求下载文件,父进程接收请求得到 new_fd,由于这时子进程 a 忙碌,所以将 new_fd 发送给子进程 b,由子进程 b 负责给 2 号客户端下载文件。
进程池流程
.
父进程:
.
创建管道 epoll监听管道读端
if(evs.fd == pid.pipe) pid1.busy = 0
创建子进程
for
{
fork
}
newFd = accept;
newFd 交给非忙碌子进程, 标记子进程为忙碌状态
.
子进程:
.
1.子进程在忙碌,传输文件
2.非忙碌
.
while(1)
{
1.等待父进程分配任务
2.拿到newFd,传文件,应用层协议,小火车,变成非忙碌
3.子进程通知父进程,文件传输完毕,不忙碌了,写管道
}
1.3 主要数据结构
父进程管理子进程所使用的数据结构 :
typedef struct{
pid_t pid; -子进程的 pid
int fd; -管道的一端 socket
short busy; -代表子进程是否忙碌,0 代表非忙碌,1 代表忙碌
}process_data;
父进程, epoll 监控 socketFd 和 所有子进程 fd(每个子进程对应一个结构体都有一个 fd ),监控子进程读事件时,子进程一旦完成任务,就往管道的另一端写,另一端端知道可读,就知道了子进程状态就是非忙碌。
创建多少个子进程,我们就用多少个对应的结构体管理子进程。
1.4 进程池代码编写流程
第一步:
1. 基本框架—创建5个子进程
头文件:
#include<......>
...
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
//int sendFd(int, int);
//int recvFd(int, int*);
//int tcpInit(int*, char*, char*);
创建子进程makeChild的封装
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
//int fds[2];
//int ret;
for(i = 0; i < processNum; i++)
{
//ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
//ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
//close(fds[0]);
childHandle(fds[1]);
}
//close(fds[1]);
//子进程pid
//p[i].pid = pid;
//存储每个子进程的管道对端
//p[i].pipeFd = fds[0];
//p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
//int newFd;
//char finishFlag;
while(1)
//{
//接收任务,没有任务时,子进程睡觉
//recvFd(pipeFd, &newFd);
//模拟给客户端发文件
//printf("file send success!\n");
//子进程通知父进程完成任务了
//write(pipeFd, &finishFlag, 1);
//}
}
进程池主函数
//#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
//#ifdef DEBUG
//int i;
//for(i = 0; i < processNum; i++)
//{
//printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
//}
//#endif
//初始化socket,并开启监听
//int socketFd;
//tcpInit(&socketFd, argv[1], argv[2]);
while(1);
return 0;
}
2. 创建5个子进程,初始化其数据,创建DEBUG开关测试打印子进程ID和管道对端ID
头文件:
#include<......>
...
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
//int sendFd(int, int);
//int recvFd(int, int*);
//int tcpInit(int*, char*, char*);
创建子进程makeChild的封装
//创建子进程,初始化数据结构
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
//int newFd;
//char finishFlag;
while(1)
//{
//接收任务,没有任务时,子进程睡觉
//recvFd(pipeFd, &newFd);
//模拟给客户端发文件
//printf("file send success!\n");
//子进程通知父进程完成任务了
//write(pipeFd, &finishFlag, 1);
//}
}
线程池主函数
#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
#ifdef DEBUG
int i;
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
//int socketFd;
//tcpInit(&socketFd, argv[1], argv[2]);
while(1);
return 0;
}
第二步
3. 创建子进程,初始化其数据,打印ID测试,启动子进程接收和发送
头文件:
#include<......>
...
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
//发送接收描述符
int sendFd(int, int);
int recvFd(int, int*);
//
//int tcpInit(int*, char*, char*);
sendFd,recvFd 发送接受描述符封装
//发送描述符
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
//接收描述符
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
创建子进程makeChild封装
//创建子进程,初始化数据结构
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
int newFd;
char finishFlag;
while(1)
{
//接收任务,没有任务时,子进程睡觉
recvFd(pipeFd, &newFd);
//模拟给客户端发文件
printf("file send success!\n");
//子进程通知父进程完成任务了
write(pipeFd, &finishFlag, 1);
}
}
进程池主函数:
#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
#ifdef DEBUG
int i;
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
//int socketFd;
//tcpInit(&socketFd, argv[1], argv[2]);
while(1);
return 0;
}
4. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket,并开启监听
头文件:
#include<......>
...
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
//发送接收描述符
int sendFd(int, int);
int recvFd(int, int*);
//初始化socket,并开启监听
int tcpInit(int*, char*, char*);
sendFd,recvFd 发送接受描述符封装
//发送描述符
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
//接收描述符
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
tcpInit 初始化socket,并开启监听封装
//初始化socket,并开启监听
int tcpInit(int *sfd,char* ip,char* port)
{
int socketFd=socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(socketFd,-1,"socket");
struct sockaddr_in serAddr;
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(atoi(port));
serAddr.sin_addr.s_addr=inet_addr(ip);
int ret;
ret=bind(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"bind");
listen(socketFd,10);
*sfd=socketFd;
return 0;
}
创建子进程makeChild封装
//创建子进程,初始化数据结构
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
int newFd;
char finishFlag;
while(1)
{
//接收任务,没有任务时,子进程睡觉
recvFd(pipeFd, &newFd);
//模拟给客户端发文件
printf("file send success!\n");
//子进程通知父进程完成任务了
write(pipeFd, &finishFlag, 1);
}
}
进程池主函数:
#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
#ifdef DEBUG
int i;
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
int socketFd;
tcpInit(&socketFd, argv[1], argv[2]);
while(1);
return 0;
}
第三步:
5. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket开启监听,注册监听每一个子进程管道对端
头文件:
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
int sendFd(int, int);
int recvFd(int, int*);
int tcpInit(int*, char*, char*);
int epollInAdd(int, int);
sendFd, recvFd发送接收描述符封装
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
tcpInit 初始化socket,并开启监听封装
int tcpInit(int *sfd,char* ip,char* port)
{
int socketFd=socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(socketFd,-1,"socket");
struct sockaddr_in serAddr;
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(atoi(port));
serAddr.sin_addr.s_addr=inet_addr(ip);
int ret;
ret=bind(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"bind");
listen(socketFd,10);
*sfd=socketFd;
return 0;
}
注册监听封装
int epollInAdd(int epfd, int fd)
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
int ret;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
ERROR_CHECK(ret, -1, "epoll_ctl");
return 0;
}
创建子进程makeChild封装
//创建子进程,初始化数据结构
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
int newFd;
char finishFlag;
while(1)
{
//接收任务,没有任务时,子进程睡觉
recvFd(pipeFd, &newFd);
//模拟给客户端发文件
printf("file send success!\n");
//子进程通知父进程完成任务了
write(pipeFd, &finishFlag, 1);
}
}
进程池主函数
#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
#ifdef DEBUG
int i;
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
int socketFd;
tcpInit(&socketFd, argv[1], argv[2]);
int epfd = epoll_create(1);
struct epoll_event *evs;
evs = (struct epoll_event*)calloc(processNum + 1, sizeof(struct epoll_event));
epollInAdd(epfd, socketFd);
//注册监听每一个子进程的管道对端
for(i = 0; i < processNum; i++)
{
epollInAdd(epfd, pData[i].pipeFd);
}
while(1);
return 0;
}
第四步:
6. 创建子进程,初始化其数据,打印ID测试,启动子进程接收发送,初始化socket开启监听,注册监听每一个子进程管道对端,模拟发送,标记忙碌
头文件
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
int sendFd(int, int);
int recvFd(int, int*);
int tcpInit(int*, char*, char*);
int epollInAdd(int, int);
sendFd,recvFd 发送接收描述符封装
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
tcpInit 初始化socket,并开启监听封装
int tcpInit(int *sfd,char* ip,char* port)
{
int socketFd=socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(socketFd,-1,"socket");
struct sockaddr_in serAddr;
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(atoi(port));
serAddr.sin_addr.s_addr=inet_addr(ip);
int ret;
ret=bind(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"bind");
listen(socketFd,10);
*sfd=socketFd;
return 0;
}
注册监听封装
int epollInAdd(int epfd, int fd)
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
int ret;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
ERROR_CHECK(ret, -1, "epoll_ctl");
return 0;
}
创建子进程makeChild封装
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
int newFd;
char finishFlag;
while(1)
{
//接收任务,没有任务时,子进程睡觉
recvFd(pipeFd, &newFd);
send(newFd, "I am Joker", 10, 0);
//模拟给客户端发文件
printf("file send success!\n");
//关闭连接
close(newFd);
//子进程通知父进程完成任务了
write(pipeFd, &finishFlag, 1);
}
}
主函数进程池
#define DEBUG
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
#ifdef DEBUG
int i;
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
int socketFd;
tcpInit(&socketFd, argv[1], argv[2]);
int epfd = epoll_create(1);
struct epoll_event *evs;
evs = (struct epoll_event*)calloc(processNum + 1, sizeof(struct epoll_event));
epollInAdd(epfd, socketFd);
//注册监听每一个子进程的管道对端
for(i = 0; i < processNum; i++)
{
epollInAdd(epfd, pData[i].pipeFd);
}
int readyFdCount, newFd, j;
char noBusyflag;
while(1)
{
readyFdCount = epoll_wait(epfd, evs, processNum + 1, -1);
for(i = 0; i < readyFdCount; ++i)
{
if(evs[i].data.fd == socketFd)
{
//接收客户端请求
newFd = accept(socketFd, NULL, NULL);
//找非忙碌的子进程
for(j = 0; j < processNum; j++)
{
if(0 == pData[j].busy)
{
//把任务发给对应的子进程(发送描述符)
sendFd(pData[j].pipeFd, newFd);
//子进程标识为忙碌
pData[j].busy = 1;
printf("%d pid is busy\n", pData[j].pid);
break;
}
}
close(newFd);
}
for(j = 0; j < processNum; j++)
{
if(evs[i].data.fd == pData[j].pipeFd)
{
//受到子进程的通知
read(pData[j].pipeFd, &noBusyflag, 1);
//子进程设置为非忙碌
pData[j].busy = 0;
printf("%d pid is not busy\n", pData[j].pid);
break;
}
}
}
}
return 0;
}
1.5 子进程采用变长结构体发送文件(难点)
由于实际我们发送的文件中可能是字符串,可能是音频,可能是视频,所以发送时,对方要知道多少数据,我们必须采用控制数据,这就是我们的应用层协议设计,这里叫其小火 车,每次火车头 data_len,记录火车 buf 中到底装载了多少数据发到对端。
typedef struct{
int data_len;//控制数据,火车头,记录火车装载内容长度
char buf[1000];//火车车厢
}train;
7. 使用小火车协议,完成进程池父子进程小文件(固定文件名)传输
头文件:
#define ARGS_CHECK(argc,val) {if(argc != val) {printf("error args\n"); return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) { if(ret == retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret, funcName) {if(ret != 0) {printf("%s:%s\n", funcName, strerror(ret)); return (void*)-1;}}
#define FILENAME "file"
typedef struct{
pid_t pid; //子进程的pid
int pipeFd; //子进程的管道对端
short busy; //用来标识子进程是否忙碌,0代表非忙碌,1代表忙碌
}process_data_t;
//传输文件协议-小火车
typedef struct{
int dataLen; //存储buf上要发送的数据数据长度
char buf[1000]; //火车车厢
}train_t;
//创建子进程
int makeChild(process_data_t*, int);
int childHandle(int);
//发送接收描述符
int sendFd(int, int);
int recvFd(int, int*);
//初始化socket,并开启监听封装
int tcpInit(int*, char*, char*);
//注册监听
int epollInAdd(int, int);
//给客户端发文件
int tranFile(int);
tranFile 给客户端发文件
//给客户端发文件
int tranFile(int newFd)
{
train_t train;
//发送文件名
train.dataLen = strlen(FILENAME);
strcpy(train.buf, FILENAME);
send(newFd, &train, 4 + train.dataLen, 0);
int fd = open(FILENAME, O_RDWR);
//收文件内容
while((train.dataLen = read(fd, train.buf, sizeof(train.buf))))
{
//发文件内容
send(newFd, &train, 4 + train.dataLen, 0);
}
//发结束符
send(newFd, &train, 4, 0);
return 0;
}
sendFd,recvFd 发送接收描述符
int sendFd(int pipeFd, int fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; //要把传递的描述符告诉内核
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = sendmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "sendmsg");
return 0;
}
int recvFd(int pipeFd, int *fd)
{
struct msghdr msg;
bzero(&msg, sizeof(msg));
struct iovec iov[2];
char buf1[10] = "hello";
iov[0].iov_base = buf1;
iov[0].iov_len = 5;
char buf2[10] = "world";
iov[1].iov_base = buf2;
iov[1].iov_len = 5;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
struct cmsghdr *cmsg;
int cmsgLen = CMSG_LEN(sizeof(int));
cmsg = (struct cmsghdr *)calloc(1, cmsgLen);
cmsg->cmsg_len = cmsgLen;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgLen;
int ret;
ret = recvmsg(pipeFd, &msg, 0);
ERROR_CHECK(ret, -1, "recvmsg");
*fd = *(int *)CMSG_DATA(cmsg);
return 0;
}
初始化socket,并开启监听封装
int tcpInit(int *sfd,char* ip,char* port)
{
int socketFd=socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(socketFd,-1,"socket");
struct sockaddr_in serAddr;
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(atoi(port));
serAddr.sin_addr.s_addr=inet_addr(ip);
int ret;
ret=bind(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"bind");
listen(socketFd,10);
*sfd=socketFd;
return 0;
}
注册监听
int epollInAdd(int epfd, int fd)
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
int ret;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
ERROR_CHECK(ret, -1, "epoll_ctl");
return 0;
}
创建子进程,初始化数据结构
//创建子进程,初始化数据结构
int makeChild(process_data_t *p, int processNum)
{
int i;
pid_t pid;
//管道,用于父子之间进程通信
int fds[2];
int ret;
for(i = 0; i < processNum; i++)
{
//流管道,全双工
ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
ERROR_CHECK(ret, -1, "socketpair");
pid = fork();
//子进程,子进程创建好以后执行 childHandle
if(0 == pid)
{
close(fds[0]);
childHandle(fds[1]);
}
//父进程,创建子进程后,记录子进程信息
//关闭管道写端
close(fds[1]);
//子进程pid
p[i].pid = pid;
//存储每个子进程的管道对端
p[i].pipeFd = fds[0];
p[i].busy = 0;
}
return 0;
}
int childHandle(int pipeFd)
{
int newFd;
char finishFlag;
while(1)
{
//接收任务,没有任务时,子进程睡觉
recvFd(pipeFd, &newFd);
//给客户端发文件
tranFile(newFd);
//关闭连接
close(newFd);
//子进程通知父进程完成任务了
write(pipeFd, &finishFlag, 1);
}
}
进程池主函数
//传参 IP地址,端口号,进程数
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("./prtcess_poll_server ip port process_num\n");
return -1;
}
//得到进程数
int processNum = atoi(argv[3]);
process_data_t *pData = (process_data_t*)calloc(processNum, sizeof(process_data_t));
//创建子进程
makeChild(pData, processNum);
int i;
#ifdef DEBUG
for(i = 0; i < processNum; i++)
{
printf("pid = %d, pidFd = %d\n", pData[i].pid, pData[i].pipeFd);
}
#endif
//初始化socket,并开启监听
int socketFd;
tcpInit(&socketFd, argv[1], argv[2]);
int epfd = epoll_create(1);
struct epoll_event *evs;
evs = (struct epoll_event*)calloc(processNum + 1, sizeof(struct epoll_event));
//把socketFd添加到epoll中监听
epollInAdd(epfd, socketFd);
//监听管道读端
for(i = 0; i < processNum; i++)
{
//添加管道读端到epoll中,当子进程非忙碌时,写管道
//父进程就知道子进程完成任务,可以再次分配任务
//注册监听每一个子进程的管道对端
epollInAdd(epfd, pData[i].pipeFd);
}
int readyFdCount, newFd, j;
char noBusyflag;
while(1)
{
readyFdCount = epoll_wait(epfd, evs, processNum + 1, -1);
for(i = 0; i < readyFdCount; ++i)
{
if(evs[i].data.fd == socketFd)
{
//接收客户端请求
newFd = accept(socketFd, NULL, NULL);
//找非忙碌的子进程
for(j = 0; j < processNum; j++)
{
if(0 == pData[j].busy)
{
//把任务发给对应的子进程(发送描述符)
sendFd(pData[j].pipeFd, newFd);
//子进程标识为忙碌
pData[j].busy = 1;
printf("%d pid is busy\n", pData[j].pid);
break;
}
}
close(newFd);
}
for(j = 0; j < processNum; j++)
{
if(evs[i].data.fd == pData[j].pipeFd)
{
//受到子进程的通知
read(pData[j].pipeFd, &noBusyflag, 1);
//子进程设置为非忙碌
pData[j].busy = 0;
printf("%d pid is not busy\n", pData[j].pid);
break;
}
}
}
}
return 0;
}
客户端
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int socketFd = socket(AF_INET, SOCK_STREAM, 0);
ERROR_CHECK(socketFd, -1, "socket");
struct sockaddr_in serAddr;
bzero(&serAddr, sizeof(serAddr));
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(atoi(argv[2]));
serAddr.sin_addr.s_addr = inet_addr(argv[1]);
int ret;
ret = connect(socketFd, (struct sockaddr*)&serAddr, sizeof(serAddr));
ERROR_CHECK(ret, -1, "connect");
int fd;
int dataLen;
char buf[1000] = {0};
recv(socketFd, &dataLen, 4, 0);
//接收文件名
recv(socketFd, buf, dataLen, 0);
printf("%s\n", buf);
if(0 != strcmp(buf, "file"))
{
return -1;
}
fd = open(buf, O_CREAT|O_RDWR, 0666);
ERROR_CHECK(fd, -1, "open");
while(1)
{
recv(socketFd, &dataLen, 4, 0);
if(dataLen > 0)
{
recv(socketFd, buf, dataLen, 0);
write(fd, buf, dataLen);
}else{
break;
}
}
close(fd);
close(socketFd);
}
使用两个窗口分别启动 服务器(进程池) 和 客户端
在服务器目录下创建一个文件 file 里边写入一点字符
服务器(进程池)输入: ./server + 本地IP地址 + 打算启动的子进程数
客户端输入: ./client + 本地IP地址
服务器打印:
7785 pid is busy
7785 pid is not busy、
然后卡住等待
客户端打印:
file
并且收到服务器发来的 file 文件