2016-11-16 7 views
0

私はこの分野で新しく、1つのサーバーとクライアントを作成していますが、すべてのコンテンツを手に入れることはできませんが、いくつかの小さなクリップは本当に混乱します。 私のサーバコード:Cソケットで、私のサーバーがコンテンツ全体を受信できないのはなぜですか?

 read(connfd, name, 20); 
     //recv(connfd,name,1024,0); 
     char* a=name; 
     while(a[0]!='\n'){ 
      a++; 
     } 
     a[0]='\0'; 
     printf("name:%s\n", name); 
     read(connfd, size, 20); 
     printf("size:%s\n", size); 
     recv(connfd,buf,8192,0); 
     printf("buf:%s\n", buf); 
     if((stream = fopen(name,"w+t"))==NULL){ 
      printf("The file was not opened! \n"); 
     } 
     int write_length = fwrite(buf,sizeof(char),8192,stream); 
     bzero(buf,8192); 
     if(put){ 
     char *res="OK\n"; 
     write(connfd, res, 1024); 
     } 
     fclose(stream); 

と私のクライアントコードは次のとおりです。今

char buffer[8192]; 
bzero(buffer,8192); 
char * put="PUT\n"; 
if ((write(fd, put, 8192)) <= 0) { 
    if (errno != EINTR) { 
     fprintf(stderr, "Write error: %s\n", strerror(errno)); 
     exit(0); 
    } 
} 
struct stat st ; 
stat(put_name, &st); 
char str[100]; 
sprintf(str, "%d", st.st_size); 
int len; 
char *current=NULL; 
len=strlen(put_name); 

char sendname[1024]; 
strcpy(sendname,put_name); 
strcat(sendname,"\n"); 
write(fd, sendname, 10); 
strcat(str,"\n"); 
write(fd, str, 10); 
FILE *stream; 
if((stream = fopen(put_name,"r"))==NULL) 
{ 
    printf("The file was not opened! \n"); 
    exit(1); 
} 
int lengsize = 0; 
while((lengsize = fread(buffer,1,8192,stream)) > 0){ 
    if(send(fd,buffer,8192,0)<0){ 
     printf("Send File is Failed\n"); 
     break; 
    } 
    bzero(buffer, 8192); 
} 

、私はすべてのコンテンツを送信することができますが、それらの一部を受け取ることができます。例えば、私のMacでは、サーバは名前を受け取ることができますが、strは無視されます。サーバのstrを出力すると、ファイルの内容が表示されます。ファイルの内容はファイル内容全体ではありません。一部のコンテンツは消えます。理由を教えてください。

+5

'read'と' recv'」の上がらないし要求されたバイト数を返すことが保証されています。彼らの戻り値をチェックして、あなたが読んでいるものの終わりを見つけるまで、読書を続けなければなりません。同様に、 'write'(と' send'?)は要求されたバイト数を送信しないかもしれません。その戻り値をチェックしてループで呼び出す必要があります。 – ikegami

+0

あなたはいくつかの例を挙げることができますか?感謝します! –

答えて

2

戻り値は、send()recv()で無視されます。あなたは戻り値をチェックしなければならない!

ファイルを送信するときに、lengsizeは、ファイルから実際に読み取られたバイト数を受信します。 lengsizeが< 8192(通常、ファイルサイズが8192の偶数倍でない場合、ファイルの最後のブロック)のときにクライアントから送信されるバイト数が多すぎます。

さらに重要なのは、クライアントがサーバーにファイルサイズを伝えているにもかかわらず、サーバーは読み取りを停止する時期を知るためにサーバーを無視していることです。サーバーはまた、出力ファイルに安全に書き込めるバイト数を知るために、返される値をrecv()として無視して、実際に受信したバイト数を知っています。

より代わりにこのような何か試してみてください:

共通

int readData(int s, void *buf, int buflen) 
{ 
    int total = 0; 
    char *pbuf = (char*) buf; 
    while (buflen > 0) { 
     int numread = recv(s, pbuf, buflen, 0); 
     if (numread <= 0) return numread; 
     pbuf += numread; 
     buflen -= numread; 
     total += numread; 
    } 
    return total; 
} 

int sendData(int s, void *buf, int buflen) 
{ 
    int total = 0; 
    char *pbuf = (char*) buf; 
    while (buflen > 0) { 
     int numsent = send(s, pbuf, buflen, 0); 
     if (numsent <= 0) return numsent; 
     pbuf += numsent; 
     buflen -= numsent; 
     total += numsent; 
    } 
    return total; 
} 

int readInt32(int s, int32_t *value) 
{ 
    int res = readData(s, value, sizeof(*value)); 
    if (res > 0) *value = ntohl(*value); 
    return res; 
} 

int sendInt32(int s, int32_t value) 
{ 
    value = htonl(value); 
    return sendData(s, &value, sizeof(value)); 
} 

char* readStr(int s) 
{ 
    int32_t size; 
    if (readInt32(s, &size) <= 0) 
     return NULL; 

    char *str = malloc(size+1); 
    if (!str) 
     return NULL; 

    if (readData(s, str, size) <= 0) { 
     free(str); 
     return NULL; 
    } 
    str[size] = '\0'; 

    return str; 
} 

int sendStr(int s, const char *str) 
{ 
    int len = strlen(str); 
    int res = sendInt32(s, len); 
    if (res > 0) 
     res = sendData(s, str, len); 
    return res; 
} 

サーバー:

char buffer[8192]; 

char *name = readStr(connfd); 
if (!name) { 
    // error handling ... 
    sendStr(connfd, "Socket read error"); 
    return; 
} 
printf("name:%s\n", name); 

int32_t filesize; 
if (readInt32(connfd, &filesize) <= 0) { 
    // error handling ... 
    free(name); 
    sendStr(connfd, "Socket read error"); 
    return; 
} 
printf("size:%d\n", filesize); 

if ((stream = fopen(name, "wb")) == NULL) { 
    // error handling ... 
    printf("The file was not opened!\n"); 
    free(name); 
    sendStr(connfd, "File not opened"); 
    return; 
} 

while (filesize > 0) { 
    int numread = readData(connfd, buf, min(filesize, sizeof(buffer))); 
    if (numread <= 0) { 
     // error handling ... 
     close(stream); 
     free(name); 
     sendStr(connfd, "Socket read error"); 
     return; 
    } 

    printf("buf:%.*s\n", numread, buf); 

    if (fwrite(buf, 1, numread, stream) != numread) { 
     // error handling ... 
     close(stream); 
     free(name); 
     sendStr(connfd, "File write error"); 
     return; 
    } 

    filesize -= numread; 
} 

fclose(stream); 
free(name); 

sendStr(connfd, "OK"); 

クライアント:

char buffer[8192]; 

struct stat st; 
if (stat(put_name, &st) != 0) { 
    // error handling ... 
    exit(0); 
} 

if ((stream = fopen(put_name, "rb")) == NULL) { 
    // error handling ... 
    printf("The file was not opened!\n"); 
    exit(0); 
} 

if (sendStr(fd, put_name) <= 0) { 
    // error handling ... 
    close(stream); 
    exit(0); 
} 

int32_t filesize = st.st_size; 
if (sendInt32(fd, filesize) <= 0) { 
    // error handling ... 
    close(stream); 
    exit(0); 
} 

int lengsize; 
while (filesize > 0) { 
    lengsize = fread(buffer, 1, min(filesize , sizeof(buffer)), stream); 
    if (lengsize <= 0) { 
     printf("Read File Failed\n"); 
     // error handling ... 
     close(stream); 
     exit(0); 
    } 

    if (sendData(fd, buffer, lengsize) <= 0) { 
     printf("Send File Failed\n"); 
     // error handling ... 
     close(stream); 
     exit(0); 
    } 

    filesize -= lengsize; 
} 

close(stream); 

char *resp = readStr(fd); 
if (!resp) { 
    // error handling ... 
    exit(0); 
} 

if (strcmp(resp, "OK") == 0) 
    printf("Send File OK\n"); 
else 
    printf("Send File Failed: %s\n", resp); 

free(resp); 
+0

多くのことを送っていますが、一緒に受け取りますか? –

+1

TCPはバイトストリームです。送信されたのと同じ順序でそれを読んでいる限り、あなたが望むものを送信することができます。 TCPはワイヤ上で複数のアイテムを一緒に送信するかもしれませんが、あなたのコードはそれに頼るべきではありません。私の例では、クライアントは32ビットのファイル名サイズ、次にファイル名文字、次に32ビットのファイルサイズ、次にファイルデータを送信します。サーバーは、32ビットのファイル名の長さ、次にファイル名の文字、次に32ビットのファイルサイズ、次にファイルのデータを読み取ります。 2GBを超えるサイズのファイルを処理する必要がある場合は、代わりに64ビットのファイルサイズを使用してください。 –

+0

あなたはとても役に立ちます! –

4

readwriteの機能は、1回の呼び出しでメッセージ全体を送受信することは保証されていません。代わりに、あなたはループ内に座って、すべてが送信されるまで徐々にメッセージを書き、すべてが読み込まれるまで徐々にすべてを読み込むことが期待されます。たとえば、あなたが知っていればは正確に送信されてきたどのくらい、あなたがこれを行うことができます:あなたが送ってきたどのくらいかわからない場合

char recvBuffer[BUFFER_SIZE]; 
int bytesRead = 0; 

while (bytesRead < BUFFER_SIZE) { 
    int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead); 
    if (readThisTime == -1) { 
     // handle error... 
    } 
    bytesRead += readThisTime; 
} 

を、これを試してみてください。

char recvBuffer[BUFFER_SIZE]; 
int bytesRead = 0; 

while (bytesRead < BUFFER_SIZE) { 
    int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead); 
    if (readThisTime == -1) { 
     // handle error... 
    } 
    if (readThisTime == 0) break; // Done! 

    bytesRead += readThisTime; 
} 
+0

以前は、FTPクライアントがファイルの最後を失うことになっていました。私はFINパケットが最終データパケットの前に到着したときに起こったと思います。そういうわけで、あなたは受け取ったはずのものをすべて受け取ったことを確認することが重要だと思います。つまり、2番目のスニペットでは追加のチェックが必要です(例:送付者はメッセージの長さまたはセンチネルを提供しなければならず、受信者は長さまたはセンチネルをチェックしなければならない) – ikegami

+0

@ikegami確かにそれはより構造化された設定では良いアイデアだろう。ここでの例は、主にループ内での読み取りの使用方法を示しています。送信されたデータのタイプや形状に他の制約がある場合は、間違いなくこれを更新する必要があります。 – templatetypedef

関連する問題