Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用C/C+;在Linux的socket编程中发送和接收文件+;(GCC/G+;+;)_C++_C_Linux_Sockets_Network Programming - Fatal编程技术网

C++ 使用C/C+;在Linux的socket编程中发送和接收文件+;(GCC/G+;+;)

C++ 使用C/C+;在Linux的socket编程中发送和接收文件+;(GCC/G+;+;),c++,c,linux,sockets,network-programming,C++,C,Linux,Sockets,Network Programming,我想使用套接字和能够发送和接收文件的C/C++语言在Linux上实现一个客户机-服务器体系结构。有没有什么图书馆可以让这项任务变得简单?任何人都可以提供一个例子吗?做一个人2发送文件。您只需打开客户端上的源文件和服务器上的目标文件,然后调用sendfile,内核就会剪切并移动数据。最可移植的解决方案就是将文件分块读取,然后以循环方式将数据写入套接字(同样,在接收文件时也是如此)。您可以将一个缓冲区分配到该缓冲区,然后从该缓冲区分配到套接字(您还可以使用send和recv,这是套接字特定的数据写入

我想使用套接字和能够发送和接收文件的C/C++语言在Linux上实现一个客户机-服务器体系结构。有没有什么图书馆可以让这项任务变得简单?任何人都可以提供一个例子吗?

做一个
人2发送文件。您只需打开客户端上的源文件和服务器上的目标文件,然后调用sendfile,内核就会剪切并移动数据。

最可移植的解决方案就是将文件分块读取,然后以循环方式将数据写入套接字(同样,在接收文件时也是如此)。您可以将一个缓冲区分配到该缓冲区,然后从该缓冲区分配到套接字(您还可以使用
send
recv
,这是套接字特定的数据写入和读取方式)。大纲将如下所示:

while (1) {
    // Read data into buffer.  We may not have enough to fill up buffer, so we
    // store how many bytes were actually read in bytes_read.
    int bytes_read = read(input_file, buffer, sizeof(buffer));
    if (bytes_read == 0) // We're done reading from the file
        break;
    
    if (bytes_read < 0) {
        // handle errors
    }
    
    // You need a loop for the write, because not all of the data may be written
    // in one call; write will return how many bytes were written. p keeps
    // track of where in the buffer we are, while we decrement bytes_read
    // to keep track of how many bytes are left to write.
    void *p = buffer;
    while (bytes_read > 0) {
        int bytes_written = write(output_socket, p, bytes_read);
        if (bytes_written <= 0) {
            // handle errors
        }
        bytes_read -= bytes_written;
        p += bytes_written;
    }
}
while(1){
//将数据读入缓冲区。我们可能没有足够的数据填充缓冲区,所以
//存储实际读取的字节数(以字节为单位)。
int bytes_read=read(输入_文件、缓冲区、sizeof(缓冲区));
if(bytes_read==0)//我们已经完成了对文件的读取
打破
如果(字节数_读取<0){
//处理错误
}
//您需要一个用于写入的循环,因为并非所有数据都可以写入
//在一次调用中;write将返回写入的字节数。p
//跟踪我们在缓冲区中的位置,同时减少读取的字节数
//跟踪要写入的剩余字节数。
void*p=缓冲区;
而(字节读取>0){
int bytes_write=write(输出_套接字,p,字节_读取);

如果(字节)写入此文件将作为一个好的
发送文件
示例:

最小可运行POSIX
读取
+
写入
示例

用法:

  • 在同一台计算机上安装两台计算机

    例如,在大多数情况下,如果两台计算机都连接到您的家庭路由器,这将起作用,这就是我测试它的方式

  • 在服务器计算机上:

    printf 'ab\ncd\n' > input.tmp
    ./client input.tmp 192.168.0.10 12345
    
  • 使用
    ifconfig
    查找服务器本地IP,例如
    192.168.0.10

  • 运行:

    ./server output.tmp 12345
    
  • 在客户端计算机上:

    printf 'ab\ncd\n' > input.tmp
    ./client input.tmp 192.168.0.10 12345
    
  • 结果:在服务器计算机上创建一个文件
    output.tmp
    ,其中包含
    'ab\ncd\n'

  • 服务器.c

    /*
    Receive a file over a socket.
    
    Saves it to output.tmp by default.
    
    Interface:
    
        ./executable [<output_file> [<port>]]
    
    Defaults:
    
    - output_file: output.tmp
    - port: 12345
    */
    
    #define _XOPEN_SOURCE 700
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <arpa/inet.h>
    #include <fcntl.h>
    #include <netdb.h> /* getprotobyname */
    #include <netinet/in.h>
    #include <sys/stat.h>
    #include <sys/socket.h>
    #include <unistd.h>
    
    int main(int argc, char **argv) {
        char *file_path = "output.tmp";
        char buffer[BUFSIZ];
        char protoname[] = "tcp";
        int client_sockfd;
        int enable = 1;
        int filefd;
        int i;
        int server_sockfd;
        socklen_t client_len;
        ssize_t read_return;
        struct protoent *protoent;
        struct sockaddr_in client_address, server_address;
        unsigned short server_port = 12345u;
    
        if (argc > 1) {
            file_path = argv[1];
            if (argc > 2) {
                server_port = strtol(argv[2], NULL, 10);
            }
        }
    
        /* Create a socket and listen to it.. */
        protoent = getprotobyname(protoname);
        if (protoent == NULL) {
            perror("getprotobyname");
            exit(EXIT_FAILURE);
        }
        server_sockfd = socket(
            AF_INET,
            SOCK_STREAM,
            protoent->p_proto
        );
        if (server_sockfd == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
        if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
            perror("setsockopt(SO_REUSEADDR) failed");
            exit(EXIT_FAILURE);
        }
        server_address.sin_family = AF_INET;
        server_address.sin_addr.s_addr = htonl(INADDR_ANY);
        server_address.sin_port = htons(server_port);
        if (bind(
                server_sockfd,
                (struct sockaddr*)&server_address,
                sizeof(server_address)
            ) == -1
        ) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
        if (listen(server_sockfd, 5) == -1) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
        fprintf(stderr, "listening on port %d\n", server_port);
    
        while (1) {
            client_len = sizeof(client_address);
            puts("waiting for client");
            client_sockfd = accept(
                server_sockfd,
                (struct sockaddr*)&client_address,
                &client_len
            );
            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(client_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);
            close(client_sockfd);
        }
        return EXIT_SUCCESS;
    }
    
    /*
    通过套接字接收文件。
    默认情况下,将其保存到output.tmp。
    接口:
    /可执行文件[]]
    默认值:
    -输出文件:output.tmp
    -港口:12345
    */
    #定义_XOPEN_源700
    #包括
    #包括
    #包括

    进一步评论

    可能的改进:

    • 当前
      output.tmp
      在每次发送完成时都会被覆盖

      这就要求创建一个简单的协议,允许传递一个文件名,以便可以上载多个文件,例如:文件名到第一个换行符,最大文件名256个字符,其余的直到套接字关闭都是内容。当然,这需要卫生设施来避免冲突

      或者,我们可以创建一个服务器,对文件进行散列以查找文件名,并在磁盘(数据库)上保留从原始路径到散列的映射

    • 一次只能连接一个客户端

      如果存在连接持续很长时间的慢速客户端,这尤其有害:慢速连接会使所有人停止连接

      解决这个问题的一种方法是为每个
      接受
      分支一个进程/线程,立即重新开始侦听,并对文件使用文件锁同步

    • 添加超时,如果需要的时间太长,请关闭客户端。否则很容易执行DoS

      poll
      select
      是一些选项:

    一个简单的HTTP
    wget
    实现如下所示:


    在Ubuntu 15.10上测试。

    Brian,我猜你和florin不会得到复选标记,因为这几乎肯定是家庭作业:(您的原始答案怎么了?您首先建议在*nix框上使用“sendfile”命令,现在您已经给出了一个编程解决方案,同时删除了原始答案的所有痕迹。我没有删除原始答案的所有痕迹。我只是在其上添加了一个标准的框架,
    read
    /
    write
    循环。我认为我应该首先解释一下,因为即使使用
    sendfile
    ,您也需要使用这样一个循环来接收文件,而且因为我在后面提到了
    splice
    ,它使用相同的模式。啊,不知怎的错过了“sendfile”的保留。对不起。@Damon您有任何参考资料来获取更多信息吗?我很好奇到底得到了什么是的;它的效率比标准的用户空间
    /
    循环要低,还是效率不高?我自己实际上对它们没有太多经验,所以我想了解更多关于gotchas的信息。在同一个程序中接收文件如何?关于:
    如果(写(sockfd,buffer,read\u return)=-1){
    在套接字上,可能会发生部分写入,因此应检查是否已写入所有字节,如果未写入,则计划再次调用
    write()
    和仍需要写入的部分数据written@user3629249谢谢,我添加了一条评论。转换到
    文件*
    然后使用
    fwrite
    行吗?
    fwrite()
    函数用于文本数据并允许格式化。
    write()
    函数也可以处理二进制数据,但没有任何格式化功能。当
    write()
    失败且程序即将退出时,应调用
    close()
    socketfd
    上,要让服务器知道它,“服务器”应该检查
    accept()
    的返回值以确保操作成功。调用
    read()
    后的“服务器”应该立即检查返回值0