c - Printf 使程序在 C 中工作,htonl 和 ntohl 不工作?

标签 c linux network-programming printf endianness

这是针对linux系统的,C语言,涉及到网络编程。它用于文件传输程序。

我一直遇到这个问题,这段代码无法预测。它要么完全成功,要么客户端中的 while 循环永远不会结束。我发现这是因为 fileLength 变量有时会是一个巨大的(负或正)值,我认为这是由于使用 ntohl 时犯了一些错误。当我输入 print 语句时,它似乎运行完美,没有错误。

这是客户端代码:

        //...here includes relevant header files

        int main (int argc, char *argv[]) {
            //socket file descriptor
            int sockfd;

            if (argc != 2) {
                fprintf (stderr, "usage: client hostname\n");
                exit(1);
            }

            //...creates socket file descriptor, connects to server


            //create buffer for filename
            char name[256];
            //recieve filename into name buffer, bytes recieved stored in numbytes
            if((numbytes = recv (sockfd, name, 255 * sizeof (char), 0)) == -1) {
                perror ("recv");
                exit(1);
            }
            //Null terminator after the filename
            name[numbytes] = '\0';
            //length of the file to recieve from server
            long fl;
            memset(&fl, 0, sizeof fl);
            //recieve filelength from server
            if((numbytes = recv (sockfd, &fl, sizeof(long), 0)) == -1) {
                perror ("recv");
                exit(1);
            }

            //convert filelength to host format
            long fileLength = ntohl(fl);


            //check to make sure file does not exist, so that the application will not overwrite exisitng files
            if (fopen (name, "r") != NULL) {
                fprintf (stderr, "file already present in client directory\n");
                exit(1);
            }
            //open file called name in write mode
            FILE *filefd = fopen (name, "wb");
            //variable stating amount of data recieved
            long bytesTransferred = 0;
            //Until the file is recieved, keep recieving
            while (bytesTransferred < fileLength) {
                printf("transferred: %d\ntotal: %d\n", bytesTransferred, fileLength);
                //set counter at beginning of unwritten segment
                fseek(filefd, bytesTransferred, SEEK_SET);
                //buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
                char buf[256];
                //recieve segment from server
                if ((numbytes = recv (sockfd, buf, sizeof buf, 0)) == -1) {
                    perror ("recv");
                    exit(1);
                }

                //first byte of buffer, stating number of bytes of data in recieved segment
                //converting from char to short requires adding 128, since the char ranges from -128 to 127
                short bufLength = buf[0] + 128;

                //write buffer into file, starting after the first byte of the buffer
                fwrite (buf + 1, 1, bufLength * sizeof (char), filefd);
                //add number of bytes of data recieved to bytesTransferred
                bytesTransferred += bufLength;

            }
            fclose (filefd);
            close (sockfd);

            return 0;
        }

这是服务器代码:

        //...here includes relevant header files

        int main (int argc, char *argv[]) {
            if (argc != 2) {
                fprintf (stderr, "usage: server filename\n");
                exit(1);
            }
            //socket file descriptor, file descriptor for specific client connections
            int sockfd, new_fd;


            //...get socket file descriptor for sockfd, bind sockfd to predetermined port, listen for incoming connections



            //...reaps zombie processes


            printf("awaiting connections...\n");

            while(1) {
                //...accepts any incoming connections, gets file descriptor and assigns to new_fd

                if (!fork()) {
                    //close socket file discriptor, only need file descriptor for specific client connection
                    close (sockfd);
                    //open a file for reading
                    FILE *filefd = fopen (argv[1], "rb");
                    //send filename to client
                    if (send (new_fd, argv[1], strlen (argv[1]) * sizeof(char), 0) == -1)
                    { perror ("send"); }
                    //put counter at end of selected file, and  find length
                    fseek (filefd, 0, SEEK_END);
                    long fileLength = ftell (filefd);
                    //convert length to network form and send it to client

                    long fl = htonl(fileLength);
                    //Are we sure this is sending all the bytes??? TEST
                    if (send (new_fd, &fl, sizeof fl, 0) == -1)
                    { perror ("send"); }
                    //variable stating amount of data unsent
                    long len = fileLength;
                    //Until file is sent, keep sending
                    while(len > 0) {
                        printf("remaining: %d\ntotal: %d\n", len, fileLength);
                        //set counter at beginning of unread segment
                        fseek (filefd, fileLength - len, SEEK_SET);
                        //length of the segment; 255 unless last segment
                        short bufLength;
                        if (len > 255) {
                            len -= 255;
                            bufLength = 255;
                        } else {
                            bufLength = len;
                            len = 0;
                        }
                        //buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
                        char buf[256];
                        //Set first byte of buffer as the length of the segment
                        //converting short to char requires subtracting 128
                        buf[0] = bufLength - 128;
                        //read file into the buffer starting after the first byte of the buffer
                        fread(buf + 1, 1, bufLength * sizeof(char), filefd);
                        //Send data too client
                        if (send (new_fd, buf, sizeof buf, 0) == -1)
                        { perror ("send"); }
                    }
                    fclose (filefd);
                    close (new_fd);
                    exit (0);
                }
                close (new_fd);
            }

            return 0;
        }

注意:我已经稍微简化了代码,希望它更清晰。 以//... 开头的任何内容都代表一堆代码

最佳答案

您似乎假设每个 send() 要么传输指定的全部字节数,要么出错,并且每个都将与 recv() 完美配对 在另一边,使得 recv() 接收到由 send() 发送的字节数(或错误输出),仅此而已不少于。这些都不是安全的假设。

您没有显示用于设置网络连接的代码。如果您使用的是基于数据报的协议(protocol)(即 UDP),那么您更有可能获得预期的发送/接收边界匹配,但您需要考虑数据包丢失或损坏的可能性。如果您使用的是基于流的协议(protocol)(即 TCP),那么您不必太担心数据丢失或损坏,但您根本没有理由期望边界匹配行为。

你至少需要三样东西:

  • 网络层之上的应用层协议(protocol)。您已经掌握了其中的一部分,例如您如何首先传输文件长度以告知客户预期的内容,但您需要对所有传输的非预定、固定长度的数据执行类似的操作。或者,发明另一种方式来传达数据边界。

  • 每个旨在传输一个以上字节的 send()/write() 都必须在一个循环中执行,以适应被分成多个部分的传输.返回值告诉您有多少请求的字节已传输(或至少有多少已移交给网络堆栈),如果少于请求的字节数,您必须循环返回以尝试传输其余字节。

  • 旨在传输一个以上字节的每个 recv()/read() 都必须在一个循环中执行,以适应被分成多个部分的传输.我建议按照 send() 中描述的相同行构建它,但您也可以选择接收数据,直到看到预先安排的分隔符。然而,基于定界符的方法更为复杂,因为它需要在接收端进行额外的缓冲。

如果没有这些措施,您的服务器和客户端很容易失去同步。其中可能的结果是客户端将部分文件名或部分文件内容解释为文件长度。

关于c - Printf 使程序在 C 中工作,htonl 和 ntohl 不工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39725968/

相关文章:

linux - 无法为 Ubuntu 安装 LightTable

http - 从 HTTP I/O 错误中恢复

c++ - GetAdaptersAddresses() 未给出正确的 IP 地址

c++ - 如何将浮点文字转换为相同位模式的无符号整数

c - 负值耗时

c - 在cuda中,加载到共享内存比加载到寄存器慢

c - 使用函数增加动态数组大小;无错误,下一个尺寸无效(快)

c - 实现 HEAD 和 GET 请求,C 中的简单 Web 服务器

linux - 关于内存泄漏需要帮助

python - 获取与给定 CIDR 匹配的子网列表