我在 C 語言中有一個 TCP 客戶端和服務器的實作,其中客戶端不斷地從檔案中發送資料,而服務器不斷地讀取資料。我成功地將兩者連接在一起,并且可以使用以下代碼片段成功發送:
/* File: client.c */
while (fread(buffer, 1, TCP_BUFFER_SIZE, in_file) > 0)
{
send_socket_data(socket_desc, buffer, TCP_BUFFER_SIZE);
}
其中TCP_BUFFER_SIZE = 2 << 20 /* approx 2MB */和send_socket_data定義:
/* File: client.c */
void send_socket_data(int socket_desc, void *buffer, int buffer_size)
{
/* Send data to server */
if (send(socket_desc, buffer, buffer_size, 0) < 0)
{
fprintf(stderr, "Send failed\n");
exit(EXIT_FAILURE);
}
}
(...在服務器中我執行以下操作)
/* File: server.c */
while ((read_size = recv(new_socket, buffer, TCP_BUFFER_SIZE, 0)) > 0)
{
/* Write to binary output file */
fwrite(buffer, TCP_BUFFER_SIZE, 1, out_file);
}
我也在檔案中檢查讀取錯誤或客戶端斷開連接等。
但是,我的問題是在程式執行期間,recv()當只呼叫了一個時send()被多次呼叫,并且在 using 之后clock,我可以看到接收端的運行速度比發送端快得多。因此,如果我要發送一個 322MB 的檔案,它最終會在服務器端存盤為一個 1GB 的檔案。
我該如何解決這個問題?還是我的實作完全錯誤?
我見過人們談論在 TCP 之上實作一個應用程式協議,就像 HTTP 所做的那樣。任何人都可以為我開一條我必須走的路。謝謝。
uj5u.com熱心網友回復:
但是,我的問題是在程式執行期間,當只呼叫了一次 send() 時,recv() 被多次呼叫
這就是 TCP 的作業原理,因此您必須更改代碼才能處理這種情況。雖然它稍微復雜一些,例如不能保證send()并且fwrite()會處理整個緩沖區。fwrite()雖然這將是一個錯誤條件,但對于套接字發送是正常的和預期的,您的代碼必須處理它。
還是我的實作完全錯誤?
不完全,你只需要一點點改變:
while ((read_size = recv(new_socket, buffer, TCP_BUFFER_SIZE, 0)) > 0)
{
/* Write to binary output file */
fwrite(buffer, read_size, 1, out_file);
}
請記住 TCP 套接字是面向流的 - 它保證您將接收到沒有重復的資料并且按順序,它不保證您將收到與發送相同的資料包。TCP中實際上沒有資料包,只是流。
注意:您的 fread 代碼可能有類似的問題:
while (fread(buffer, 1, TCP_BUFFER_SIZE, in_file) > 0)
{
send_socket_data(socket_desc, buffer, TCP_BUFFER_SIZE);
}
可能不會讀取整個緩沖區,例如,如果您到達檔案末尾(除非您的檔案總是精確地由 TCP_BUFFER_SIZE 劃分的大小而沒有提醒)。所以你需要保持讀取的大小并用它來發送資料。
uj5u.com熱心網友回復:
TCP 是一個位元組流,它沒有訊息邊界的概念,就像 UDP 一樣。因此,TCP 中的發送和讀取之間沒有 1:1 的關系。send()發送一個資料塊可能需要多個s recv(),讀取同一個塊資料可能需要多個s。您必須通過以下方式在正在傳輸的資料本身中處理此問題:
在發送資料本身之前發送資料的長度,以便接收者預先知道需要多少位元組,并且一旦讀取了那么多位元組就可以停止讀取。
在資料之后發送一個唯一的分隔符,以便接收方可以繼續讀取,直到讀取分隔符為止。
嘗試更多類似的東西:
客戶
void send_socket_data(int socket_desc, void *buffer, int buffer_size)
{
unsigned char *pbuf = (unsigned char*) buffer;
int sent_size;
while (buffer_size > 0)
{
if ((sent_size = send(socket_desc, pbuf, buffer_size, 0)) < 0)
{
perror("Send failed");
exit(EXIT_FAILURE);
}
pbuf = sent_size;
buffer_size -= sent_size;
}
}
void send_file_data(int socket_desc, FILE* in_file)
{
if (fseek(in_file, 0, SEEK_END) != 0)
{
perror("Seek failed");
exit(EXIT_FAILURE);
}
long in_size = ftell(in_file);
if (pos < 0)
{
perror("Tell failed");
exit(EXIT_FAILURE);
}
rewind(in_file);
// see: https://stackoverflow.com/questions/3022552/
uint64_t tmp_size = htonll(in_size);
send_socket_data(socket_desc, &tmp_size, sizeof(tmp_size));
size_t read_size;
while (in_size > 0)
{
if ((read_size = fread(buffer, 1, min(in_size, TCP_BUFFER_SIZE), in_file)) < 1)
{
perror("Read failed");
exit(EXIT_FAILURE);
}
send_socket_data(socket_desc, buffer, read_size);
in_size -= read_size;
}
}
void send_file(int socket_desc, const char *filename)
{
FILE *in_file = fopen(filename, "rb");
if (!in_file)
{
perror("Open failed");
exit(EXIT_FAILURE);
}
send_file_data(socket_desc, in_file);
fclose(in_file);
}
服務器
int read_socket_data(int socket_desc, void *buffer, int buffer_size)
{
unsigned char *pbuf = (unsigned char*) buffer;
int read_size;
while (buffer_size > 0)
{
if ((read_size = recv(socket_desc, pbuf, buffer_size, 0)) <= 0)
{
if (read_size < 0)
perror("Recv failed");
else
fprintf(stderr, "Client disconnected prematurely\n");
return read_size;
}
pbuf = read_size;
buffer_size -= read_size;
}
return 1;
}
int read_file_data(int socket_desc, FILE* out_file)
{
uint64_t in_size;
int read_size = read_socket_data(socket_desc, &in_size, sizeof(in_size));
if (read_size < 1)
return read_size;
// see: https://stackoverflow.com/questions/809902/
in_size = ntohll(in_size);
while (in_size > 0)
{
if ((read_size = read_socket_data(socket_desc, buffer, min(in_size, TCP_BUFFER_SIZE))) < 1)
return read_size;
if (fwrite(buffer, read_size, 1, out_file) < 1)
{
perror("Write failed");
return -1;
}
in_size -= read_size;
}
return 1;
}
int send_file(int socket_desc, const char *filename)
{
FILE *out_file = fopen(filename, "wb");
if (!on_file)
{
perror("Open failed");
return -1;
}
int read_size = read_file_data(socket_desc, out_file);
fclose(in_file);
return read_size;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/421204.html
標籤:
