2013-04-01 77 views
0

我正在开发一个关于客户端和服务器的C++程序(最像是一个练习类),使用HTTP协议,用户给客户端一个文件文件名和大小(字节),然后客户端创建n个线程,每个线程向服务器请求特定数量的字节,服务器参与订单,客户端接收数据并放在一起。在大文件上使用fwrite进行字节损坏

我的程序对于小文件(100kb-200kb)工作正常,但是当我尝试从服务器发送大文件(例如Mb)时,所有字节都收到但最终文件已损坏,每个线程都有自己的init和结束字节号,并创建一个名为“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; 
} 

对不起我的语法错误:对。

+0

N个线程写入的N个单个文件是否包含正确的数据? – alk

+0

如果原始文件是1000字节,并且我创建10个线程每个“file_n。txt“有100字节,所以尺寸很好,我不知道内容(它适用于小文件),但让我再次检查与hexdump(个人文件),我会告诉你。 – Alevsk

+0

是的,去检查那些N文件,因为这基本上是为了弄清故障的根本原因在于服务器端文件斩波器,发送器,接收器还是客户端文件合并器。 – alk

回答

2

你有很多的错误。

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

  2. 指定的内容长度为四个字节,但内容长于此长度。

  3. 当您的代码已经知道事物的大小时,请勿使用sizeofstrlen。它会让你陷入麻烦。

  4. 您只能拨打read一次。您需要保持呼叫read,直到您收到所有数据。

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

+0

嗨,谢谢你的回答,我会更多地阅读ut HTTP(我不知道关于\ r的感谢),content-lenght:4只是现在的例子,服务器只是解析请求的主体,我想问一下关于继续阅读的东西,现在讨论只调用一次该方法)每个file_n.txt都填充n个字节,你的意思是我最终可能有100个字节的垃圾:s? – Alevsk

+0

@Avvsk:你只能读一个字节。 TCP套接字上的'read'函数为您提供任何可用的数据,如果需要,可以阻塞,直到至少有一个字节可用。您需要不断调用'read'直到读取所有数据。您需要实际实施HTTP协议以确定何时拥有所有数据。 (我建议实现HTTP 1.0,因为这样做非常简单 - 当'read'返回零时,您拥有所有数据。) –

0

我不认为你可以在运行时声明字符串大小,则需要在最后

改变

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

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

,并删除缓冲区

delete [] buffer; 
delete [] bodyContent; 

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