2012-01-11 117 views
2

我正在处理客户端必须将文件上载到服务器的二进制文件传输程序。对于这种情况,我需要首先发送文件名,然后再发送文件内容。但这对我来说不可行。使用TCP通过套接字进行二进制文件传输

让我们来看看代码:

// Client-side code to send file name 
void sendFileName(
    int sd,   /*Socket Descriptor*/ 
    char *fname) /*Array Containing the file name */ 
{  
    int n , byteswritten=0 , written ; 
    char buffer[1024]; 
    strcpy(buffer , fname); 
    n=strlen(buffer); 
    while (byteswritten<n) 
    { 
     written=write(sd , buffer+byteswritten,(n-byteswritten)); 
     byteswritten+=written; 
    } 
    printf("File name : %s sent to server \n",buffer); 
} 

在这段代码中,我通过套接字写入文件名&服务器将读取插槽它的名称如下:

// Server-side code to read file name from client 
while ((n = read((int)connfd, (fname + pointer), 1024)) > 0) 
{ 
    pointer=pointer+n; 
} 

好,问题是我必须在发送文件名后在客户端关闭写入端,该文件名将成为服务器端代码的FIN段,以停止从服务器读取数据。

如果我关闭读端,如:

shutdown(sd,SHUT_WR);  //Closing write end at client side 

我怎么能写通过插座(即发送)的文件内容服务器,以便它可以从套接字读取?

注意:我所做的就是在文件名后添加来自客户端的文件内容,并为内容添加特殊字符(用于通知文件名的末尾),然后添加文件内容。

在客户端,

void readWriteFile(
    int sd,      /*Socket Descriptor */ 
    int fd,      /*File Descriptot */ 
    char *fname)     /*File Name */ 
{ 
    char buffer[1050]; 
    int n; 
    int len = 0; 
    char *tmp = (char *)malloc(sizeof (char) * (strlen(fname) + 2)); 
    strcpy(tmp, fname);   //Copying the file name with tmp 
    strcat(tmp, "/");   //Appending '/' to tmp 
    len = strlen(tmp); 
    memset(buffer, '\0', sizeof (buffer)); 
    strcpy(buffer, tmp);   //Now copying the tmp value to buffer 

    while ((n = read(fd, buffer + len, 1024)) > 0) 
    { 
     if (write(sd, buffer, n) != n) 
     { 
     printf("File write Error \n"); 
     exit(0); 
     } 
     len = 0; 
    } 
    printf("File sent successfully \n"); 
} 

而在服务器端,

char fname[50], buffer[1024]; 
    int n, fd; 
    int i; 
    int start = 0; 

    while ((n = read((int)connfd, buffer, 1024)) > 0) // Reading from socket 
    { 
     if (!start) 
     { 
      /* This 'if' loop will executed almost once i.e. until 
      getting the file name */ 
     for (i = 0; i < 1024; i++) 
     { 
      /* Since '/' is the termination character for file name */ 
      if (buffer[i] == '/') 
      { 
       start = 1;  // Got the file name 
       break; 
      } 

      fname[i] = buffer[i]; //Storing the file name in fname 
     } 

     fname[i] = '\0'; 

     /* Creating the file in the server side */ 
     fd = open(fname, O_WRONLY | O_CREAT, S_IRWXU); 

     if (fd < 0) 
     { 
      perror("File error"); 
      exit(0); 
     } 

     /* Here writing the buffer content to the file after 
      the (buffer+i+1), because after this address only, we 
      can get the original file content */ 

     write(fd, buffer + i + 1, n); 
     } 
     else 
     { 
     write(fd, buffer, n); 
     } 
    } 
    printf("%s received successful \n", fname); 

此代码工作正常图像,可执行&文本文件。但是如果我发送任何音频文件,它不会在服务器端播放。尺寸保持不变。但我想知道为什么这会发生在音频文件上。逻辑中有什么错误?我还没有尝试过视频文件。

+0

注意返回值给'written'可能是'在很普通的情况下-l',例如'errno'是'EGAIN'或'EINTR'。这会给你的逻辑带来麻烦。 – 2012-01-11 05:00:31

+0

“但这对我来说不可行。”如果不可行,为什么要问这个问题呢?听起来你已经放弃了。 – 2012-01-11 06:07:04

回答

8

为什么说“发送文件名后我必须在客户端关闭写入端”?你没有完成发送你的数据,所以你肯定不想关闭套接字呢?

首先决定要如何组织你的数据,使接收器可以读取它,并重建原始文件名和文件内容。你可能会想如何构建它的一些例子:

  • 发送文件名,用NULL字节终止,则发送内容。
  • 发送网络字节顺序(big endian)的包含了文件名的长度和文件内容的长度,分别两个32位整数。然后发送文件名,然后发送文件内容。
  • 序列化两者的文件名和文件内容转换为结构化文件形式,如JSON或XML。发送序列化版本。 (可能对您的使用案例过度)

无论您决定是否发送完整结构,请关闭套接字。

随着这些安排的任何一个,接收器收到所有需要明确地重建原始文件的名称和内容的信息。

UPDATE:你增加了更多你的问题,这几乎是一个不同的问题...

你的服务器端代码(一个读取的文件名和文件内容)包含了很多错误。

  • 您认为文件名将完全在第一批缓冲数据中传递。如果你在没有找到文件名结束符的情况下耗尽了这个缓冲区,那么你的代码会遍布整个地方(它会打开一个不正确的文件,它会写入一些垃圾,它会返回到从下一个缓冲区读取时开始再次查找文件名)。
  • 你写了从数据到输出文件的第一bufferful n字节,即使有不适n字节。实际上有n减去然而很多被使用的文件名。
  • 你不这样做就read()write()任何错误检查,但我会假设你省略了,为了清楚的问题...
  • 你不检查由另一端所提供的文件名是否超过你的50字节的缓冲区。

在另一个大小的代码中,有一个明显的缓冲区溢出,您可以将1024个字节读入只有1024 - len个字节的缓冲区。

您需要修复这一切,然后才能指望任何工作。