1、使用多进程,实现多个客户端同时下载文件
目录:
头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define FILENAME "file"
typedef struct childdata{
pid_t pid;
int fdw;
short busy;//标示子进程是否忙碌
}cdata,*pcdata;
typedef struct tdata{
int len;
char buf[1000];
}td,*ptd;
void make_child(pcdata,int);
void set_init(int,char*,char*);
void send_file(int);
main.c
#include "func.h"
int main(int argc,char* argv[])
{
if(argc!=4)
{
printf("error args\n");
return -1;
}
// 需要创建的进程数
int count=atoi(argv[3]);
// 开辟进程总数所消耗的内存
pcdata p=(pcdata)calloc(count,sizeof(cdata));
// 创建父进程和子进程
make_child(p,count);
int sfd;
sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
// call set_init and bind
set_init(sfd,argv[1],argv[2]);
listen(sfd,count);
// 进程之间的触发响应机制
int epfd;
epfd=epoll_create(1);
struct epoll_event event,*evs;
evs=(struct epoll_event*)calloc(count+1,sizeof(event));
memset(&event,0,sizeof(event));
// 水平触发管理进程
event.events=EPOLLIN;
event.data.fd=sfd;
int ret;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
// 管理每个进程的状态
for(i=0;i<count;i++)
{
event.events=EPOLLIN;
event.data.fd=p[i].fdw;
epoll_ctl(epfd,EPOLL_CTL_ADD,p[i].fdw,&event);
}
int j,ret1,new_fd;
short flag;
while(1)
{
memset(evs,0,(count+1)*sizeof(event));
// 等待进程消息
ret=epoll_wait(epfd,evs,count+1,-1);
if(ret>0)
{
for(i=0;i<ret;i++)
{
// 进程之间有消息通信到来
if(evs[i].data.fd==sfd)
{
// 接收消息
new_fd=accept(sfd,NULL,NULL);
// 遍历一下当前的进程数
for(j=0;j<count;j++)
{
// 非忙碌
if(p[j].busy==0)
{
// 让该进程发送消息
send_fd(p[j].fdw,new_fd);
p[j].busy=1;
break;
}
}
close(new_fd);
}
for(j=0;j<count;j++)
{
// 有写入,说明可以读管道
if(evs[i].data.fd==p[j].fdw)
{
read(p[j].fdw,&flag,sizeof(flag));
p[j].busy=0;//把子进程置为非忙碌状态
printf("run ok\n");
}
}
}
}
}
close(sfd);
close(epfd);
return 0;
}
make_child。c
#include "func.h"
//实现子进程处理事件描述符
void handle_request(int fdr)
{
int new_fd;
short flag=1;
while(1)
{
// 子进程之间接收消息
recv_fd(fdr,&new_fd);
printf("new_fd=%d\n",new_fd);
// 处理发送数据
send_file(new_fd);
// 改变子进程的状态
write(fdr,&flag,sizeof(flag));
}
}
// 创建子进程数
void make_child(pcdata p,int count)
{
int fds[2];
int i;
pid_t pid;
for(i=0;i<count;i++)
{
// 全双工tcp通信
socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
pid=fork();
if(0==pid)
{
// 关闭写端
close(fds[1]);
//子进程处理函数
handle_request(fds[0]);
}
// 父进程传递每个进程的状态
p[i].pid=pid;
// 父进程写端打开
p[i].fdw=fds[1];
p[i].busy=0;
// 关闭读端
close(fds[0]);
}
}
pool_n.c
#include "func.h"
void send_n(int fd,char *buf,int len)
{
int total=0;
int pos;
while(total<len)
{
pos=send(fd,buf+total,len-total,0);
total=total+pos;
}
}
void recv_n(int fd,char *buf,int len)
{
int total=0;
int pos;
while(total<len)
{
pos=recv(fd,buf+total,len-total,0);
total=total+pos;
}
}
send_fd.c
#include "func.h"
// 发送描述符
void send_fd(int fdw,int fd)
{
// 进程同时可以进行读和写,通信消息机制
struct msghdr msg;
// 接收消息
memset(&msg,0,sizeof(msg));
char buf[10]="hello";
char buf1[10]=" world";
struct iovec iov[2];
iov[0].iov_base=buf;
iov[0].iov_len=5;
iov[1].iov_base=buf1;
iov[1].iov_len=6;
msg.msg_iov=iov;
msg.msg_iovlen=2;
// 处理消息
struct cmsghdr *cmsg;
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*)CMSG_DATA(cmsg)=fd;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
// 发送数据
ret=sendmsg(fdw,&msg,0);
if(-1==ret)
{
perror("sendmsg");
return;
}
}
void recv_fd(int fdr,int* fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
char buf[10]="hello";
char buf1[10]=" world";
struct iovec iov[2];
iov[0].iov_base=buf;
iov[0].iov_len=5;
iov[1].iov_base=buf1;
iov[1].iov_len=6;
msg.msg_iov=iov;
msg.msg_iovlen=2;
struct cmsghdr *cmsg;
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;
msg.msg_control=cmsg;
msg.msg_controllen=len;
int ret;
ret=recvmsg(fdr,&msg,0);
if(-1==ret)
{
perror("sendmsg");
return;
}
*fd=*(int*)CMSG_DATA(cmsg);
}
send_file.c
#include "func.h"
void send_file(int new_fd)
{
td t;
memset(&t,0,sizeof(t));
t.len=strlen(FILENAME);
strcpy(t.buf,FILENAME);
send_n(new_fd,&t,4+t.len);
int fd;
fd=open(FILENAME,O_RDONLY);
if(-1==fd)
{
perror("open");
return;
}
while(memset(&t,0,sizeof(t)),(t.len=read(fd,t.buf,sizeof(t.buf)))>0)
{
send_n(new_fd,&t,4+t.len);
}
t.len=0;
send_n(new_fd,&t.len,4);//发送结束符给客户端
close(new_fd);
}
send_init.c
#include "func.h"
// 初始化网络ip和端口绑定进程
void set_init(int sfd,char* ip,char* port)
{
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(port));//port转网络字节序
ser.sin_addr.s_addr=inet_addr(ip);//IP地址转网络字节序
int ret;
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(ser));
if(-1==ret)
{
perror("bind");
return;
}
}
client
pool_n.c
#include "func.h"
void send_n(int fd,char *buf,int len)
{
int total=0;
int pos;
while(total<len)
{
pos=send(fd,buf+total,len-total,0);
total=total+pos;
}
}
void recv_n(int fd,char *buf,int len)
{
int total=0;
int pos;
while(total<len)
{
pos=recv(fd,buf+total,len-total,0);
total=total+pos;
}
}
pro_pool_client.c
#include "func.h"
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd;
sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
// connect
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(ser));
if(-1==ret)
{
perror("connect");
return -1;
}
char buf[1000]={0};
int len;
// client recv 文件描述符
recv_n(sfd,(char*)&len,4);
printf("len=%d\n",len);
// 接收数据
recv_n(sfd,buf,len);
int fd;
//本地创建文件
fd=open(buf,O_RDWR|O_CREAT,0666);
if(-1==fd)
{
perror("open");
return -1;
}
while(1)
{
recv_n(sfd,(char*)&len,4);
memset(buf,0,sizeof(buf));
if(len >0) // 接收数据,并将buf写入
{
recv_n(sfd,buf,len);
write(fd,buf,len);
}else{
break;
}
}
close(fd);
close(sfd);
}
服务器端:
客服端:
进程数