2013-04-01 75 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()分配和釋放緩衝區。