1.用readn代替read的原因
为避免频繁的调用read。read调用系统调用,系统调用需要在用户态和核心态转换,需要保存上下文,频繁调用read,会使系统性能下降,in指标上升。为避免频繁调用read,我们在用户空间创建用户缓冲区,将套接字的缓冲区的内容全部读入用户缓冲区后,一次性把用户缓冲区的内容用read写入内核缓冲区。这样可以提高效率。相同的writen也是这个原因。
ssize_t unpluo_readn(int fd,void* vptr,int n) { size_t nleft=n; char *ptr=(char*)vptr; ssize_t nread; while(nleft>0) { if(nread=read(fd,ptr,nleft)<0) { if(errno==EINTR) nread=0; else return -1; } else if(nread==0) break; nleft-=nread; ptr+=nread; } return (n-nleft); }
2.标准IO与文件IO的区别
FILE的fopen等标准IO是库文件,标准IO将硬盘上的文件一次性读入内存中,使用文件指针加以操作,在fclose之后,将内存中的文件写回磁盘。所以通常说标准文件是流操作。标准IO的操作一直在用户态。
read、write等文件IO是系统调用(使用了系统调用),文件IO用于文件在用户态和核心态之间的传输。
通常的搭配。标准IO打开文件,fopen调用read从磁盘上将内容拷贝至内存,标准IO操作内存中的文件流,fclose调用write将文件从内存拷贝回硬盘。
3.服务器端
4.客户端逻辑:先从客户端接收图片大小,创建图片大小的空文件,然后接收客户端发来的图片。
此处的坑是:在读取图片最后的那点数据时,需要在文件结尾加上\r\n,不然图片传输过来都是打不开的。void servluo_match(int sockfd) { char recv[MAXLINE]; char flen[7]; unpluo_readn(sockfd,flen,7); fprintf(stderr,"the size of image is %s\n",flen); int bytes=atoi(flen); FILE *dlf=NULL; if(bytes>0) { char filename[100]; strcpy(filename,"image/tmp.jpg"); dlf=fopen(filename,"wb"); if(dlf==NULL) { fprintf(stderr,"failed open file\n"); return; } fprintf(stderr,"start recv image %s\n",filename); while(bytes>MAXLINE) { unpluo_readn(sockfd,recv,MAXLINE); bytes-=MAXLINE; fwrite(recv,MAXLINE,1,dlf); } if(bytes<=MAXLINE) { unpluo_readn(sockfd,recv,bytes); char *tmp=recv; strcat(tmp,"\r\n"); fwrite(recv,bytes,1,dlf); tmp=NULL; } fclose(dlf); fprintf(stderr,"write done"); } }
其他考虑:
1.如果客户端放在windows下,就需要将每个接收到的\r\n替换成\n了。
逻辑:先发送图片大小,然后再发送图片
此处的坑是:客户端分两次发送文件大小和图片,但是于服务器而言可能是一次性接收的一堆内容,所以第一次发送文件大小的char[]大小一定要与服务器端一致,否则,服务器端【图片的字节】被【大小的字节】给占了。void str_cli(FILE *fp, int sockfd) { char send[MAXLINE]; //while(1) //{ fprintf(stderr,"please input the name of retrieved image file:"); fflush(stdin); fflush(stdout); fflush(stderr); char tmp[100]; if(fgets(tmp,MAXLINE,fp)==NULL) continue; char *filename=strtok(tmp,"\n");//需要去除\n否则文件打开失败 FILE *ulf=fopen(filename,"rb"); if(ulf==NULL) { fprintf(stderr,"failed open %s\n",filename); continue; } fprintf(stderr,"open file %s,",filename); long filelen;//计算文件长度 fseek(ulf,0,SEEK_END); filelen=ftell(ulf); rewind(ulf); char flen[7]; sprintf(flen,"%d\n",filelen); fprintf(stderr,"it's size is %s",flen); unpluo_writen(sockfd,flen,7);//至此发送文件大小 long left=filelen; while(left>MAXLINE) { fread(send,MAXLINE,1,ulf); unpluo_writen(sockfd,send,MAXLINE); left-=MAXLINE; } if(left<=MAXLINE) { fread(send,left,1,ulf); unpluo_writen(sockfd,send,left); } fclose(ulf); fprintf(stderr,"send %s ok\n",filename); //} return; }