一尘不染

带套接字的多线程文件传输

linux

我正在尝试在C中创建一个多线程服务器-
客户端文件传输系统。有些客户端将发送或列出或做其他选择(在交换机的情况下,您可以看到),而服务器则存储文件并提供大量服务客户。

就我所知,多线程意识形态确实很困难。它需要太多的经验而不是知识。我已经在该项目上工作了一个多星期,但我一直无法解决这些问题。

有4个选择:第一个是在其目录中列出客户端的本地文件,第二个是在客户端和服务器之间传输的列表文件,第三个是从用户读取文件名并将文件复制到服务器的目录中。

我的关键问题是关于多线程。我无法连接多个客户端。我已经读过a到z堆的代码,但是我确实无法捕捉到我的错误而陷入困境。

另一个问题是,客户端将在SIGINT被捕获时结束,但是,例如,在按ctrl-
c选择列表文件后,它不会停止。服务器文件也有同样的问题。与客户端捕获相比,这更麻烦,因为当服务器到达时SIGINT,客户端将分别与服务器断开连接。

感谢您的帮助!


服务器

/*
 Soner
 Receive a file over a socket.

 Saves it to output.tmp by default.

 Interface:

 ./executable [<port>]

 Defaults:

 - output_file: output.tmp
 - port: 12345
 */

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.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>

#include <pthread.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

enum { PORTSIZE = 5 };

void* forClient(void* ptr);
void sig_handler(int signo)
{
    if (signo == SIGINT)
        printf("!!  OUCH,  CTRL - C received  by server !!\n");
}

int main(int argc, char **argv) {
    struct addrinfo hints, *res;
    int enable = 1;
    int filefd;
    int server_sockfd;
    unsigned short server_port = 12345u;
    char portNum[PORTSIZE];

    socklen_t client_len[BUFSIZ];
    struct sockaddr_in client_address[BUFSIZ];
    int client_sockfd[BUFSIZ];
    int socket_index = 0;

    pthread_t threads[BUFSIZ];

    if (argc != 2) {
        fprintf(stderr, "Usage   ./server  <port>\n");
        exit(EXIT_FAILURE);
    }
    server_port = strtol(argv[1], NULL, 10);

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;       //ipv4
    hints.ai_socktype = SOCK_STREAM; // tcp
    hints.ai_flags = AI_PASSIVE;     // fill in my IP for me

    sprintf(portNum, "%d", server_port);
    getaddrinfo(NULL, portNum, &hints, &res);

    server_sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, (SO_REUSEPORT | SO_REUSEADDR), &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    if (bind(server_sockfd, res->ai_addr, res->ai_addrlen) == -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[socket_index] = sizeof(client_address[socket_index]);
        puts("waiting for client");
        client_sockfd[socket_index] = accept(
                               server_sockfd,
                               (struct sockaddr*)&client_address[socket_index],
                               &client_len[socket_index]
                               );
        if (client_sockfd[socket_index] < 0) {
            perror("Cannot accept connection\n");
            close(server_sockfd);
            exit(EXIT_FAILURE);
        }

        pthread_create( &threads[socket_index], NULL, forClient, (void*)client_sockfd[socket_index]);

        if(BUFSIZ == socket_index) {
            socket_index = 0;
        } else {
            ++socket_index;
        }

        pthread_join(threads[socket_index], NULL);
        close(filefd);
        close(client_sockfd[socket_index]);
    }
    return EXIT_SUCCESS;
}
void* forClient(void* ptr) {
    int connect_socket = (int) ptr;
    int filefd;
    ssize_t read_return;
    char buffer[BUFSIZ];
    char *file_path;
    char receiveFileName[BUFSIZ];

    int ret = 1;
    // Thread number means client's id
    printf("Thread number %ld\n", pthread_self());
    pthread_mutex_lock( &mutex1 );

    // until stop receiving go on taking information
    while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {

        file_path = receiveFileName;

        fprintf(stderr, "is the file name received? ?   =>  %s\n", file_path);

        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(connect_socket, 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);
    }

    pthread_mutex_unlock( &mutex1 );

    fprintf(stderr, "Client dropped connection\n");
    pthread_exit(&ret);
}

客户端

/*
 Soner
 Send a file over a socket.

 Interface:

 ./executable [<sever_hostname> [<port>]]

 Defaults:

 - server_hostname: 127.0.0.1
 - port: 12345
 */

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.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>

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

void
sig_handler(int signo)
{
    if (signo == SIGINT)
        printf("!!  OUCH,  CTRL - C received on client  !!\n");
}

int
main(int argc, char **argv)
{
    struct addrinfo hints,
    *res;
    char *server_hostname = "127.0.0.1";
    char file_path[BUFSIZ];
    char *server_reply = NULL;
    char *user_input = NULL;
    char buffer[BUFSIZ];
    int filefd;
    int sockfd;
    ssize_t read_return;
    struct hostent *hostent;
    unsigned short server_port = 12345;
    char portNum[PORTSIZE];
    char remote_file[BUFSIZ];
    int select;
    char *client_server_files[BUFSIZ];
    int i = 0;
    int j;

    // char filename_to_send[BUFSIZ];

    if (argc != 3) {
        fprintf(stderr, "Usage   ./client  <ip>  <port>\n");
        exit(EXIT_FAILURE);
    }

    server_hostname = argv[1];
    server_port = strtol(argv[2], NULL, 10);

    /* Prepare hint (socket address input). */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;          // ipv4
    hints.ai_socktype = SOCK_STREAM;    // tcp
    hints.ai_flags = AI_PASSIVE;        // fill in my IP for me

    sprintf(portNum, "%d", server_port);
    getaddrinfo(NULL, portNum, &hints, &res);

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Do the actual connection. */
    if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }

    while (1) {
        if (signal(SIGINT, sig_handler)) {
            break;
        }

        puts("connected to the server");
        puts("-----------------");
        puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
        puts("-----------------");
        while (1) {
            scanf("%d", &select);

            switch (select) {
                case 1: // list files of client's directory
                    system("find . -maxdepth 1 -type f | sort");
                    break;

                case 2: // listServer
                    puts("---- Files btw Server and the Client ----");
                    for (j = 0; j < i; ++j) {
                        puts(client_server_files[j]);
                    }
                    break;

                case 3: // send file
                    memset(file_path, 0, sizeof file_path);
                    scanf("%s", file_path);

                    memset(remote_file, 0, sizeof remote_file);
                    // send file name to server
                    sprintf(remote_file, "%s", file_path);
                    send(sockfd, remote_file, sizeof(remote_file), 0);

                    filefd = open(file_path, O_RDONLY);
                    if (filefd == -1) {
                        perror("open send file");
                        //exit(EXIT_FAILURE);
                        break;
                    }

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

                    // add files in char pointer array
                    client_server_files[i++] = file_path;

                    close(filefd);
                    break;

                case 5:
                    free(user_input);
                    free(server_reply);
                    exit(EXIT_SUCCESS);

                default:
                    puts("Wrong selection!");
                    break;
            }

        }
    }

    free(user_input);
    free(server_reply);
    exit(EXIT_SUCCESS);
}

阅读 165

收藏
2020-06-03

共1个答案

一尘不染

我修复了其他人提到的大多数错误。

使多线程/多客户端正常工作的要点:

消除互斥。

将先前索引的所有数组合并socket_index到新的“控件”结构中。主线程为结构执行malloc,将其填充,然后将结构指针传递给线程。

pthread_join从主线程中删除并运行所有分离的线程。main不再对客户端线程进行任何关闭/清理。

客户端线程现在执行关闭/清理/释放。

即使有所有这些,服务器/客户端代码仍然需要做一些工作,但是现在,它 确实 可以与多个同时进行的客户端连接一起工作,我认为这是主要问题。

注意:之前
我已经回答了一个类似的问题:使用popen()通过套接字执行命令要特别注意“ flag”字符的讨论。

无论如何,这是代码。我已经清理了它,注释了错误和修复,并用包裹了旧/新代码#if 0。请注意,某些“旧”代码并非纯粹是原始代码,而是我的过渡版本。[请原谅免费样式清理]:


server.c:

/*
 Soner
 Receive a file over a socket.

 Saves it to output.tmp by default.

 Interface:

 ./executable [<port>]

 Defaults:

 - output_file: output.tmp
 - port: 12345
 */

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.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>

#include <pthread.h>

// NOTE: this consolidates four arrays that were indexed by socket_index
struct client {
    socklen_t client_len;
    struct sockaddr_in client_address;
    int client_sockfd;
    pthread_t thread;
};

// NOTE: no longer used/needed for true multiclient
#if 0
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
#endif

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

void *forClient(void *ptr);

void
sig_handler(int signo)
{
    if (signo == SIGINT)
        printf("!!  OUCH,  CTRL - C received  by server !!\n");
}

int
main(int argc, char **argv)
{
    struct addrinfo hints,
    *res;
    int enable = 1;
    //int filefd;  // NOTE: this is never initialized/used
    int server_sockfd;
    unsigned short server_port = 12345u;
    char portNum[PORTSIZE];

    // NOTE: now all client related data is malloc'ed
#if 0
    int socket_index = 0;
#else
    struct client *ctl;
#endif

    if (argc != 2) {
        fprintf(stderr, "Usage   ./server  <port>\n");
        exit(EXIT_FAILURE);
    }
    server_port = strtol(argv[1], NULL, 10);

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;          // ipv4
    hints.ai_socktype = SOCK_STREAM;    // tcp
    hints.ai_flags = AI_PASSIVE;        // fill in my IP for me

    sprintf(portNum, "%d", server_port);
    getaddrinfo(NULL, portNum, &hints, &res);

    server_sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, (SO_REUSEPORT | SO_REUSEADDR), &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    if (bind(server_sockfd, res->ai_addr, res->ai_addrlen) == -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);

    // NOTE: we want the threads to run detached so we don't have to wait
    // for them to do cleanup -- the thread now does its own close/cleanup
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,1);

    while (1) {
        // NOTE/BUG: using a fixed list, if you actually let threads detach,
        // you don't know which thread completes allowing its control struct
        // to be reused
        // the solution is to allocate a fresh one, fill it, pass it to the
        // thread and let the _thread_ do all the closes and cleanup
#if 0
        ctl = &control_list[socket_index];
#else
        ctl = malloc(sizeof(struct client));
        if (ctl == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
#endif

        ctl->client_len = sizeof(ctl->client_address);
        puts("waiting for client");

        ctl->client_sockfd = accept(server_sockfd,
            (struct sockaddr *) &ctl->client_address, &ctl->client_len);

        if (ctl->client_sockfd < 0) {
            perror("Cannot accept connection\n");
            close(server_sockfd);
            exit(EXIT_FAILURE);
        }

        // NOTE: we're running the threads detached now and we're passing down
        // extra information just in case the client loop needs it
#if 0
        pthread_create(&ctl->thread, NULL, forClient, ctl);
#else
        pthread_create(&ctl->thread, &attr, forClient, ctl);
#endif

#if 0
        if (BUFSIZ == socket_index) {
            socket_index = 0;
        }
        else {
            ++socket_index;
        }
#endif

        // NOTE/BUG: this is why you couldn't do multiple clients at the same
        // time -- you are doing a thread join
        // but you _had_ to because the main thread didn't know when a thread
        // was done with the control struct without the join
#if 0
        pthread_join(threads[socket_index], NULL);
        close(filefd);
        close(client_sockfd[socket_index]);
#endif
    }

    return EXIT_SUCCESS;
}

void *
forClient(void *ptr)
{
#if 0
    int connect_socket = (int) ptr;
#else
    struct client *ctl = ptr;
    int connect_socket = ctl->client_sockfd;
#endif
    int filefd;
    ssize_t read_return;
    char buffer[BUFSIZ];
    char *file_path;
    long long file_length;
    char receiveFileName[BUFSIZ];

    //int ret = 1;

    // Thread number means client's id
    printf("Thread number %ld\n", pthread_self());

    // NOTE: to run parallel threads, this prevents that
#if 0
    pthread_mutex_lock(&mutex1);
#endif

    // until stop receiving go on taking information
    while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
        // NOTE/FIX2: now we have the client send us the file length so we
        // know when to stop the read loop below
        file_length = strtoll(receiveFileName,&file_path,10);

        if (*file_path != ',') {
            fprintf(stderr,"syntax error in request -- '%s'\n",
                receiveFileName);
            exit(EXIT_FAILURE);
        }
        file_path += 1;

        fprintf(stderr, "is the file name received? ?   =>  %s [%lld bytes]\n",
            file_path,file_length);

        // NOTE: if you want to see _why_ sending the length is necessary,
        // uncomment this line and the "unable to send two files" bug will
        // reappear
        //file_length = 1LL << 62;

        filefd = open(file_path,
            O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
        if (filefd == -1) {
            perror("open");
            exit(EXIT_FAILURE);
        }

        // NOTE/BUG2/FIX: now we only read up to what we're told to read
        // previously, we would keep trying to read, so on the _second_
        // send, our read call here would get the data that _should_ have
        // gone into the recv above
        // in other words, we'd lose synchronization with what the client
        // was sending us [and we'd put the second filename into the first
        // file as data at the bottom]
        for (;  file_length > 0;  file_length -= read_return) {
            read_return = BUFSIZ;
            if (read_return > file_length)
                read_return = file_length;

            read_return = read(connect_socket, buffer, read_return);
            if (read_return == -1) {
                perror("read");
                exit(EXIT_FAILURE);
            }
            if (read_return == 0)
                break;

            if (write(filefd, buffer, read_return) == -1) {
                perror("write");
                exit(EXIT_FAILURE);
            }
        }

        fprintf(stderr,"file complete\n");

        // NOTE/BUG: filefd was never closed
#if 1
        close(filefd);
#endif
    }

#if 0
    pthread_mutex_unlock(&mutex1);
#endif

    fprintf(stderr, "Client dropped connection\n");

    // NOTE: do all client related cleanup here
    // previously, the main thread was doing the close, which is why it had
    // to do the pthread_join
    close(connect_socket);
    free(ctl);

    // NOTE: this needs a void * value like below
#if 0
    pthread_exit(&ret);
#endif

    return (void *) 0;
}

client.c:

/*
 Soner
 Send a file over a socket.

 Interface:

 ./executable [<sever_hostname> [<port>]]

 Defaults:

 - server_hostname: 127.0.0.1
 - port: 12345
 */

#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.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>

// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif

// NOTE2: the "volatile" attribute here is critical to proper operation
volatile int signo_taken;

// NOTE/BUG2: don't use BUFSIZ when you really want something else
#define MAXFILES        1000

void
sig_handler(int signo)
{

    // NOTE/BUG2/FIX: doing printf within a signal handler is _not_ [AFAIK] a
    // safe thing to do because it can foul up the internal structure data of
    // stdout if the base task was doing printf/puts and the signal occurred
    // in the middle -- there are a number of other restrictions, such as
    // _no_ malloc, etc.

    // so, just alert the base layer and let it handle things when it's in a
    // "safe" state to do so ...
    signo_taken = signo;
}

int
main(int argc, char **argv)
{
    struct addrinfo hints,
    *res;
    char *server_hostname = "127.0.0.1";
    char file_path[BUFSIZ];
    char *server_reply = NULL;
    char *user_input = NULL;
    char buffer[BUFSIZ];
    int filefd;
    int sockfd;
    struct stat st;
    ssize_t read_return;
    struct hostent *hostent;
    unsigned short server_port = 12345;
    char portNum[PORTSIZE];
    char remote_file[BUFSIZ];
    int select;
    char *client_server_files[MAXFILES];
    int i = 0;
    int j;

    // char filename_to_send[BUFSIZ];

    if (argc != 3) {
        fprintf(stderr, "Usage   ./client  <ip>  <port>\n");
        exit(EXIT_FAILURE);
    }

    server_hostname = argv[1];
    server_port = strtol(argv[2], NULL, 10);

    /* Prepare hint (socket address input). */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;          // ipv4
    hints.ai_socktype = SOCK_STREAM;    // tcp
    hints.ai_flags = AI_PASSIVE;        // fill in my IP for me

    sprintf(portNum, "%d", server_port);
    getaddrinfo(NULL, portNum, &hints, &res);

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Do the actual connection. */
    if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }

    // NOTE/FIX2: this only needs to be done once, since the desired action is
    // to [cleanly] stop the program
    signal(SIGINT, sig_handler);

    // NOTES:
    // (1) instead of using signo_taken as is done, below there are alternate
    //     ways to handle signals with sigsetjmp and siglongjmp
    // (2) but the main reason to _not_ do this is to prevent the handler
    //     from messing up a file transfer
    while (! signo_taken) {
        puts("connected to the server");
#if 0
        puts("-----------------");
        puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
        puts("-----------------");
#endif

        while (! signo_taken) {
            // NOTE: not a bug, but it helps the user to output the menu each
            // time
#if 1
            puts("-----------------");
            puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
            puts("-----------------");
#endif

            scanf("%d", &select);

            // NOTE: we should check this after _any_ call that requests user
            // input (e.g. scanf, fgets(...,stdin), etc.)
            if (signo_taken)
                break;

            switch (select) {
            case 1:                 // list files of client's directory
                system("find . -maxdepth 1 -type f | sort");
                break;

            case 2:                 // listServer
                puts("---- Files btw Server and the Client ----");
                for (j = 0; j < i; ++j) {
                    puts(client_server_files[j]);
                }
                break;

            case 3:                 // send file
                fputs("Enter filename: ",stdout);
                fflush(stdout);

                memset(file_path, 0, sizeof file_path);
                scanf("%s", file_path);

                if (signo_taken)
                    break;

                // NOTE/FIX: check the file _before_ sending request to server
                // and we [now] want to know the file length so we can send
                // that to the server so it will know when to stop receiving
#if 1
                filefd = open(file_path, O_RDONLY);
                if (filefd == -1) {
                    perror("open send file");
                    // exit(EXIT_FAILURE);
                    break;
                }

                // get the file's byte length
                if (fstat(filefd,&st) < 0) {
                    perror("stat send file");
                    // exit(EXIT_FAILURE);
                    close(filefd);
                    break;
                }
#endif

                // send file name to server
                memset(remote_file, 0, sizeof(remote_file));
#if 0
                sprintf(remote_file, "%s", file_path);
#else
                sprintf(remote_file, "%lld,%s",
                    (long long) st.st_size,file_path);
#endif
                send(sockfd, remote_file, sizeof(remote_file), 0);

                // NOTE/BUG2: this should be done above to _not_ confuse server
#if 0
                filefd = open(file_path, O_RDONLY);
                if (filefd == -1) {
                    perror("open send file");
                    // exit(EXIT_FAILURE);
                    break;
                }
#endif

                while (1) {
                    read_return = read(filefd, buffer, BUFSIZ);
                    if (read_return == 0)
                        break;

                    if (read_return == -1) {
                        perror("read");
                        // exit(EXIT_FAILURE);
                        break;
                    }

                    if (write(sockfd, buffer, read_return) == -1) {
                        perror("write");
                        // exit(EXIT_FAILURE);
                        break;
                    }
                }

                close(filefd);

                // add files in char pointer array
                // NOTE/BUG2: file_path gets overwritten, so we must save it
                // here
#if 0
                client_server_files[i++] = file_path;
#else
                if (i < MAXFILES)
                    client_server_files[i++] = strdup(file_path);
#endif

                puts("file complete");
                break;

            case 5:
                free(user_input);
                free(server_reply);
                exit(EXIT_SUCCESS);
                break;

            default:
                puts("Wrong selection!");
                break;
            }

        }
    }

    // NOTE/FIX2: we output this here when it's save to do so
    if (signo_taken)
        printf("!!  OUCH,  CTRL - C received on client  !!\n");

    free(user_input);
    free(server_reply);
    exit(EXIT_SUCCESS);
}

更新:

我已经解决了连接中断问题,但是仍然出现信号。我在文件发送和信号处理上又留下了两个问题

我已经对客户端信号处理进行了重新设计,以使其按预期方式工作(即打印消息并停止客户端)。

我还解决了只能发送一个文件的问题。要了解这一点,请考虑客户端和服务器的操作。

To send a file, client prompts for filename, does a send call with the
filename in it. It then opens the file and does a read/write loop to send the
file data to the server [and then closes the file descriptor].

To receive a file, server does a recv call to get the filename. It then
opens the file [for output] and does a read/write to write the data from the
socket to the file [and then closes the file descriptor].

Here is the problem: The termination condition for the server’s read/write
loop is to wait until the read(connect_socket,...) call returns 0. But, it
will not return zero [unless the socket has been closed].

So, now the client does a send call to send the second filename. But, the
data for this, instead of going into the server’s recv call, will merely be
part of the read buffer. That is, the second filename will just be
appended to the first file as data.

The solution is to have the client tell the server what the file size is. So,
instead of the client doing a send of filename, it now does a send of
filesize,filename

The server will now decode this filesize and split off the filename in the
recv buffer. Now, the server’s read/write loop will maintain a count of how
many bytes still need to be read and the loop stops when the remaining count
hits zero.

There were one or two other minor bugs. I’ve updated both client.c and
server.c with the bug fixes and annotations

2020-06-03