0

我正在开发一个关于客户端和服务器的 c++ 程序(最像练习课),使用 HTTP 协议,用户给客户端一个文件文件名和大小(字节),然后客户端创建 n 个线程,每个线程都要求到服务器的特定字节数,服务器参与命令,客户端接收数据并将所有数据放在一起。

我的程序适用于小文件(100kb - 200kb),但是当我尝试从服务器发送大文件(例如 Mb)时,所有字节都被接收但最终文件已损坏,每个线程都有自己的初始化和结束字节数并创建一个名为“file_n.txt”的文件,因此将所有字节放在一起时的字节顺序没有问题,最终损坏的文件具有与原始文件相同的字节数(所有字节都是收到,我也检查服务器日志关于线程要求的字节间隔)但它的 hexdump 是不同的(显然)。

你认为 fwrite 函数与这个问题有关吗?如果是的话,你会很酷,请给我指出正确的方向,我正在努力解决这个问题,这是我的 client.cpp 代码

#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h> 
#include <iostream>
#include <string>
#include <sstream>

using namespace std;
const int MAX_HEADER_SIZE = 1000;
int threadsEnd = 0;

struct bytes
{
  int initByte;
    int endByte;
  int bufferSize;
  int id;
  char * port;
  char * ip;
  char * image;
};

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

void * request_bytes (void * parameters)
{
  struct bytes * p = (struct bytes *) parameters;

  int sockfd, portno, n;
  struct sockaddr_in serv_addr;
  struct hostent *server;

  int totalBuffer = MAX_HEADER_SIZE + p->bufferSize + 1;
  int totalBodyContent = p->bufferSize + 1;

  char buffer[totalBuffer];
  char bodyContent[totalBodyContent];

  portno = atoi(p->port);
  server = gethostbyname(p->ip);

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
  serv_addr.sin_port = htons(portno);

  if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");


  ostringstream init,end;
  init << p->initByte;
  end << p->endByte;

  string HttpRequestString = string("POST / HTTP/1.1\r\n") 
                           + string("Host: ") + p->ip + string("\n")
                           + string("Connection: Close\n")
                           + string("Content-Length: 4\n")
                           + string("Content-Type: txt\n\n")
                           + string("nombre=") + p->image + string("&inicio=") + init.str() + string("&fin=") + end.str() + string("\n");

  const char * HttpRequest = HttpRequestString.c_str(); 

  n = write(sockfd,(void *)HttpRequest, strlen(HttpRequest));

  if (n < 0) 
    error("ERROR writing to socket");

  bzero(buffer,(MAX_HEADER_SIZE + p->bufferSize));
  int headerEndDetermined = 0, bodyEnd = 0;

  int x = 1;
  int bodyInit = 1;
  int total_bytes = 0;

  n = read(sockfd,buffer,((MAX_HEADER_SIZE + p->bufferSize) - 1));

  if (n < 0) 
    error("ERROR reading from socket");

  for(; x < strlen(buffer); x++)
    if(buffer[x - 1] == '\n')
      if(buffer[x] == '\n')
      {
         headerEndDetermined = 1;
         bodyInit = x + 1;
         break;
      }


  for(x = 0; x < p->bufferSize ; x++)
  {
    bodyContent[x] = buffer[bodyInit];
    bodyInit++;
  }

  //Escritura de archivo
  char filename[32];
  snprintf(filename, sizeof(char) * 32, "file%i", p->id);

  FILE * pFile;
  pFile = fopen (filename,"wb");
  if(pFile != NULL)
  {
    fwrite (bodyContent,1,sizeof(bodyContent) - 1,pFile);
    fclose (pFile);
  }

  close(sockfd);
  threadsEnd++;

  return NULL;
}

int main (int argc, char *argv[])
{
  if (argc < 5) {
       fprintf(stderr,"uso %s hostname puerto image_name bytes\n", argv[0]);
       exit(0);
  }

  int globalByte = atoi(argv[4]);
  int threadRequest = 10;
  int requestBytes = (globalByte / threadRequest);
  int globalInitialByte = 1;
  int globalEndByte = requestBytes;
  int x = 0, i = 1;
  int totalBytesRequested = 0;

  pthread_t request[threadRequest];

  for(; x < threadRequest; x++){
    struct bytes request_args;

    request_args.initByte = globalInitialByte;
    request_args.endByte = globalEndByte;
    request_args.bufferSize = requestBytes;
    request_args.id = x + 1;

    globalInitialByte = globalEndByte + 1;
    globalEndByte = globalEndByte + requestBytes;

    if(x == (threadRequest - 1))
    {
      if((totalBytesRequested + requestBytes) < globalByte)
      {
        request_args.endByte = globalByte; 
        request_args.bufferSize = requestBytes + (globalByte - (totalBytesRequested + requestBytes));
      }    
    }
    request_args.ip = argv[1];
    request_args.port = argv[2];
    request_args.image = argv[3];

    pthread_create (&request[x], NULL, &request_bytes, &request_args);
    pthread_join (request[x], NULL); 

    totalBytesRequested += requestBytes;
  }

  /*do
  {
    cout<<"Threads completos: "<<threadsEnd<<endl;
  }while(threadsEnd < threadRequest);*/

  string createFileString = string("cat ");
  for(; i <= threadRequest; i++)
  {
    ostringstream filen;
    filen << i;
    createFileString = createFileString + string("file") + filen.str() + string(" ");
  }
  createFileString = createFileString + string("> new_") + argv[3];                  
  system(createFileString.c_str());

    return 0;
}

抱歉我的语法不好:p。

4

2 回答 2

2

你有很多错误。

  1. HTTP 协议指定行必须以“\r\n”结尾,而不是“\n”。

  2. 您指定了四个字节的内容长度,但您的内容比这更长。

  3. 不要使用sizeof或者strlen当您的代码已经知道事物的大小时。它会让你陷入困境。

  4. 你只调用read一次。您需要继续拨打电话read,直到收到所有数据。

  5. 您指定了 HTTP 1.1 合规性,但您的代码实际上并不符合 HTTP 1.1 规范。例如,如果您收到带有分块编码的数据,您的代码会严重中断。HTTP 1.1 客户端需要支持分块编码。“所有 HTTP/1.1 应用程序必须能够接收和解码chunked传输编码[.]”—— RFC2616 3.6.1

于 2013-04-01T07:25:30.307 回答
0

我认为您不能在运行时声明字符串大小,您需要更改

char buffer[totalBuffer];
char bodyContent[totalBodyContent];

char buffer = new char[totalBuffer];
char bodyContent = new char[totalBodyContent];

并在最后删除缓冲区

delete [] buffer;
delete [] bodyContent;

或者,您可以使用malloc()andfree()来分配和释放缓冲区。

于 2013-04-01T07:26:55.867 回答