2012-08-29 163 views
1

這部分代碼接收文件時所採用一個客戶發送和接收文件與插座

void do_retr_cmd(int f_sockd){ 
    int fd; 
    ssize_t nread = 0; 
    uint32_t fsize, fsize_tmp, total_bytes_read, size_to_receive; 
    char *filename = NULL, *conferma = NULL, *filebuffer = NULL; 
    char buf[256], dirp[256], t_buf[256]; 

    memset(dirp, 0, sizeof(dirp)); 
    memset(buf, 0, sizeof(buf)); 
    memset(t_buf, 0, sizeof(t_buf)); 
    printf("Write the name of file to download: "); 
    fgets(dirp, BUFFGETS, stdin) 
    filename = NULL; 
    filename = strtok(dirp, "\n"); 
    sprintf(buf, "RETR %s", dirp); 
    if(send(f_sockd, buf, strlen(buf), 0) < 0){ 
    perror("Errore durante l'invio del nome del file"); 
    onexit(f_sockd, 0, 0, 1); 
    } 
    fsize = 0; 
    recv(f_sockd, t_buf, sizeof(t_buf), 0) 
    fsize = atoi(t_buf); 
    fd = open(filename, O_CREAT | O_WRONLY, 0644); 
    fsize_tmp = fsize; 
    filebuffer = (char *)malloc(fsize); 
    total_bytes_read = 0; 
    nread = 0; 
    for(size_to_receive = fsize; size_to_receive > 0;){ 
    nread = read(f_sockd, filebuffer, size_to_receive); 
    if(nread < 0){ 
     perror("read error on retr"); 
     onexit(f_sockd, 0, 0, 1); 
    } 
    if(write(fd, filebuffer, nread) != nread){ 
     perror("write error on retr"); 
     onexit(f_sockd, 0, 0, 1); 
    } 
    size_to_receive -= nread; 
    } 
    close(fd); 
    fflush(stdout); 
    fflush(stdin); 
    memset(buf, 0, sizeof(buf)); 
    recv(f_sockd, buf, 21, 0) 
    printf("%s", buf); 
    memset(buf, 0, sizeof(buf)); 
    memset(t_buf, 0, sizeof(t_buf)); 
    memset(dirp, 0, sizeof(dirp)); 
    free(filebuffer); 
} 

而且這部分代碼發送文件時使用由服務器

void do_server_retr_cmd(f_sockd, m_sockd){ 
    int fd, rc; 
    uint32_t fsize, size_to_send; 
    char *filename = NULL, *other = NULL; 
    char buf[512], t_buf[256]; 
    off_t offset; 
    struct stat fileStat; 

    memset(buf, 0, sizeof(buf)); 
    memset(t_buf, 0, sizeof(t_buf)); 
    recv(f_sockd, buf, sizeof(buf), 0) 
    other = NULL; 
    filename = NULL; 
    other = strtok(buf, " "); 
    filename = strtok(NULL, "\n"); 

    if(strcmp(other, "RETR") == 0){ 
    printf("Ricevuta richiesta RETR\n"); 
    } else /* do something */ 

    fd = open(filename, O_RDONLY); 

    memset(&fileStat, 0, sizeof(fileStat)); 
    fileStat.st_size = 0; 
    fstat(fd, &fileStat) 
    fsize = fileStat.st_size; 
    snprintf(t_buf, 255, "%" PRIu32, fsize); 
    send(f_sockd, t_buf, sizeof(t_buf), 0) 
    offset = 0; 
    for (size_to_send = fsize; size_to_send > 0;){ 
    rc = sendfile(f_sockd, fd, &offset, size_to_send); 
    if (rc <= 0){ 
     perror("sendfile"); 
     onexit(f_sockd, m_sockd, fd, 3); 
    } 
    size_to_send -= rc; 
    } 
    close(fd); 
    fflush(stdout); 
    fflush(stdin); 
    memset(buf, 0, sizeof(buf)); 
    strcpy(buf, "226 File transfered\n"); 
    send(f_sockd, buf, strlen(buf), 0) 
    memset(buf, 0, sizeof(buf)); 
    memset(t_buf, 0, sizeof(t_buf)); 
} 

- >檢查錯誤已被省略< -
我有一個很大的問題機智h這2段代碼。當我啓動主程序我必須寫:
1. retr然後我按enter鍵
2. Write the filename to download:我寫的文件名,然後我按enter鍵
的問題是,有時文件被下載正確,有時它是不是下載,但它的一部分顯示在標準輸出(在終端上)。
我不明白爲什麼我有這種奇怪的行爲。
PS:我知道我的代碼很醜,但我是C-newbie!

我在Ubuntu amd64上開發並使用GCC-4.6.3(C語言)。

+0

它顯示在您運行服務器或客戶端程序的終端中嗎? –

+1

我也建議你學會使用調試器,所以你可以通過代碼做發送/接收,以確保它做正確的事情。 –

+1

如果沒有看到真實的代碼,我們無法判斷。例如,當'recv'的返回值不符合您的期望時,錯誤很可能發生在您所做的事情上。 –

回答

3

TCP連接,給你一個字節的可靠的雙向流,但你的「應用程序消息」的界限,不會保留,這意味着一個send()可以在多個對對方接收recv() S和其他可以將幾個send() s合併爲一個recv()(並且可以接收您發送的最後一個塊的一部分)。好處是你收到你發送的字節,按照你發送的順序。

服務器代碼中的recv(f_sockd, buf, sizeof(buf), 0);行假設您在此處讀取文件名,而實際上無論您的客戶端發送了什麼字節,都可以達到256字節。

您需要在裸露的TCP之上施加某種應用程序級協議。一個非常簡單的一個辦法是將在表格文件內容的前面發送文本標題:

file-size file-name\n 

所以你的服務器能找到第一個換行符,劈在第一空間的線,並有數量預計的字節數以及將這些字節保存到的文件名。不要忽略該換行符後面的其餘接收緩衝區,將其保存到文件中。這也使您可以重新使用該連接進行多個文件傳輸。

希望這會有所幫助。

1
recv(f_sockd, buf, 21, 0) 
printf("%s", buf); 

printf打印一噸垃圾隨意的,因爲有實施控制它接收並印刷沒有什麼實際的協議。例如,printf如何知道要打印多少個字節?

1

我的回答以前的版本並不完全正確,但這就是爲什麼你看到奇怪的行爲。您發送的文件大小爲

snprintf(t_buf, 255, "%" PRIu32, fsize); 

然後用

recv(f_sockd, t_buf, sizeof(t_buf), 0) 

接收但是這不能保證實際讀取的字節sizeof(t_buf)。然後atoi有時會返回不正確的大小,並將該文件的其餘部分視爲狀態消息,將在末尾打印(直到第一個空字符)。

因爲recv可能不會返回所有的你問一次數據,你必須檢查它的返回值,並可能重複調用recv

size_t to_recv = sizeof(t_buf); 
size_t rcvd = 0; 
while (to_recv > 0) { 
    ssize_t r = recv(f_sockd, t_buf + rcvd, sizeof(t_buf) - rcvd, 0); 
    if (r < 0) { 
     //error 
    } 
    else { 
     to_recv -= r; 
     rcvd += r; 
    } 
} 

顯然,你要麼必須知道有多少數據期望或者想出一個更好的協議,如其他答案中所建議的(例如,查找終止符以確定您何時讀取了大小)。

+0

但如果我寫'snprintf(t_buf,255,PRIu32,fsize);'我得到了一個編譯器警告! – polslinux

+0

對不起,我的錯。修正了答案:) –

+0

確定:)所以我可以解決這個問題?使用'recv(f_sockd,t_buf,sizeof(uint32_t),0)'? – polslinux