具有 FIFO 管道的分段错误(核心转储)以及服务器和客户端之间的分叉通信

Segmentation fault (core dumped) with FIFO pipes and fork communication between server and client

我正在编写一个项目,您可以在一个终端中启动服务器,而在作为客户端的其他终端中,您可以使用 FIFO 管道将消息从一个用户发送到另一个用户。服务器创建从客户端读取消息的 FIFO 管道。客户端创建 FIFO 管道以从服务器读取消息。

在一个启动服务器的终端中,我键入 ./projectname --start,在客户端中,我键入 ./projectname --login nickname。当我关闭客户端时,我的服务器收到分段错误(核心转储)错误消息。在 x 小时内,我试图以我所知道的一切可能的方式摆脱它。如何修复?

我也曾尝试使用 void verifyloginclient(char *login) 函数注册用户,但父进程无法从子进程接收信息并陷入无限状态 while(1) 向服务器发送一些消息,这也使服务器因分段错误而崩溃,因此它被评论现在。

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

char userlist[10][30];
int succesfulllogin = 1;


void handler(int signum){
    pid_t pid;
    pid = wait(NULL);
    succesfulllogin = 0;
    printf("Parent knows child %d finished\n", (int)pid);
    printf("Parent ending...\n");
}
void sig_handler_parent(int signum){
    printf("signal response from child!\n");
    succesfulllogin = 0;
    exit(0);
}
void sig_handler_child(int signum){
    printf("signal from parent\n");
    succesfulllogin = 0;
    exit(0);
}

void verifyloginclient(char *login)
{
    int fd;
    char buf[256];
    char buf2[256];
    snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");
    if((fd = open(buf, O_WRONLY)) == -1){
        perror("openFdloginclient");
        exit(EXIT_FAILURE);
    }
    snprintf(buf2, sizeof buf2, "%s%s%s", login, " /login ",login);
    if((write(fd, buf2, strlen(buf2))) == -1){
        perror("writeloginclient");
    }
    close(fd);
}

int verifyloginserwer(char *login)
{
    // check if login is on the userlist
        int flagcmp = -1;
        int x;
        int indeks=0;
        for(int i=0;i<10;i++){
            x = strcmp(&userlist[i][0],login);
            if(x==0){
                flagcmp = i;
            }
        }
        if(flagcmp == -1){ //if it doesnt exist we add him to first free slot
            for(int i=0;i<10;i++){
                if(userlist[i][0]=='[=10=]'){
                    strcpy(userlist[i],login);
                    printf("userlist: %s\n", userlist[i]);
                    succesfulllogin = 1;
                    break;
                }
                else{
                    indeks++;
                }
            }
        }
        else if(flagcmp != -1 || indeks==10){
            fprintf(stderr, "loggin error!\n");
            succesfulllogin = 0;
            return 2;
        }
        return 0;
}


int splitstring(char *source, int desiredlength, char **s1,char **s2)
{
    int len;

    len = strlen(source);
    if (desiredlength > len)
        return(0);
    *s1 = (char *) malloc(sizeof(char) * desiredlength);
    *s2 = (char *) malloc(sizeof(char) * len-desiredlength+1);
    if(s1 ==NULL || s2 == NULL)
        return 0;
    strncpy(*s1,source,desiredlength);
    strncpy(*s2,source+desiredlength, len-desiredlength);

    return(1);
}

void startserwer()
{
    int fdserwer;
    int fdanuluj;
    int fd;
    char readbuf[80];
    int read_bytes;
    char buf[256]; //pipe server
    char buf2[256]; //pipe client
    char buf3[256]; // message to client
    for(int i=0;i<10;i++){ // declare userlist
        userlist[i][0] = '[=10=]';
    }
    
    snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");

    umask(0);
    if(mkfifo(buf, 0777) == -1){
        perror("mkfifoserwer");
        exit(EXIT_FAILURE);
    }

    while(1){

        if((fdserwer = open(buf, O_CREAT | O_RDONLY))== -1){
            perror("openFdserwer");
            exit(EXIT_FAILURE);
        }

        if((read_bytes = read(fdserwer, &readbuf, sizeof(readbuf)))== -1){
            perror("readbytes");
        }
        else{
            readbuf[read_bytes] = '[=10=]';

            //SENDER LOGIN
            char senderlogin[80];
            strcpy(senderlogin, readbuf);
            char *token = strtok(senderlogin, " ");
            printf("%s\n", token);
            strcpy(senderlogin, token);

            int b;
            char *loginnadawcy;
            char *resztakomendy;
            b = splitstring(readbuf, (int) strlen(token)+1,&loginnadawcy,&resztakomendy);
            if(b!=1){
                printf("error in command\n");
            }
            else{
                loginnadawcy[strlen(token)]='[=10=]';

                //COMMAND NAME AFTER SLASH /
                char command[80];
                strcpy(command, readbuf);
                token = strtok(NULL, " ");
                printf("%s\n",token);
                strcpy(command, token);

                int a;
                char *komenda;
                char *reszta;
                a = splitstring(resztakomendy, (int)strlen(token)+1,&komenda,&reszta);
                if(a!=1){
                printf("error\n");
                }
                else{

                    komenda[strlen(token)]='[=10=]';

                    if(strcmp(komenda,"/login")==0){
                        if(verifyloginserwer(senderlogin)==2){
                            //open client pipe
                            snprintf(buf2, sizeof buf2, "%s%s%s", "fifopipes/",senderlogin,".fifo");
                            
                            if((fdanuluj = open(buf2, O_WRONLY)) == -1){
                                perror("openFdanuluj");
                                exit(EXIT_FAILURE);
                            }
                            //SEND MESSAGE TO CLIENT
                            snprintf(buf3, sizeof buf3, "%s%s", "serwer ", "end");

                            if((write(fdanuluj, buf3, strlen(buf3))) == -1){
                                perror("writeFdanuluj");
                            }
                            close(fdanuluj);
                        }
                    }
                    else if(strcmp(komenda,"/w")==0){
                        //RECEIVER LOGIN
                        char receiverlogin[80];
                        token = strtok(NULL, " ");
                        printf("%s\n",token);
                        strcpy(receiverlogin, token);

                            
                        int r;
                        char *login;
                        char *message;
                        r = splitstring(reszta,(int)strlen(token)+1,&login,&message);
                        if(r!=1){
                            printf("error\n");
                        }
                        else{
                            //RECEIVE INFORMATION
                            login[strlen(receiverlogin)]='[=10=]';
                            printf("from %s to %s: %s and length is %d \n", senderlogin, login, message, (int)strlen(message));
                                    
                            //OPEN CLIENT PIPE
                            snprintf(buf2, sizeof buf2, "%s%s%s", "fifopipes/",receiverlogin,".fifo");
                            if((fd = open(buf2, O_WRONLY))== -1){
                                perror("openFd");
                                exit(EXIT_FAILURE);
                            }
                            //SEND MESSAGE TO CLIENT
                            snprintf(buf3, sizeof buf3, "%s%s%s", senderlogin," ",message);
                            if( (write(fd, buf3, strlen(buf3) )) == -1){
                                perror("writeopenfd");
                            }
                            close(fd);
                        }
                    }
                }
            }
        }
        close(fdserwer);
    }
    unlink(buf);
}


void clientchild(char *login)
{
    int fd;
    char readbuf[80];
    int read_bytes;
    char buf[256];
    snprintf(buf, sizeof buf, "%s%s%s", "fifopipes/",login,".fifo");

    umask(0);
    if(mkfifo(buf, 0777) == -1){
        perror("mkfifoclient");
        exit(EXIT_FAILURE);
    }

    while(1){

        if((fd = open(buf, O_CREAT | O_RDONLY)) == -1){
            perror("openFdchild");
            exit(EXIT_FAILURE);
        }
        
        if((read_bytes = read(fd, &readbuf, sizeof(readbuf))) == -1){
            perror("readfdchild");
        }
        else{
            readbuf[read_bytes] = '[=10=]';

            //SENDER LOGIN
            char nickname[80];
            strcpy(nickname, readbuf);
            char * token = strtok(nickname, " ");
            //printf("%s\n",token);
            int r;
            char *login;
            char *message;
            r = splitstring(readbuf,(int)strlen(token)+1,&login,&message);
            if(r!=1){
                printf("blad\n");
            }
            else{
                login[strlen(token)]='[=10=]';
                if(strcmp(login,"serwer")==0 && strcmp(message,"end")==0){
                    printf("Login error. Closing...\n");
                    close(fd);
                    break;
                }
                printf("%s: %s and length is %d \n", login, message, (int)strlen(message));
                close(fd);
                //printf("Received string: \"%s\" and length is %d \n", readbuf, (int)strlen(readbuf));
            }
            //free(login);
            //free(message);
        }
    }
    unlink(buf);
}

void clientparent(char *login)
{
    signal(SIGQUIT,sig_handler_parent);
    int fd;
    int stringlen;
    char readbuf[80];
    char buf[256];
    char buf2[256];
    snprintf(buf, sizeof buf, "%s%s", "fifopipes/","serwer.fifo");
    printf("FIFO_CLIENT: Send messages infinitely\n");

    while(1){

        if((fd = open(buf, O_WRONLY)) == -1){
            perror("openFdparent");
            exit(EXIT_FAILURE);
        }
        //printf("Enter string: ");
        fgets(readbuf, sizeof(readbuf), stdin);
        stringlen = strlen(readbuf);
        readbuf[stringlen - 1] = '[=10=]';
        snprintf(buf2, sizeof buf2, "%s%s%s", login," ",readbuf);

        if(strlen(login)+1 == strlen(buf2)){
            printf("Error\n");
            break;
        }
        else{

            if((write(fd, buf2, strlen(buf2))) == -1){
                perror("writeparent");
            }
            printf("Sent string: \"%s\" and string length is %d \n", buf2, (int)strlen(buf2));
        }
    }
    close(fd);
}

void splitclient(char *login)
{
    printf("login: %s\n", login);
    pid_t pid = fork();
    printf("fork returned: %d\n", (int) pid);

    //signal(SIGCHLD, handler);

    if (pid < 0){
        perror("Fork failed");
    }
    else if (pid == 0){
        signal(SIGQUIT,sig_handler_child);
        printf("I am the child with pid %d\n", (int) getpid());
        char buf[256];
        snprintf(buf, sizeof buf, "%s%d", "Child ",(int) getpid());
        //kod dziecka
        clientchild(login);
        //kill(getppid(),SIGQUIT);
        exit(0);
    }
    else{
        // We must be the parent
        printf("I am the parent, waiting for child to end \n");

        //kod rodzica
        clientparent(login);
        pid_t childpid = wait(NULL);
        printf("Parent knows child %d finished\n", (int)childpid);
        printf("Parent ending...\n");
    }

}

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

    while(1)
    {
        int c;
        int option_index = 0;
        static struct option long_options[] =
        {
            {"start", no_argument, NULL, 's'},
            {"login", required_argument, NULL, 'l'},
            {0, 0, 0, 0}
        };

        

        c = getopt_long (argc, argv, "sl:", long_options, &option_index);

        if (c == -1)
            break;

        switch (c)
        {
            case 's':
                printf("start server\n");
                startserwer();
                break;

            case 'l':
                printf("login with option %s \n", optarg);
                    //verifyloginclient(optarg);
                    splitclient(optarg);
                break;

            case '?':
                break;

            default:
                abort();
        }
    }

    if (optind < argc)
    {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
            printf("%s ", argv[optind++]);
        putchar ('\n');
    }


    return 0;
}

来自startServer()中的代码:

if((read_bytes = read(fdserwer, &readbuf, sizeof(readbuf)))== -1){
    perror("readbytes");
}
else{
    :
    char *token = strtok(senderlogin, " ");
    :
    strcpy(senderlogin, token);
    b = splitstring(readbuf, (int) 
    strlen(token)+1,&loginnadawcy,&resztakomendy);

您没有验证 token 是否是非 NULL 指针 post 标记化。在 child 退出的情况下,read() 将 return 为 0(而不是 -1 作为失败),表明 EOF 正如 man 告诉的那样:

RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file),

因此 senderlogin 缓冲区将是空的并且不会产生任何标记。您需要考虑这种情况并进行适当的空值检查。添加空 chek 将导致服务器程序正常退出。