使用套接字

时间:2016-05-28 09:52:59

标签: c linux multithreading sockets

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

就我所见,多线程意识形态真的很难。它需要太多的经验而不是知识。我已经在这个项目上工作了一个多星期,而且我还没有能够解决这些问题。

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

我这里的重要问题是关于多线程。我无法连接多个客户端。我已经阅读了从a到z堆的代码,但我真的无法理解我的错误而且卡住了。

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

感谢您的帮助!

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 <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);
}

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

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);
}

1 个答案:

答案 0 :(得分:1)

我修复了其他人提到的大部分错误。

获得多线程/多客户端的关键点:

消除互斥锁。

将先前由socket_index编入索引的所有数组合并到一个新的&#34;控件&#34;结构。主线程为struct执行malloc,填充它,并将结构指针传递给线程。

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

客户端线程现在执行关闭/清理/免费。

即便如此,服务器/客户端代码仍然需要一些工作,但现在, 使用多个同时发生的客户端连接,我认为这是主要问题。

注意:之前我已经回答了类似的问题:executing commands via sockets with popen()要特别注意对#34;标志的讨论&#34;字符。

无论如何,这是代码。我已对其进行了清理,对错误和修复进行了注释,并使用#if 0包装了旧/新代码。注意一些&#34; old&#34;代码不是纯粹的原始代码,而是我的临时版本。 [请原谅无偿的风格清理]:

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);
}

<强>更新

  

我已解决了连接中断问题,但信号仍在发生。我在文件发送和信号处理方面留下了两个问题

我已经重新设计了客户端信号处理,以便它按预期工作[即打印消息并停止客户端]。

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

要发送文件,客户端提示输入文件名,请使用文件名进行send调用。然后它打开文件并执行读/写循环以将文件数据发送到服务器[然后关闭文件描述符]。

要接收文件,服务器会进行recv调用以获取文件名。然后它打开文件[用于输出]并执行读/写操作以将数据从套接字写入文件[然后关闭文件描述符]。

问题在于:服务器读/写循环的终止条件是等到read(connect_socket,...)调用返回0.但是,它将返回零[除非套接字已关闭]。

因此,现在客户端进行send调用以发送第二个文件名。但是,这个数据不是进入服务器的recv调用,而只是read缓冲区的一部分。也就是说,第二个文件名将作为数据附加到第一个文件

解决方案是让客户端告诉服务器文件大小是多少。因此,代替客户执行send filename,它现在执行send filesize,filename

服务器现在将解码此文件大小并在recv缓冲区中拆分文件名。现在,服务器的读/写循环将保持仍然需要读取多少字节的计数,并且当剩余计数达到零时循环停止。

还有一两个小错误。我已经使用错误修复和注释

更新了client.c和server.c