是什么导致服务器一遍又一遍地读取客户端的最后一条消息?

时间:2019-07-17 18:15:46

标签: c multithreading unix server posix

鉴于服务器的代码,job_read函数中出现了一个无法解释的错误。

如果此服务器正在与客户端连接,则似乎一切正常-尽管可能还有其他一些错误。如果客户端连接,则将启动一个新线程,然后读取该客户端的输入。如果客户端发送消息,则该消息将传播到所有其他客户端。客户端通过终端运行。如果使用STRG + C停止客户端,一切都会正常,但是如果退出终端时使客户端崩溃,则一切都不会正常。

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>

#define BUFLEN 255
#define MAX_CONNECTIONS 128
#define MAX_NAME_LENGTH 30

struct JOB_INFO {
    int socket_ids[MAX_CONNECTIONS];
    bool end_of_program;
    int open_cnncts;
    pthread_mutex_t socketids_changingMutex;
};

struct SERVER_INFO {
    struct sockaddr_in serv_add;
    struct sockaddr_in cli_adr;
    socklen_t cli_len;
    pthread_t write_thread;
};
//Global Variables
FILE* plog_file;

void delete_socket(int socketid, struct JOB_INFO* pjob_data);
void* job_read(void*);
void* job_write(void*);
void error(const char* msg);
void setUpFileStream(int argc, char* argv[]);
int setUpConnections(struct SERVER_INFO* pserver_data, char* port_numc);
void enterConnectingLoop(struct SERVER_INFO* pserver_data, struct JOB_INFO* pjob_data, int sockfd);

void error(const char* msg){
    perror(msg);
    exit(1);
}

int main(int argc, char* argv[]) {
    setUpFileStream(argc, argv);

    int sock_fd;
    struct JOB_INFO job_data;
    job_data.end_of_program = false;
    job_data.open_cnncts = 0;

    struct SERVER_INFO server_data;
    //Create mutex
    if(pthread_mutex_init(&(job_data.socketids_changingMutex), NULL) < 0){
        error("Could not initialize Mutex");
    }
    //Initialzing threads and create write_thread
    pthread_create(&server_data.write_thread, NULL, job_write, (void*)&job_data);

    //Setup for connections
    sock_fd = setUpConnections(&server_data, argv[1]);
    fprintf(plog_file,"Listening...");
    fflush(stdout); //For unknown reason this is needed.
    enterConnectingLoop(&server_data, &job_data, sock_fd);

    job_data.end_of_program = true;
    close(sock_fd);

    pthread_join(server_data.write_thread, NULL);
    pthread_mutex_destroy(&job_data.socketids_changingMutex);
    return EXIT_SUCCESS;
}

void* job_read(void * p){

    char** pc = (char**)p; //allow pointer arithmetic
    int new_sock_fd = *((int*) (pc[1]));  //Casting
    struct JOB_INFO* pjob_data = ((struct JOB_INFO*) (pc[0])); //Casting

    ssize_t n; //Error catching variable
    ssize_t m; //Error catching variable
    char buffer[BUFLEN];
    char name[MAX_NAME_LENGTH];
    sprintf(name, "Client %d: ", new_sock_fd);
    fprintf(plog_file, "Reading from %s\n", name);

    while(!pjob_data->end_of_program){
        n = read(new_sock_fd, buffer, BUFLEN-1);
        if ( n > 0 ) buffer[n] = '\0';  // add a \0 on success
        if(n<0){
            printf("Buffer: %s", buffer);
            error("Reading Failed");
        }
        pthread_mutex_lock(&pjob_data->socketids_changingMutex);
        if(n == 0){
            delete_socket(new_sock_fd, pjob_data);
            pthread_exit(NULL);
        }

        for(int i = 0; i < pjob_data->open_cnncts; i++){
            if(pjob_data->socket_ids[i] == new_sock_fd){
                continue;
            }
            m = write(pjob_data->socket_ids[i], name, strlen(name));
            if((m < 0)){
                error("Writing name failed");
            }
            n = write(pjob_data->socket_ids[i], buffer, strlen(buffer));
            if((n < 0)){
                error("Writing message failed");
            }
        }
        pthread_mutex_unlock(&pjob_data->socketids_changingMutex);
        printf("%s%s", name, buffer);
        fflush(stdout); //For unknown reason this is needed.
    }
    delete_socket(new_sock_fd, pjob_data);
    free(p);
    pthread_exit( NULL );
}

void* job_write(void* args){
    struct JOB_INFO* pjob_data = (struct JOB_INFO*)(args);
    fprintf(plog_file, "Started writing thread...\n");
    ssize_t n; //Error catching variable
    ssize_t m; //Error catching variable
    char buffer[BUFLEN];
    char* name = "Server: \0";

    while(!pjob_data->end_of_program) {
        fgets(buffer, BUFLEN, stdin);

        pthread_mutex_lock(&pjob_data->socketids_changingMutex);
        for(int i = 0; i < pjob_data->open_cnncts; i++){
            m = write(pjob_data->socket_ids[i], name, strlen(name));
            if((m < 0)){
                error("Writing name failed");
            }
            n = write(pjob_data->socket_ids[i], buffer, strlen(buffer));
            if((n < 0)){
                error("Writing message failed");
            }
        }
        pthread_mutex_unlock(&pjob_data->socketids_changingMutex);
        if(strcmp("Bye\n", buffer) == 0){
            exit(EXIT_SUCCESS);
        }
    }
    free(args);
    pjob_data->end_of_program = true;
    pthread_exit( NULL );
}

void enterConnectingLoop(struct SERVER_INFO* pserver_data, struct JOB_INFO* pjob_data, int sockfd){
    listen(sockfd, MAX_CONNECTIONS);

    for(pjob_data->open_cnncts = 0; (!pjob_data->end_of_program); /*mutex needs to be set*/ ){
        void** p = malloc(2*sizeof(void*));
        p[0] = (void*)pjob_data;
        p[1] = (void*)&(pjob_data->socket_ids[pjob_data->open_cnncts]);
        pjob_data->socket_ids[pjob_data->open_cnncts] =
                accept(sockfd, (struct sockaddr*) &pserver_data->cli_adr, &pserver_data->cli_len);
        pthread_mutex_lock(&(pjob_data->socketids_changingMutex)); //Note you cannot mutex the accept()
        printf("SOCKETFD %d\n", (pjob_data->socket_ids[pjob_data->open_cnncts]));
        fprintf(plog_file,"Client connected.\n");
        pthread_t thread;
        pthread_create(&thread , NULL, job_read, p);
        pjob_data->open_cnncts++;
        pthread_mutex_unlock(&(pjob_data->socketids_changingMutex));
    }
}

void delete_socket(int socketid, struct JOB_INFO* pjob_data){
    bool found = false;
    for(int i = 0; i < pjob_data->open_cnncts; i++){
        if(found){
            pjob_data->socket_ids[i-1] = pjob_data->socket_ids[i];
        }
        if(pjob_data->socket_ids[i] == socketid){
            close(socketid);
            found = true;
        }
    }
    if(found){
        pjob_data->open_cnncts--;
    }
}

inline void setUpFileStream(int argc, char* argv[]){
    printf("Server started...\n");
    if(argc < 2){
        fprintf(stderr, "You must provide a port number");
        exit(EXIT_FAILURE);
    }
    if(argc == 3){
        plog_file = fopen(argv[2], "w");
    } else {
        plog_file = fopen("logfile.txt", "w");
    }
    plog_file = stdout;
    stderr = plog_file;
}

inline int setUpConnections(struct SERVER_INFO* pserver_data, char* port_numc){
    pserver_data->cli_len = sizeof(pserver_data->cli_adr);
    memset(&pserver_data->serv_add, 0, sizeof((pserver_data->serv_add)));

    uint16_t port_num = (uint16_t)atoi(port_numc);
    pserver_data->serv_add.sin_family = AF_INET;
    pserver_data->serv_add.sin_addr.s_addr = INADDR_ANY;
    pserver_data->serv_add.sin_port = htons(port_num);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }

    if(bind(sockfd, (struct sockaddr*) (&pserver_data->serv_add), sizeof(pserver_data->serv_add)) < 0){
        error("Binding failed.");
    }
    return sockfd;
}", buffer) == 0){
            exit(EXIT_SUCCESS);
        }
    }
    free(args);
    pjob_data->end_of_program = true;
    pthread_exit( NULL );
}

void enterConnectingLoop(struct SERVER_INFO* pserver_data, struct JOB_INFO* pjob_data, int sockfd){
    listen(sockfd, MAX_CONNECTIONS);

    for(pjob_data->open_cnncts = 0; (!pjob_data->end_of_program); /*mutex needs to be set*/ ){
        void** p = malloc(2*sizeof(void*));
        p[0] = (void*)pjob_data;
        p[1] = (void*)&(pjob_data->socket_ids[pjob_data->open_cnncts]);
        pjob_data->socket_ids[pjob_data->open_cnncts] =
                accept(sockfd, (struct sockaddr*) &pserver_data->cli_adr, &pserver_data->cli_len);
        pthread_mutex_lock(&(pjob_data->socketids_changingMutex)); //Note you cannot mutex the accept()
        printf("SOCKETFD %d\n", (pjob_data->socket_ids[pjob_data->open_cnncts]));
        fprintf(plog_file,"Client connected.\n");
        pthread_t thread;
        pthread_create(&thread , NULL, job_read, p);
        pjob_data->open_cnncts++;
        pthread_mutex_unlock(&(pjob_data->socketids_changingMutex));
    }
}

void delete_socket(int socketid, struct JOB_INFO* pjob_data){
    bool found = false;
    for(int i = 0; i < pjob_data->open_cnncts; i++){
        if(found){
            pjob_data->socket_ids[i-1] = pjob_data->socket_ids[i];
        }
        if(pjob_data->socket_ids[i] == socketid){
            close(socketid);
            found = true;
        }
    }
    if(found){
        pjob_data->open_cnncts--;
    }
}

inline void setUpFileStream(int argc, char* argv[]){
    printf("Server started...\n");
    if(argc < 2){
        fprintf(stderr, "You must provide a port number");
        exit(EXIT_FAILURE);
    }
    if(argc == 3){
        plog_file = fopen(argv[2], "w");
    } else {
        plog_file = fopen("logfile.txt", "w");
    }
    plog_file = stdout;
    stderr = plog_file;
}

inline int setUpConnections(struct SERVER_INFO* pserver_data, char* port_numc){
    pserver_data->cli_len = sizeof(pserver_data->cli_adr);
    memset(&pserver_data->serv_add, 0, sizeof((pserver_data->serv_add)));

    uint16_t port_num = (uint16_t)atoi(port_numc);
    pserver_data->serv_add.sin_family = AF_INET;
    pserver_data->serv_add.sin_addr.s_addr = INADDR_ANY;
    pserver_data->serv_add.sin_port = htons(port_num);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }

    if(bind(sockfd, (struct sockaddr*) (&pserver_data->serv_add), sizeof(pserver_data->serv_add)) < 0){
        error("Binding failed.");
    }
    return sockfd;
}

在作业功能中,服务器似乎一次又一次地打印客户端的最后一条消息。

void* job_read(void * p){

    char** pc = (char**)p; //allow pointer arithmetic
    int new_sock_fd = *((int*) (pc[1]));  //Casting
    struct JOB_INFO* pjob_data = ((struct JOB_INFO*) (pc[0])); //Casting

    ssize_t n; //Error catching variable
    ssize_t m; //Error catching variable
    char buffer[BUFLEN];
    char name[MAX_NAME_LENGTH];
    sprintf(name, "Client %d: ", new_sock_fd);
    fprintf(plog_file, "Reading from %s\n", name);

    while(!pjob_data->end_of_program){
        n = read(new_sock_fd, buffer, BUFLEN-1);
        if ( n > 0 ) buffer[n] = '\0';  // add a \0 on success
        if(n<0){
            printf("Buffer: %s", buffer);
            error("Reading Failed");
        }
        pthread_mutex_lock(&pjob_data->socketids_changingMutex);
        if(n == 0){
            delete_socket(new_sock_fd, pjob_data);
            pthread_exit(NULL);
        }

        for(int i = 0; i < pjob_data->open_cnncts; i++){
            if(pjob_data->socket_ids[i] == new_sock_fd){
                continue;
            }
            m = write(pjob_data->socket_ids[i], name, strlen(name));
            if((m < 0)){
                error("Writing name failed");
            }
            n = write(pjob_data->socket_ids[i], buffer, strlen(buffer));
            if((n < 0)){
                error("Writing message failed");
            }
        }
        pthread_mutex_unlock(&pjob_data->socketids_changingMutex);
        printf("%s%s", name, buffer);
        fflush(stdout); //For unknown reason this is needed.
    }
    delete_socket(new_sock_fd, pjob_data);
    free(p);
    pthread_exit( NULL );
}

有两件事。在此函数中,名称变量似乎不时消失。她没有得到打印或发送。但是据我所知,name变量总是被初始化。

重现该错误:

编译服务器和客户端(下面的代码)。 启动服务器并打开端口:

./Server 9999

启动客户端:

./Client 127.0.0.1 9999

在客户端中输入内容。 通过关闭终端退出客户端。 服务器行为异常。

谢谢!

客户代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <stdbool.h>

#define BUFLEN 255

bool endprogram = false;
int sockfd;

void error(const char* msg){
    perror(msg);
    exit(1);
}

void* job_read(void* p){
    char buffer[BUFLEN];
    while(!endprogram){
        bzero(buffer, BUFLEN);
        ssize_t n = read(sockfd, buffer, (BUFLEN));
        if(n < 0){
            error("Error on reading");
        }
        printf("%s", buffer);
        int i = strncmp("Bye", buffer, 3);
        if(i == 0){
            endprogram = true;
            pthread_exit(NULL);
        }
    }
   pthread_exit(NULL);
}

int main(int argc, const char * argv[]) {
    pthread_t readt;

    int16_t portnum;
    struct sockaddr_in serveraddr;
    struct hostent* server;

    if(argc < 3){
        perror("You shall provide a port and a ip adress");
    }
    portnum = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket");
    }

    server = gethostbyname(argv[1]);
    if(!server){
        error("No such host");
    }

    bzero((char*)&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, sizeof(server->h_length));
    serveraddr.sin_port = htons(portnum);

    if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))<0){
        error("Connection failed");
    }

    pthread_create(&readt, NULL, &job_read, NULL);

    ssize_t n;
    char buffer[BUFLEN];
    while(!endprogram){
        fgets(buffer, BUFLEN, stdin);
        n = write(sockfd, buffer, strlen(buffer));
        if(n < 0){
            error("Error on writing");
        }
        n = strcmp(buffer, "Bye");
        if(n == 0){
            endprogram = true;
        }
    }
    pthread_join(readt, NULL);
    close(sockfd);
    return 0;
}

编辑:

减少问题真的很困难,因为我需要所有这些方法来设置问题或维护核心功能。但是,这是第一次减少(无法编译),可能是该错误所在。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>

#define BUFLEN 255
#define MAX_CONNECTIONS 128
#define MAX_NAME_LENGTH 30

struct JOB_INFO {
    int socket_ids[MAX_CONNECTIONS];
    bool end_of_program;
    int open_cnncts;
    pthread_mutex_t socketids_changingMutex;
};

struct SERVER_INFO {
    struct sockaddr_in serv_add;
    struct sockaddr_in cli_adr;
    socklen_t cli_len;
    pthread_t write_thread;
};
//Global Variables
FILE* plog_file;

void delete_socket(int socketid, struct JOB_INFO* pjob_data);
void* job_read(void*);
void error(const char* msg);
void enterConnectingLoop(struct SERVER_INFO* pserver_data, struct JOB_INFO* pjob_data, int sockfd);

int main(int argc, char* argv[]) {

    int sock_fd;
    struct JOB_INFO job_data;
    job_data.end_of_program = false;
    job_data.open_cnncts = 0;

    struct SERVER_INFO server_data;
    enterConnectingLoop(&server_data, &job_data, sock_fd);

    job_data.end_of_program = true;
    close(sock_fd);

    pthread_join(server_data.write_thread, NULL);
    pthread_mutex_destroy(&job_data.socketids_changingMutex);
    return EXIT_SUCCESS;
}

void* job_read(void * p){

    char** pc = (char**)p; //allow pointer arithmetic
    int new_sock_fd = *((int*) (pc[1]));  //Casting
    struct JOB_INFO* pjob_data = ((struct JOB_INFO*) (pc[0])); //Casting

    ssize_t n; //Error catching variable
    ssize_t m; //Error catching variable
    char buffer[BUFLEN];
    char name[MAX_NAME_LENGTH];
    sprintf(name, "Client %d: ", new_sock_fd);
    fprintf(plog_file, "Reading from %s\n", name);

    while(!pjob_data->end_of_program){
        n = read(new_sock_fd, buffer, BUFLEN-1);
        if ( n > 0 ) buffer[n] = '\0';  // add a \0 on success
        if(n<0){
            printf("Buffer: %s", buffer);
            error("Reading Failed");
        }
        pthread_mutex_lock(&pjob_data->socketids_changingMutex);
        if(n == 0){
            delete_socket(new_sock_fd, pjob_data);
            pthread_exit(NULL);
        }

        for(int i = 0; i < pjob_data->open_cnncts; i++){
            if(pjob_data->socket_ids[i] == new_sock_fd){
                continue;
            }
            m = write(pjob_data->socket_ids[i], name, strlen(name));
            if((m < 0)){
                error("Writing name failed");
            }
            n = write(pjob_data->socket_ids[i], buffer, strlen(buffer));
            if((n < 0)){
                error("Writing message failed");
            }
        }
        printf("%s%s", name, buffer);
        pthread_mutex_unlock(&pjob_data->socketids_changingMutex);
        fflush(stdout); //For unknown reason this is needed.
    }
    delete_socket(new_sock_fd, pjob_data);
    free(p);
    pthread_exit( NULL );
}

void enterConnectingLoop(struct SERVER_INFO* pserver_data, struct JOB_INFO* pjob_data, int sockfd){
    listen(sockfd, MAX_CONNECTIONS);

    for(pjob_data->open_cnncts = 0; (!pjob_data->end_of_program); /*mutex needs to be set*/ ){
        void** p = malloc(2*sizeof(void*));
        p[0] = (void*)pjob_data;
        p[1] = (void*)&(pjob_data->socket_ids[pjob_data->open_cnncts]);
        pjob_data->socket_ids[pjob_data->open_cnncts] =
                accept(sockfd, (struct sockaddr*) &pserver_data->cli_adr, &pserver_data->cli_len);
        pthread_mutex_lock(&(pjob_data->socketids_changingMutex)); //Note you cannot mutex the accept()
        printf("SOCKETFD %d\n", (pjob_data->socket_ids[pjob_data->open_cnncts]));
        fprintf(plog_file,"Client connected.\n");
        pthread_t thread;
        pthread_create(&thread , NULL, job_read, p);
        pjob_data->open_cnncts++;
        pthread_mutex_unlock(&(pjob_data->socketids_changingMutex));
    }
}

void delete_socket(int socketid, struct JOB_INFO* pjob_data){
    bool found = false;
    for(int i = 0; i < pjob_data->open_cnncts; i++){
        if(found){
            pjob_data->socket_ids[i-1] = pjob_data->socket_ids[i];
        }
        if(pjob_data->socket_ids[i] == socketid){
            close(socketid);
            found = true;
        }
    }
    if(found){
        pjob_data->open_cnncts--;
    }
}

我将尝试建立一个程序,使用输入和输出文件在PC上执行相同的操作,从而包围整个inet-setup-monster ...

编辑编辑:

如果同一件事发生在IDE之外,我会得到一个coredump-而不是愚蠢的输出:类似于此:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

void* job_read(void* p){
    sleep(1000); //delay to ensure job_write is waiting
    FILE* pfile = ((FILE**)p)[0];
    while(1){
        char buffer[20];
        fgets(buffer, 19, pfile);
        printf("&s", buffer);
    }

}

void* job_write(void* p){
    FILE* pfile = ((FILE**)p)[0];
    fputs("hi\n", pfile);
    sleep(1200); //delay to ensure job_write is waiting
    pthread_exit(NULL); //FUCK THE OPEN CONNECTION
}

int main(int argc, char* argv[]) {
    (void)argv; //Compiler things
    (void)argc;
    printf("HERE");
    FILE* in = fopen("in.txt", "w");
    pthread_t firstread; //Simulate all threads in actual IO: Server
    pthread_t firstwrite; //Client

    FILE** p = malloc(sizeof(FILE*));
    p[0] = in;

    pthread_create(&firstwrite, NULL, job_write, (void*)&p);
    pthread_create(&firstread, NULL, job_read, (void*)&p);

    pthread_join(firstwrite, NULL);
}

我想这可能是解决我实际问题的方法。我的假设是我试图写一个不再存在的文件或类似的文件。但是,我想这是因为文件关闭导致该错误

EDIT EDIT EDIT

我可以肯定地说,问题是由job_read函数引起的。 如前所述,一个聪明的用户意识到,如果终端退出,则要花一些时间才能终止实际进程。这可能会导致错误之一。

1 个答案:

答案 0 :(得分:1)

如果输入流关闭,则此代码将反复重复从stdin读取的最后一行:

while(!pjob_data->end_of_program) {
    fgets(buffer, BUFLEN, stdin);

    pthread_mutex_lock(&pjob_data->socketids_changingMutex);
    for(int i = 0; i < pjob_data->open_cnncts; i++){
        m = write(pjob_data->socket_ids[i], name, strlen(name));
        if((m < 0)){
            error("Writing name failed");
        }
        n = write(pjob_data->socket_ids[i], buffer, strlen(buffer));
        if((n < 0)){
            error("Writing message failed");
        }
    }
    pthread_mutex_unlock(&pjob_data->socketids_changingMutex);
    if(strcmp("Bye\n", buffer) == 0){
        exit(EXIT_SUCCESS);
    }
}

您永远不会检查fgets()是否失败。完成后,buffer的先前内容将写入套接字。然后,只要pjob_data->end_of_program的计算结果不为true,循环就会反复重复,每次都发送buffer的内容。也可能真的很快。

编辑:

客户端代码中存在相同的错误

while(!endprogram){
    fgets(buffer, BUFLEN, stdin);
    n = write(sockfd, buffer, strlen(buffer));
    if(n < 0){
        error("Error on writing");
    }
    n = strcmp(buffer, "Bye");
    if(n == 0){
        endprogram = true;
    }
}

解决方法是检查fgets()的返回值,如果NULL,请采取适当的措施。我对代码的分析还不足以了解“适当”的含义。

此代码调用了互斥体已锁定的pthread_exit()

    pthread_mutex_lock(&pjob_data->socketids_changingMutex);
    if(n == 0){
        delete_socket(new_sock_fd, pjob_data);
        pthread_exit(NULL);
    }

在此之后锁定或解锁pjob_data->socketids_changingMutex的任何尝试都可能会无声地失败或死锁。