在Linux上使用TCP进行文件传输

时间:2022-05-01 21:16:10

I'm trying TCP file transfer on Linux. After establishing the connection, the server should send "send.txt" to the client, and the client receives the file and saves it as "receive.txt". Then the connection breaks.

我在Linux上尝试TCP文件传输。建立连接后,服务器应将“send.txt”发送到客户端,客户端接收该文件并将其保存为“receive.txt”。然后连接中断。

The correct input and output should be:

正确的输入和输出应该是:

Server terminal:

$./server &
[server] obtain socket descriptor successfully.
[server] bind tcp port 5000 in addr 0.0.0.0 successfully.
[server] listening the port 5000 successfully.
[server] server has got connect from 127.0.0.1.
[server] send send.txt to the client…ok!
[server] connection closed.

Client terminal:

$./client 
[client] connected to server at port 5000…ok!
[client] receive file sent by server to receive.txt…ok!
[client] connection lost.

And both the server and client should exit after the process.

服务器和客户端都应该在进程后退出。

But what I've got now gives

但我现在得到的是

$ ./server &
[server] obtain socket descriptor successfully.
[server] bind tcp port 5000 in addr 0.0.0.0 sucessfully.
[server] listening the port 5000 sucessfully.
[server] server has got connect from 127.0.0.1.
[server] send send.txt to the client...ok!
[server] connection closed.

/*Here the server doesn't exit*/

$ ./client
[client] connected to server at port 5000...ok!

/*Here the client doesn't exit*/

Also, an EMPTY "receive.txt" is generated.

此外,生成EMPTY“receive.txt”。

My code was first written for transferring simple strings, and it worked correctly. So I guess the problem lies in the file transferring part.

我的代码最初是为传输简单字符串而编写的,并且它正常工作。所以我猜问题在于文件传输部分。

My code is as follows:

我的代码如下:

server.c

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#define PORT 5000 // The port which is communicate with server
#define BACKLOG 10
#define LENGTH 512 // Buffer length
int main ()
{
    int sockfd; // Socket file descriptor
    int nsockfd; // New Socket file descriptor
    int num;
    int sin_size; // to store struct size
    struct sockaddr_in addr_local;
    struct sockaddr_in addr_remote;
    /* Get the Socket file descriptor */
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
    {
        printf ("ERROR: Failed to obtain Socket Descriptor.\n");
        return (0);
    }
    else printf ("[server] obtain socket descriptor successfully.\n");
    /* Fill the local socket address struct */
    addr_local.sin_family = AF_INET; // Protocol Family
    addr_local.sin_port = htons(PORT); // Port number
    addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address
    bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct
    /* Bind a special Port */
    if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 )
    {
        printf ("ERROR: Failed to bind Port %d.\n",PORT);
        return (0);
    }
    else printf("[server] bind tcp port %d in addr 0.0.0.0 sucessfully.\n",PORT);
    /* Listen remote connect/calling */
    if(listen(sockfd,BACKLOG) == -1)
    {
        printf ("ERROR: Failed to listen Port %d.\n", PORT);
        return (0);
    }
    else printf ("[server] listening the port %d sucessfully.\n", PORT);
    int success = 0;
    while(success == 0)
    {
        sin_size = sizeof(struct sockaddr_in);
        /* Wait a connection, and obtain a new socket file despriptor for single connection */
        if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1) 
            printf ("ERROR: Obtain new Socket Despcritor error.\n");
        else printf ("[server] server has got connect from %s.\n", inet_ntoa(addr_remote.sin_addr));
        /* Child process */
        if(!fork())
        {
            char* f_name = "send.txt";
            char sdbuf[LENGTH]; // Send buffer
            printf("[server] send %s to the client...", f_name);
            FILE *fp = fopen(f_name, "r");
            if(fp == NULL)
            {
                printf("ERROR: File %s not found.\n", f_name);
                exit(1);
            }
            bzero(sdbuf, LENGTH);
            int f_block_sz;
            while((f_block_sz = fread(sdbuf, sizeof(char), LENGTH, fp))>0)
            {
                if(send(nsockfd, sdbuf, f_block_sz, 0) < 0)
                {
                    printf("ERROR: Failed to send file %s.\n", f_name);
                    break;
                }
                bzero(sdbuf, LENGTH);
            }
            printf("ok!\n");
            success = 1;
            close(nsockfd);
            printf("[server] connection closed.\n");
            while(waitpid(-1, NULL, WNOHANG) > 0);
        }
    }
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORT 5000
#define LENGTH 512 // Buffer length
int main(int argc, char *argv[])
{
    int sockfd; // Socket file descriptor
    char revbuf[LENGTH]; // Receiver buffer
    struct sockaddr_in remote_addr; 
    /* Get the Socket file descriptor */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("ERROR: Failed to obtain Socket Descriptor!\n");
        return (0);
    }
    /* Fill the socket address struct */
    remote_addr.sin_family = AF_INET; 
    remote_addr.sin_port = htons(PORT); 
    inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); 
    bzero(&(remote_addr.sin_zero), 8);
    /* Try to connect the remote */
    if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
    {
        printf ("ERROR: Failed to connect to the host!\n");
        return (0);
    }
    else printf("[client] connected to server at port %d...ok!\n", PORT);
    //printf ("OK: Have connected to %s\n",argv[1]);
    printf("[client] receive file sent by server to receive.txt...");
    char* f_name = "receive.txt";
    FILE *fp = fopen(f_name, "a");
    if(fp == NULL) printf("File %s cannot be opened.\n", f_name);
    else
    {
        bzero(revbuf, LENGTH);
        int f_block_sz = 0;
        int success = 0;
        while(success == 0)
        {
            while(f_block_sz = recv(sockfd, revbuf, LENGTH, 0))
            {
                if(f_block_sz < 0)
                {
                    printf("Receive file error.\n");
                    break;
                }
                int write_sz = fwrite(revbuf, sizeof(char), f_block_sz, fp);
                if(write_sz < f_block_sz)
                {
                    printf("File write failed.\n");
                    break;
                }
                bzero(revbuf, LENGTH);
            }
            printf("ok!\n");
            success = 1;
            fclose(fp);
        }
    }
    close (sockfd);
    printf("[client] connection lost.\n");
    return (0);
}

Thank you very much!

非常感谢你!

2 个解决方案

#1


4  

You need to add code to the f_block_sz code. recv() has a few posible return values:

您需要将代码添加到f_block_sz代码中。 recv()有一些可靠的返回值:

  • <0 -- Error, error number in errno (errno.h)
  • <0 - 错误,errno中的错误号(errno.h)

  • 0 -- Connection closed
  • 0 - 连接已关闭

  • >0 -- Data read, number of bytes
  • > 0 - 数据读取,字节数

You need to handle the second case. Add this else case:

你需要处理第二种情况。添加其他案例:

else if(f_block_sz)
{
  break;
}

So that the loop will be broken when the server closes the connection, and your code will print you [client] connection lost and exit.

因此,当服务器关闭连接时,循环将被破坏,并且您的代码将打印您[客户端]连接丢失并退出。

#2


3  

You have another problem in that your server program is multiprocess and forks every time it gets an incoming connection. The parent stays alive to accept new connections and the child processes the connection. The exit from the child process is not enough to cause the parent to exit as well.

您还有另一个问题,即您的服务器程序是多进程的,并且每次获取传入连接时都会分叉。父级保持活动状态以接受新连接,子级处理连接。子进程的退出不足以导致父进程退出。

If you only want to process one connection then don't use fork.

如果您只想处理一个连接,请不要使用fork。

If you wish to continue your current setup then you will need to change the child process to use _exit instead of exit and get the parent process to handle the SIGCHLD signal (received when a child process exits).

如果您希望继续当前设置,则需要将子进程更改为使用_exit而不是exit,并让父进程处理SIGCHLD信号(在子进程退出时接收)。

ie.

#include <signal.h>

void terminate(int signal) {
    // close sockfd -- you will need to make it global
    // or have terminate alter some global variable that main can monitor to 
    // detect when it is meant to exit
    exit(0); // don't exit if you choose the second option
}

int main() {

    signal(SIGCHLD, terminate);

    ...

}

#1


4  

You need to add code to the f_block_sz code. recv() has a few posible return values:

您需要将代码添加到f_block_sz代码中。 recv()有一些可靠的返回值:

  • <0 -- Error, error number in errno (errno.h)
  • <0 - 错误,errno中的错误号(errno.h)

  • 0 -- Connection closed
  • 0 - 连接已关闭

  • >0 -- Data read, number of bytes
  • > 0 - 数据读取,字节数

You need to handle the second case. Add this else case:

你需要处理第二种情况。添加其他案例:

else if(f_block_sz)
{
  break;
}

So that the loop will be broken when the server closes the connection, and your code will print you [client] connection lost and exit.

因此,当服务器关闭连接时,循环将被破坏,并且您的代码将打印您[客户端]连接丢失并退出。

#2


3  

You have another problem in that your server program is multiprocess and forks every time it gets an incoming connection. The parent stays alive to accept new connections and the child processes the connection. The exit from the child process is not enough to cause the parent to exit as well.

您还有另一个问题,即您的服务器程序是多进程的,并且每次获取传入连接时都会分叉。父级保持活动状态以接受新连接,子级处理连接。子进程的退出不足以导致父进程退出。

If you only want to process one connection then don't use fork.

如果您只想处理一个连接,请不要使用fork。

If you wish to continue your current setup then you will need to change the child process to use _exit instead of exit and get the parent process to handle the SIGCHLD signal (received when a child process exits).

如果您希望继续当前设置,则需要将子进程更改为使用_exit而不是exit,并让父进程处理SIGCHLD信号(在子进程退出时接收)。

ie.

#include <signal.h>

void terminate(int signal) {
    // close sockfd -- you will need to make it global
    // or have terminate alter some global variable that main can monitor to 
    // detect when it is meant to exit
    exit(0); // don't exit if you choose the second option
}

int main() {

    signal(SIGCHLD, terminate);

    ...

}