为什么我的C套接字文件传输(服务器/客户端)程序只能正确运行一次?

时间:2016-02-08 10:34:15

标签: c linux sockets gcc

这是我第一次在Stack Overflow上发帖。如果我没有正确地遵循协议,我道歉。

我有一个带有客户端和服务器组件的简单C套接字程序。程序将一个VM上的客户端文件发送到另一个VM上的服务器。程序正常运行,文件第一次成功发送。

但是,当我尝试第二次运行程序时,似乎没有传输该文件。通过调查,我发现重新启动虚拟机后,程序再次运行。为什么会这样?

这是服务器代码:

NewFileServer.c

/* Server code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "10.20.20.55"
#define FILENAME    "/home/saul/M2.py"

int main(int argc, char **argv)
{
        int server_socket;
        int peer_socket;
        socklen_t sock_len;
        ssize_t len;
        struct sockaddr_in server_addr;
        struct sockaddr_in peer_addr;
        ssize_t rec;
        int sent_bytes = 0;
        char file_size[512];
        int file_size_A;
        struct stat file_stat;
        off_t offset;
        int remain_data;
        char buffer[BUFSIZ];
        FILE *received_file;
        int closed = 0;
        int fclosed = 0;

        /* Create server socket */
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("Socket Created Successfully: %d\n", server_socket);
        /* Zeroing server_addr struct */
        memset(&server_addr, 0, sizeof(server_addr));
        /* Construct server_addr struct */
        server_addr.sin_family = AF_INET;
        //inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        server_addr.sin_port = htons(PORT_NUMBER);

        /* Bind */
        if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1)
        {
                fprintf(stderr, "Error on bind --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("Binding to socket done\n");
        /* Listening to incoming connections */
        if ((listen(server_socket, 5)) == -1)
        {
                fprintf(stderr, "Error on listen --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("No error on listen\n");

        sock_len = sizeof(struct sockaddr_in);
        peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len);

        printf("\nAccept has been performed\n");

        if (peer_socket < 0)
        {
                fprintf(stderr, "Error on accept --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }
        fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr));


        /* To receive file */
        /*____________________________________________________*/
        /* Receiving file size */

        len = recv(peer_socket, buffer, BUFSIZ, 0);
        if(len <= 0)
        {
            printf("\nEither an error has occurred or there is no data in recv: %d\n", len);
        }
        printf("\nThe message has been received and the length of it is %d\n", len);
        len = 5;
        file_size_A = atoi(buffer);
        printf("\nThe size of file_size_A is %d\n", file_size_A);
        //fprintf(stdout, "\nFile size : %d\n", file_size);

        received_file = fopen(FILENAME, "w");
        if (received_file == NULL)
        {
                fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        remain_data = file_size_A;

        while (((len = recv(peer_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0))
        {
                fwrite(buffer, sizeof(char), len, received_file);
                remain_data -= len;
                fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
        }

        printf("Len after skipping the while loop is %d\n", len);

        fclosed = fclose(received_file);

        if(fclosed != 0)
        {
            fprintf(stderr, "Failed to close received_file --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nreceived_file closed\n");
        }

        /*_____________________________________________________*/


        closed = close(peer_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close peer_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nPeer Socket closed\n");
        }

        closed = close(server_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close server_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nServer Socket closed\n");
        }

        printf("\nProcess ID is: %d\n", getpid());

        return 0;
}

以下是客户代码:

NewFileClient.c

/* Client code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "10.20.20.55"
#define FILENAME        "/home/saul2/test.py"

int main(int argc, char **argv)
{
        int client_socket;
        ssize_t len;
        struct sockaddr_in remote_addr;
        char buffer[BUFSIZ];
        char ip[BUFSIZ];
        //int file_size;
        char file_size[512];
        FILE *received_file;
        int remain_data = 0;
        int pNum = 0;
        int fd;
        struct stat file_stat;
        socklen_t       sock_len;
        struct sockaddr_in      peer_addr;
        off_t offset;
        int sent_bytes = 0;
        int closed = 0;
        int closeFd = 0;

        /* Zeroing remote_addr struct */
        memset(&remote_addr, 0, sizeof(remote_addr));

        /* Construct remote_addr struct */
        remote_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr));
        remote_addr.sin_port = htons(PORT_NUMBER);

        /* Create client socket */
        client_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fprintf(stderr, "Socket created successfully %d\n", client_socket);
        inet_ntop(AF_INET, &(remote_addr.sin_addr), ip, BUFSIZ);
        fprintf(stderr, "\nThe ip address is converted back is %s \n", ip);
        pNum = ntohs(remote_addr.sin_port);
        fprintf(stderr, "\nThe re-converted port number is, %d\n", pNum);


        /* Connect to the server */
        if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
        {
                fprintf(stderr, "Error on connect --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }




        /* To send file */
        /*______________________________________________*/
        fd = open(FILENAME, O_RDONLY);
        if (fd == -1)
        {
                fprintf(stderr, "Error opening file --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

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

                exit(EXIT_FAILURE);
        }

        fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);

        sock_len = sizeof(struct sockaddr_in);

        sprintf(file_size, "%d", file_stat.st_size);

        /* Sending file size */
        len = send(client_socket, file_size, sizeof(file_size), 0);
        if (len < 0)
        {
              fprintf(stderr, "Error on sending greetings --> %s", strerror(errno));

              exit(EXIT_FAILURE);
        }

        fprintf(stdout, "Client sent %d bytes for the size\n", len);

        offset = 0;
        remain_data = file_stat.st_size;
        /* Sending file data */
        while (((sent_bytes = sendfile(client_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0))
        {
                fprintf(stdout, "1. Client sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
                remain_data -= sent_bytes;
                fprintf(stdout, "2. Client sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
        }

        closeFd = close(fd);
        if(closeFd == -1)
        {
            fprintf(stderr, "Failed to close file --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nFile closed\n");
        }

        closed = close(client_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close client_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nClient Socket closed\n");
        }

        /*____________________________________________________*/


        //close(client_socket);
        printf("\nProcess ID is: %d\n", getpid());


        return 0;
}

最初,我认为这可能是因为一个端口处于打开状态。据我所知,我的代码关闭了它使用的所有文件和端口。我使用了netstat命令:

netstat -atn

查看开放端口。我发现客户端确实有一个开放的端口号5000。它的状态为“TIME_WAIT”。大约2分钟后,港口似乎自行关闭。我查看了这一点,对于正在关闭的端口来说,这似乎是正常的行为。

有趣的是,即使在此端口关闭后,程序仍然无法正常运行,只能在重启后才能运行。我发现的另一个奇怪的事情是,几天前,如果我在第一次成功执行程序后等待7-8分钟然后再次尝试,它将正确执行并且文件将被传输。但是,现在这不再起作用,唯一能解决问题的是系统重启。

我知道我的代码远非完美,但我的代码是否存在导致此问题的根本问题?

非常感谢您的帮助。

我正在编辑帖子以添加程序的输出。

首先,成功执行:

服务器输出:

Socket Created Successfully: 3
Binding to socket done
No error on listen

Accept has been performed
Accept peer --> 10.20.20.44

The message has been received and the length of it is 512

The size of file_size_A is 193
Receive 193 bytes and we hope :- 0 bytes
Len after skipping the while loop is 0

received_file closed

Peer Socket shutdown

Server Socket shutdown

Process ID is: 1397

客户输出:

Socket created successfully 3

The ip address is converted back is 10.20.20.55

The re-converted port number is, 5000
File Size:
193 bytes
Client sent 512 bytes for the size
1. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 193
2. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 0

File closed

Client Socket shutdown

Process ID is: 1298

现在我将在执行失败时添加输出。

服务器输出:

Socket Created Successfully: 3
Binding to socket done
No error on listen

Accept has been performed
Accept peer --> 10.20.20.44

The message has been received and the length of it is 705

The size of file_size_A is 193
Len after skipping the while loop is 0

received_file closed

Peer Socket shutdown

Server Socket shutdown

Process ID is: 1398

客户输出:

Socket created successfully 3

The ip address is converted back is 10.20.20.55

The re-converted port number is, 5000
File Size:
193 bytes
Client sent 512 bytes for the size
1. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 193
2. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 0

File closed

Client Socket shutdown

Process ID is: 1299

我还没有解决这个问题。

由于

2 个答案:

答案 0 :(得分:2)

问题是:您希望对数据进行某些分块。这在STREAM(TCP)套接字上无效,并且在您的情况下失败,当在第一个recv()中返回整个数据时 - 调用:

在这里你读取整个数据包(标题+数据):

len = recv(peer_socket, buffer, BUFSIZ, 0);

这是你的while循环:

while (((len = recv(peer_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0))

在第一个(成功的)情况下,第一个recv()的数据,但是在while()条件内的调用中。

在第二个(不成功)的情况下,在第一个recv() - 调用中读取头+数据,因此在while() - 条件中对recv()的调用返回0和while ()-loop根本不执行。

对我来说,有点不清楚,协议是如何准确定义的。如果你的标题总是512字节(这似乎是你输出的情况),那么在第一次调用recv时只读取512字节可能会有所帮助:

len = recv(peer_socket, buffer, 512, 0);

但是你仍然要确保真的读取了512个字节(否则循环直到你得到其余部分),否则它会失去同步。

底线是:

永远不会期望来自流套接字的数据在从其接收数据时以某种方式被分块。总是指定要读取的字节数,并检查是否已经读取了实际 的字节数(如果没有,则再次调用read())。

答案 1 :(得分:1)

看起来像是竞争条件。更确切地说,它只是偶然第一次工作,因为初始化时间导致两个客户端写入在服务器上单独读取。在以下运行中,第一个服务器读取将读取这两个写入。

密钥在这里:705 = 512(标题大小)+ 193(文件大小)。

有问题的行在这里:

    /* Receiving file size */

    len = recv(peer_socket, buffer, BUFSIZ, 0);

当客户端应为消息部分发送最多512个字节时,您会读取BUFSIZ字节。如果客户端足够快,则它已将所有内容排队,并且在消息的512字节之后的第一次读取中连接数据部分。当然,没有任何内容可用于后续读取,您立即到达文件末尾并立即退出接收循环。

你应该写在NewFileServer.c:

    /* Receiving file size */

    char file_size[512];
    len = recv(peer_socket, file_size, sizeof(file_size), 0);
    ...
    file_size_A = atoi(file_size);
    ...

这可确保读取的消息仅占用其512字节,并保留文件数据以进行后续读取。