c++ - 在 TCP 线程服务器 C++ 中传输文件

标签 c++ linux multithreading sockets tcp

大家好,我有一个小问题,我应该从服务器(带有线程的 TCP 服务器到客户端)传输文件。问题出现在传输结束时,客户端收到了文件,但它卡住了,我无法再与其通信。

这是服务器

int main(int argc, char *argv[])
{
    int socket_desc, client_sock, c;
    struct sockaddr_in server, client;

//Create socket
    socket_desc = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");

//Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(2025);

//Bind
    if (bind(socket_desc, (struct sockaddr *) &server, sizeof(server)) < 0)
    {
//print the error message
        perror("bind failed. Error");
        return 1;
    }
    puts("bind done");

//Listen
    listen(socket_desc, 5);

//Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);

    int enable = 1;

//Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);
    pthread_t thread_id;

    while ((client_sock = accept(socket_desc,
                                 (struct sockaddr *) &client,
                                 (socklen_t*) &c)))
    {
        puts("Connection accepted");
        if (setsockopt(client_sock,
                       SOL_SOCKET,
                       SO_REUSEADDR,
                       &enable,
                       sizeof(int)) < 0)
            error("setsockopt(SO_REUSEADDR) failed");
        if (pthread_create(&thread_id,
                           NULL,
                           connection_handler,
                           (void*) &client_sock) < 0)
        {
            perror("could not create thread");
            return 1;
        }

//Now join the thread , so that we dont terminate before the thread
        pthread_join(thread_id, NULL);
        puts("Handler assigned");
    }

    if (client_sock < 0)
    {
        perror("accept failed");
        return 1;
    }

    return 0;
}

void *connection_handler(void *socket_desc)
{
    printf("Enter in handler");
//Get the socket descriptor
    int sock = *(int*) socket_desc;
    send_problemo(sock);

    return 0;
}

这是发送函数,我认为这是真正的问题

int send_problemo(int *sock)
{
    ssize_t read_return;
    char *file_path = "Problems/1.txt";
    char buffer[BUFSIZ];
    int filefd;
    filefd = open(file_path, O_RDONLY);
    char end[2] = "1";
    if (filefd == -1)
    {
        perror("open");
        exit (EXIT_FAILURE);
    }
    while (1)
    {
        read_return = read(filefd, buffer, BUFSIZ);
        if (read_return == 0)
        {
            printf("este 0 \n");
            break;
        }
        if (read_return == -1)
        {
            perror("read");
            exit (EXIT_FAILURE);
        }
        if (write(sock, buffer, read_return) == -1)
        {
            perror("write");
            exit (EXIT_FAILURE);
        }
    }
// close(sock);
    close(filefd);
}

客户端正常连接,并在该函数中接收文件

int recive_problemo(int *sockfd)
{
    char *file_path = "path.c";
    char buffer[BUFSIZ];
    ssize_t read_return;
    int filefd;
    filefd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    if (filefd == -1)
    {
        perror("open");
        exit (EXIT_FAILURE);
    }

    do
    {
        read_return = read(sockfd, buffer, BUFSIZ);
        if (read_return == -1)
        {
            perror("read");
            exit (EXIT_FAILURE);
        }
        if (write(filefd, buffer, read_return) == -1)
        {
            perror("write");
            exit (EXIT_FAILURE);
        }
    } while (read_return > 0);
    close(filefd);
}

我设法解决了这个问题。如果我从服务器关闭(SHUT_WR),客户端不再卡住,但我想与它进一步通信。 如果我从客户端转移到服务器,也具有相同的功能,它工作完美,所以有人可以帮助我吗?

最佳答案

do
{
    read_return = read(sockfd, buffer, BUFSIZ);
    // error handling
    // file write
} while (read_return > 0);

将继续循环,直到套接字关闭或出现错误。它无法判断文件是否已完成。

常见的解决方案是关闭套接字(但您不希望这样做)并建立通信协议(protocol),以便您知道文件何时完成并可以退出循环。

为了让事情变得非常简单,我建议在发送文件之前发送文件的长度。循环现在看起来像:

uint64_t file_len;
read_return = recv(sockfd, &file_len, sizeof(file_len), MSG_WAITALL);
if (read_return == sizeof(file_len))
{
    // Strongly consider handling the endian of file_len here
    while (file_len)
    {
        size_t readmax = std::min(file_len, BUFSIZ);
        read_return = read(sockfd, buffer, readmax); 
        if (read_return > 0)
        {
            if (write(filefd, buffer, read_return) == -1)
            {
                perror("write");
                exit (EXIT_FAILURE);
            }
            file_len -= read_return;
        }
        else
        {
             // handle error
             // exit loop if not recoverable
        }
    }
}

服务器端负责获取和发送文件的长度。我不会深入讨论这个问题,因为有太多不同的方法可以获取文件的长度。选择您最喜欢的。

Documentation on recv and MSG_WAITALL .

关于c++ - 在 TCP 线程服务器 C++ 中传输文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47823736/

相关文章:

c# - 如何在 C# 中正确制作 Dllimport?

json - 如何过滤位于 [ 和 ] 内的零件?

linux - 是否有一个内核模块可以准确地返回简单的 'ifconfig' 的作用?

WPF 在单独的线程中运行动画

c++ - 如何将 MATLAB Lab 色标转换为 OpenCV Lab 色标?

python - 如何使用 CUDA CURAND 保存和恢复随机数生成器的状态?

c++ - 如果不知道要读取的字符数,如何使用 fgets?

java - 如何为您的 Java 应用程序创建 native 二进制文件?

c# - 获取错误 System.IO.IOException : 'The process cannot access the file'

java - 设计具有可变条目到期时间的 Guava LoadingCache