网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
{
if(0==exit_num)
{
printf("exerting quit action--please wait····\n");
event.data.fd=sfd; //不在接受连接
event.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_DEL,sfd,&event);
for(i=0;i<child_len;i++)
{
printf("正在处理子进程p[i].pid=%d ",p[i].pid);
printf("\n");
kill(p[i].pid,SIGKILL);
}
wait(NULL);
printf("you have quited\n");
return(0);
}else{
printf("请等待,有客户正在交易···\n");
}
}
ret1=epoll_wait(epfd,evs,child_len+1,-1);
for(i=0;i<ret1;i++)
{ //妈的,又他妈犯这种错误了
if(evs[i].data.fd==sfd) //网络请求到达
{
newfd=accept(sfd,NULL,NULL);
//找到一个空闲的子进程,并且将newfd发送给它
for(j=0;j<child_len;j++)
{
if(p[j].busy==0)
{
//通过tcp管道发送newfd给子进程
send_fd(p[j].tfds,newfd);
exit_num++;
p[j].busy=1;
printf("find a not busy proccess,send newfd success!\n");
break;
}
}
close(newfd); //关闭父进程对newfd的引用计数
}
//监听子进程的tcp管道的发送完成信息
for(j=0;j<child_len;j++)
{
if(evs[i].data.fd==p[j].tfds)
{
//read(p[j].tfds,&flag,sizeof(flag));
char buf[15]={0};
read(p[j].tfds,buf,sizeof(buf));
printf("%s\n",buf);
//read(p[j].tfds,&flag,sizeof(flag));
p[j].busy=0;
exit_num--;
printf("child proccess %d is not busy!\n",j);
if(0==exit_num)
{
printf("Are you stall want to finish this deal,please push Ctrl+C\n");
}
}
}
}
}
}
**4、子进程流程代码**
#include"func.h"
/*首先,要创建len个子进程,就会用到fork( )函数,于是,必须把父进程和子进程分别开来,一个父进程,len个子进程。
其次,初始化是针对定义的数据结构初始化,有:
1、每个子进程在fork创建之后需要根据返回值保存自己的pid.
2、对于全双工的TCP管道,需要用socketpair( )函数初始化
3、刚开始时,将其每个子进程的busy位置为0,表示空闲
对于子进程,必须让它在循环里面持续发送,从父进程接收任务,父进程将到发送数据
,再到发送完,通知父进程,可以通过写一个flag,然后父进程将其busy改为0,
表示空闲。
*/
void child_handle(int fdr)
{
int newfd;
char flag[6]={0};
strcpy(flag,“over”);
while(1)
{
recv_fd(fdr,&newfd);
send_data(newfd);
printf(“I am child,send success!\n”);
write(fdr,&flag,sizeof(flag));
//write(fdr,“hello”,5);
}
}
void make_child(pchild p,int len)
{
int fds[2],i,ret;
pid_t pid;
for(i=0;i<len;i++)
{
ret=socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
pid=fork();
if(0==pid)
{
close(fds[1]);
child_handle(fds[0]);
//p[i].tfds=fds[0]; 将fds[0]端留给子进程
} //让其子进程在里面循环,不出来
close(fds[0]);
p[i].pid=pid; //获取子进程的pid
p[i].busy=0; //置为空闲
p[i].tfds=fds[1]; //将管道的一端传递给主进程
}
}
**5、父进程向子进程发送套接字文件描述符(内核信息)**
#include “func.h”
void send_fd(int fdw,int fd)
{
struct msghdr msg;
memset(&msg,0,sizeof(msg));
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;
char buf1[10]=“hello”;
char buf2[10]=“world”;
struct iovec iov[2];
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
int 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));
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;
char buf1[10]=“hello”;
char buf2[10]=“world”;
struct iovec iov[2];
iov[0].iov_base=buf1;
iov[0].iov_len=5;
iov[1].iov_base=buf2;
iov[1].iov_len=5;
msg.msg_iov=iov;
msg.msg_iovlen=2;
int ret=recvmsg(fdr,&msg,0);
if(-1==ret)
{
perror(“sendmsg”);
return;
}
fd=(int)CMSG_DATA(cmsg);
}
**6、子进程的主要逻辑业务----发送文件**
#include"func.h"
void send_data(int newfd)
{
train t;
memset(&t,0,sizeof(t));
strcpy(t.buf,FILENAME);
t.len=strlen(FILENAME);
printf(“the strcpy t.buf is %s, len =%d\n”,t.buf,t.len);
//先发文件名
int ret;
ret=send_n(newfd,(char )&t,t.len+4);
if(ret==-1)
{
perror(“send”);
return;
}
int fd=open(FILENAME,O_RDONLY);
if(-1==fd)
{
perror(“open”);
return;
}
struct stat filestat;
fstat(fd,&filestat);
t.len=sizeof(long);
memcpy(t.buf,&filestat.st_size,sizeof(filestat.st_size));
send_n(newfd,(char)&t,4+t.len);
while(memset(t.buf,0,sizeof(t.buf)),(t.len=read(fd,t.buf,sizeof(t.buf)))>0)
{
send_n(newfd,(char *)&t,t.len+4);
}
t.len=0;
send_n(newfd,(char *)&t,4+t.len);
close(newfd);
close(fd);
}
**7、为了匹配网络两端的发送速度,以及发送大文件时,可能的缓冲区大小的瓶颈,造成**
**的数据丢失,故需要精确控制要发送和接受的字节数,需要循环发送。**
#include"func.h"
int send_n(int sfd,char *p,int len)
{
int total=0,ret;
while(total<len)
{
ret=send(sfd,p+total,len-total,0);//取出每一次发送了多少个
total=total+ret;
}
return 0;
}
int recv_n(int sfd,char *p,int len )
{
int total=0,ret;
while(total<len)
{
ret=recv(sfd,p+total,len-total,0);
total=total+ret;
}
return 0;
}
**8、客户器端源代码**
#include<time.h>
#include <sys/stat.h>
#include<errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include<sys/wait.h>
typedef struct{
pid_t pid;
int tfds;
short busy;
}child,*pchild;
void send_fd(int fdw,int fd);
void recv_fd(int fdr,int* fd);
void make_child(pchild p,int len);
void child_handle(int fdr);
int recv_n(int,char *,int);
int send_n(int sfd,char *p,int len)
{
int total=0,ret;
while(total<len)
{
ret=send(sfd,p+total,len-total,0);//取出每一次发送了多少个
total=total+ret;
}
return 0;
}
int recv_n(int sfd,char *p,int len )
{
int total=0,ret;
while(total<len)
{
ret=recv(sfd,p+total,len-total,0);
total=total+ret;
}
return 0;
}
int main(int argc,char *argv)
{
if(3!=argc)
{
printf(“error argcs!\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));
int ret,ret1;
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
ret=connect(sfd,(struct sockaddr)&ser,sizeof(ser));
int len;
char buf[1000]={0};
recv_n(sfd,(char *)&len,sizeof(len)); //先接受文件名
recv_n(sfd,buf,len);
int fd=open(buf,O_WRONLY|O_CREAT,0666);
if(-1==fd)
{
perror(“open”);
return -1;
}
recv_n(sfd,(char )&len,sizeof(len)); //在发送文件大小
long file_len;
recv_n(sfd,(char)&file_len,len);
long l=0;
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
har )&len,sizeof(len)); //在发送文件大小
long file_len;
recv_n(sfd,(char)&file_len,len);
long l=0;
[外链图片转存中…(img-WWAI8E87-1715409100084)]
[外链图片转存中…(img-XEXyIzq8-1715409100085)]
[外链图片转存中…(img-zAqrNABJ-1715409100085)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新