简体   繁体   English

无法正确发送文件。 套接字。 C

[英]Not sending file correctly. Sockets. C

I am writing a program that a client can ask for files to a server. 我正在编写一个客户端可以向服务器请求文件的程序。 Then the server will send them in chunks of 512 bytes. 然后,服务器将以512字节的块发送它们。 The problem is that when the client read the file: 问题是当客户端读取文件时:

*Sometimes the first 512 bytes are different from the original file. *有时前512个字节与原始文件不同。 The total read file also has a different size (and obviously it also ends different from the original file) and therefore the client loop that writes to the new file does never end. 总读取文件的大小也不同(显然,它的结束也不同于原始文件),因此写入新文件的客户端循环永远不会结束。

*Sometimes it works perfectly and i don't know why. *有时效果很好,我不知道为什么。

Server: 服务器:

            /* Check if file exists */
            if(access(retrFileName, F_OK) == 0){

                /* Open file */
                fd = open(retrFileName, O_RDONLY); 
                lseek(fd, 0, SEEK_SET);
                if (fd == -1){
                        fprintf(stderr, "Error opening file --> %s", strerror(errno));

                        exit(EXIT_FAILURE);
                }

                /* Get file stats */
                if (fstat(fd, &fileStat) < 0){
                        fprintf(stderr, "Error fstat --> %s", strerror(errno));
                        exit(EXIT_FAILURE);
                }

                sprintf(fileSize, "%li", fileStat.st_size);

                /* Sending file data */
                offset = 0;
                remainData = fileStat.st_size;
                while (((sentBytes = sendfile(clientSock, fd, &offset, 512)) == 512) && (remainData > 0)) {
                        remainData -= sentBytes;
                        fprintf(stdout, "Server envio %d bytes del file, offset ahora vale: %li y quedan = %d bytes\n", sentBytes, offset, remainData);
                }
                remainData -= sentBytes;
                fprintf(stdout, "Server envio %d bytes del file, offset ahora vale: %li y quedan = %d bytes\n", sentBytes, offset, remainData);//do while
                close(fd);////////////////////////
                send(clientSock, NICETRANSFER, sizeof(NICETRANSFER), 0); //LO METE AL ARCHIVO
                printf("send\n");
                //close(clientSock);///////////

            }
            else{
                send(clientSock, FILEERROR, sizeof(FILEERROR), 0);
                printf("send\n");
            }


        }

Client: 客户:

/* Open file */
            receivedFile = fopen("r.txt", "wb");
            if (receivedFile == NULL){
                fprintf(stderr, "Failed to open file --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
            }

            /* Write to the file */
            int contador = 0;
            int remainData = fileSize;
            do{
                if(remainData < 512)
                    bytesLeidos = recv(clientSock, readingBuffer, remainData, 0);
                else
                    bytesLeidos = recv(clientSock, readingBuffer, 512, 0);

                fwrite(readingBuffer, bytesLeidos, 1, receivedFile);

                remainData -= 512;
                contador += 512;
                printf("bytesleidos: %li, contador: %d:\n%s\n\n", bytesLeidos, contador, readingBuffer);

            }while(contador < fileSize);
            fclose(receivedFile);

Golden rule of socket programming: Always check the return value from recv . 套接字编程的黄金法则:始终检查recv的返回值。 It's not always what you think it will be. 这并不总是您所想的。

Even though you "send" 512 bytes at a time, you are in no way guaranteed that TCP will deliver the same number of bytes at a time to the receiver. 即使您一次“发送” 512个字节,也无法保证TCP一次将相同数量的字节发送给接收方。 TCP segmentation, IP fragmentation, and general Internet weirdness will cause the recv side to get an arbitrary number of bytes at a time. TCP分段,IP分段和一般的Internet异常会导致recv一次获取任意数量的字节。

Hence, your hardcoded assumption that recv will always return 512 is incorrect: 因此,您对recv将始终返回512的硬编码假设是不正确的:

remainData -= 512;
contador += 512;

Instead, you should be saying: 相反,您应该说:

remainData -= bytesLeidos;
contador += bytesLeidos;

An you need to check for errors and socket closing too. 您还需要检查错误和套接字关闭。

This is an improved main loop for your client code: 这是客户端代码的改进的主循环:

while (remainData > 0)
{
    size_t recvSize = (remainData >= 512) ? 512 : remainData;
    bytesLeidos = recv(clientSock, readingBuffer, recvSize, 0);
    if (bytesLeidos > 0)
    {
        fwrite(readingBuffer, bytesLeidos, 1, receivedFile);
        remainData -= bytesLeidos;
        contador += bytesLeidos;

        /* null terminate readingBuffer so garbage isn't printed.*/
        /* Make sure readingBuffer is allocated to be at least */
        /*  N+1 bytes (513) to account for this character being appended. */

        readingBuffer[bytesLeidos] = '\0'; 
        printf("bytesleidos: %li, contador: %d:\n%s\n\n", bytesLeidos, contador, readingBuffer);
    }
    else if (bytesLeidos == 0)
    {
        /* remote side closed connection */
        printf("Remote side exited connection\n");
        break;   
    }
    else if (bytesLeidos < 0)
    {
         /* connection error */
        printf("Connection error\n");
        break;   
    }
}

I solved my problem!! 我解决了我的问题! I needed to sync both client and server. 我需要同步客户端和服务器。 To do so, the server send the size of the file and waits for an answer for the client with recv . 为此,服务器发送文件的大小,并使用recv等待客户端的答案。 When the client recieve the file size, it send a "" message. 当客户端收到文件大小时,它将send “”消息。

I don't know if this is the correct solution, but this way you can sync server and client. 我不知道这是否是正确的解决方案,但是您可以通过这种方式同步服务器和客户端。

After sync, the server send the respective file normally with sendfile 同步后,服务器使用sendfile正常发送各自的文件

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM