图像检索服务器编写问题记录——用socket传输图片

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.服务器端

逻辑:先从客户端接收图片大小,创建图片大小的空文件,然后接收客户端发来的图片。

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");
	}
}
此处的坑是:在读取图片最后的那点数据时,需要在文件结尾加上\r\n,不然图片传输过来都是打不开的。

其他考虑:

1.如果客户端放在windows下,就需要将每个接收到的\r\n替换成\n了。


4.客户端

逻辑:先发送图片大小,然后再发送图片

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;
}
此处的坑是:客户端分两次发送文件大小和图片,但是于服务器而言可能是一次性接收的一堆内容,所以第一次发送文件大小的char[]大小一定要与服务器端一致,否则,服务器端【图片的字节】被【大小的字节】给占了。

你可能感兴趣的:(图像检索服务器编写记录)