使用TCP通过Socket进行二进制文件传输

时间:2012-01-11 04:27:38

标签: c sockets client-server shutdown file-transfer

我正在开发一个二进制文件传输程序,客户端必须将文件上传到服务器。对于这种情况,我需要先发送文件名,然后再发送文件内容。但这对我来说不可行。

让我们看看代码:

// 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);
}

在这段代码中,我将文件名写在socket&amp; amp; server将从socket读取名称,如下所示:

// 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);

此代码适用于图像,可执行文件和文本文件。但是,如果我发送任何音频文件,它不会在服务器端播放。尺寸仍然是大小。但我想知道为什么这会发生在音频文件上。逻辑上有什么不对吗?我还没有尝试过视频文件。

1 个答案:

答案 0 :(得分:8)

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

首先确定如何构建数据,以便接收方可以读取它并重建原始文件名和文件内容。您可能希望如何构建它的一些示例:

  • 发送文件名,以NUL字节结尾,然后发送内容。
  • 以网络字节顺序(big endian)发送两个32位整数,分别包含文件名长度和文件内容长度。然后发送文件名,然后发送文件内容。
  • 将文件名和文件内容序列化为结构化文档格式,如JSON或XML。发送序列化版本。 (对你的用例来说可能有点过分。)

在您发送完整个结构后关闭套接字,无论您认为它是什么。

通过这些安排中的任何一种,接收方都拥有明确重建原始文件名和内容所需的所有信息。

更新:您在问题中添加了更多内容,这几乎是一个不同的问题......

您的服务器端代码(读取文件名和文件内容的代码)包含许多错误。

  • 您假设文件名将在第一个缓冲区数据中完全传递。如果你在没有找到文件名终止符的情况下耗尽了那个缓冲区,你的代码就会遍布整个地方(它会打开一个不正确的文件,它会写一些垃圾,它会在读取下一个缓冲区时从头开始再次查找文件名)。
  • 即使没有n字节可用,您也会将第一个缓冲区数据中的n个字节写入输出文件。实际上有n减去文件名使用了很多。
  • 您没有对read()write()进行任何错误检查,但我会假设您为了问题的清晰而忽略了这一点......
  • 您不检查另一端提供的文件名是否超过50字节的缓冲区。

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

您需要先解决所有这些问题,然后才能发挥作用。

相关问题