Search code examples
cwhile-loopstdininfinite-loopeof

Stuck on stdin reading until ^C or EOF, then "stdin spam"


Hello to any competent people out there who would stumble upon my post.
I require assistance like never before.

My problem is here:

for(i = 1; i < 6; i++){
    wprintw(filsWin, "Entre ton choix en position %d :\n", i);
    wrefresh(filsWin);
                
    clean_stdin();
    choix = getchar();
                
    if(choix < '0' | choix > '7'){
        wprintw(filsWin, "Ce n'est pas dans les choix. Réessaie.\n");
        wrefresh(filsWin);
        i--;
    }
    else {
        combiGagnante[i-1] = choix;
        if(send(SockService, &choix, 1, 0) >= 0){
            wprintw(filsWin, "\n%s%cm%s%s inséré en position %d.\n\n", EXMNRM, combiGagnante[i-1], DOT, RSTCOL, i);
            wrefresh(filsWin);
        }
        else {
            perror("send()");
            raise(SIGINT);
        }
    }
}

I am currently making a Mastermind in C for my bachelor degree, which has to be 2 TCP server-client programs. I wanted to use the ncurses library thanks to its graphics possibilities, and this code is part of the server, executed by a child process upon connection of the client.

The problem is at choix = getchar();.

Whatever is used here for reading stdin, I can type anything, the terminal won't even echo it. ^C on the server quits it fortunately (thanks to my signal catching tbh). However, if I ^C on the client during the connection, the client does terminate like the server (signal catching ftw), but then getchar() gets literally flooded by endless EOF (-1) characters (which this code responds to with the if (choix < '0' | choix > '7') statement), leaving me no choice but to ultimately ^C the server.

I have run out of ideas to fix the issue, as I have no idea what can possibly spam stdin like that. I even tried to flush it and to "clean" it; in vain (I need to get rid of clean_stdin(), it does absolutely nothing).

I am open to any solution, and can offer more information and code if needed.

Thanks in advance!

A Stack Overflow newbie

P.S.: choix is an int, not a char (for convenience purposes), and filsWin is the child's subwin.

EDIT: In response to @Armali, here is server.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <uchar.h>

#include <arpa/inet.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>

#include <curses.h>

#include "mast_vars.h"

#define TRUE 1
#define FALSE 0

#define EXMNRM "\x1B[0;3"
#define RSTCOL "\x1B[0;0m"
//#define BLKCOL "\x1B[0;30m"
//#define REDCOL "\x1B[0;31m"
//#define GRNCOL "\x1B[0;32m"
//#define YLWCOL "\x1B[0;33m"
//#define BLUCOL "\x1B[0;34m"
//#define MAGCOL "\x1B[0;35m"
//#define CYACOL "\x1B[0;36m"
//#define WHTCOL "\x1B[0;37m"
#define DOT "\u2b24"
#define INVDOT "\u2022"


int SockEcoute, SockService;
WINDOW *pereWin, *filsWin;

int creerSocket(int, int *, struct sockaddr_in *);

void clean_stdin();

char convertPionIntoColor(char);

void segVSig(int sig);
void intSig(int sig);
void pipeSig(int sig);
void closeEverything();

int main(int argc, char *argv[]){
    struct sockaddr_in adresse;
    int portTCP, lg;
    
    char combiEssayees[5][essaisDonnes];
    char pionsInvest[5][essaisDonnes];
    
    int i, codGagne = 2, SockService = atoi(argv[1]);
    int32_t it;
    
    
    if(argc != 2 || argc == 1){
        fprintf(stderr, "Usage : MMSrvTCP <n°port>");
        exit(2);
    }

    signal(SIGINT, intSig);
    signal(SIGSEGV, segVSig);
    signal(SIGPIPE, pipeSig);
    
    portTCP=atoi(argv[1]);
    
    lg=sizeof(adresse);
    
    if((SockEcoute=creerSocket(SOCK_STREAM, &portTCP, &adresse))==-1){
        perror("Erreur creerSocket");
        exit(1);
    }
    
    if(listen(SockEcoute, 5)==-1){
        perror("Erreur listen");
        exit(2);
    }
    
    initscr();
    refresh();
    pereWin = subwin(stdscr, LINES / 2, COLS, 0, 0);
    box(pereWin, 0, 0);
    wrefresh(pereWin);
    
    wprintw(pereWin, "Serveur de PID %d lancé\n", getpid());
    
    while(1){
        wprintw(pereWin, "En attente d'un client.\n\n");
        wrefresh(pereWin);
        
        SockService=accept(SockEcoute, &adresse, &lg);
        wprintw(pereWin, "Connexion acceptée par %s\n", inet_ntoa(adresse.sin_addr));
        wrefresh(pereWin);
        
        if(fork() == 0){
            close(SockEcoute);
            dup2(SockService, STDIN_FILENO);
            close(SockService);
            
            filsWin = subwin(stdscr, LINES / 2, COLS, LINES / 2, 0);
            box(filsWin, 0, 0);
            wrefresh(filsWin);
            
            wprintw(filsWin, "Le choix de couleurs se fait ainsi :\n\n");
            wprintw(filsWin, "0 = Noir\n1 = Rouge\n2 = Vert\n3 = Jaune\n4 = Bleu\n5 = Magenta\n6 = Cyan\n7 = Blanc\n\n");
            wrefresh(filsWin);
            
            for(i = 1; i < 6; i++){
                wprintw(filsWin, "Entre ton choix en position %d :\n", i);
                wrefresh(filsWin);
                
                choix = getch();
                
                if(choix < '0' | choix > '7'){
                    wprintw(filsWin, "Ce n'est pas dans les choix. Réessaie.\n");
                    wrefresh(filsWin);
                    i--;
                }
                else {
                    combiGagnante[i-1] = choix;
                    if(send(SockService, &choix, 1, 0) >= 0){
                        wprintw(filsWin, "\n%s%cm%s%s inséré en position %d.\n\n", EXMNRM, combiGagnante[i-1], DOT, RSTCOL, i);
                        wrefresh(filsWin);
                    }
                    else {
                        perror("send()");
                        raise(SIGINT);
                    }
                }
            }
            
            do {
                wprintw(filsWin, "Combien veux-tu donner d'essais à ton décodeur ? 12 maxi.\n> ");
                wrefresh(filsWin);
                scanf("%d", &essaisDonnes);
                if(essaisDonnes > 12){
                    wprintw(filsWin, "C'est trop. Réessaie.\n");
                    wrefresh(filsWin);
                    //continue;
                }
                else if(essaisDonnes < 1){
                    wprintw(filsWin, "Bien tenté, mais tu ne peux pas ne pas lui donner d'essais. Réessaie.\n\n");
                    wrefresh(filsWin);
                    //continue;
                }
            } while(essaisDonnes >= 1 | essaisDonnes <= 12);
            
            it = htonl(essaisDonnes);
            if(send(SockService, &it, sizeof(it), 0) < 0){
                perror("recv() ");
                raise(SIGINT);
            }
            
            wprintw(filsWin, "Le décodeur se prépare...\n");
            wrefresh(filsWin);
            if(recv(SockService, &pret, 1, 0) < 0){
                perror("recv() ");
                raise(SIGINT);
            }
            
            if(pret == 'y' | pret == 'Y'){
                wprintw(filsWin, "Le décodeur est prêt, c'est parti.\n");
                wrefresh(filsWin);
                wgetch(filsWin);
            }
            else if(pret == 'n' | pret == 'N' | pret == 0x01){ // TODO : trouver le code FIN
                wprintw(filsWin, "Ah non, il est parti.\n\n");
                wrefresh(filsWin);
                close(SockService);
                wgetch(filsWin);
                wrefresh(filsWin);
                delwin(filsWin);
                clear();
                endwin();
                exit(1);
            }
            
            essaiActuel = 1;
            
            wclear(filsWin);
            
            while(essaiActuel <= essaisDonnes){
                wprintw(filsWin, "Essai N.%d en cours...\n\n", essaiActuel);
                wrefresh(filsWin);
                
                for(i = 0; i < 5; i++){
                    if(recv(SockService, &combiEssayees[i][essaiActuel-1], 1, 0) < 0){
                        perror("recv() ");
                        raise(SIGINT);
                    }
                    if(combiEssayees[i][essaiActuel-1] == 0x01){ // TODO : trouver le code FIN
                        wprintw(filsWin, "Le client nous a quitté.");
                        wrefresh(filsWin);
                        close(SockService);
                        wgetch(filsWin);
                        wrefresh(filsWin);
                        delwin(filsWin);
                        clear();
                        endwin();
                        exit(1);
                    }
                    
                    wprintw(filsWin, "%s%cm%s%s ", EXMNRM, combiEssayees[i][essaiActuel-1], DOT, RSTCOL);
                    //printw("%d octets reçus\n", nb);
                    wrefresh(filsWin);
                }
                
                wprintw(filsWin, "\n\nLe placement des pions d'investigation se fait ainsi :\n");
                wprintw(filsWin, "0 = Rien (mauvaise couleur, mauvais endroit)\n1 = Blanc (bonne couleur, mauvais endroit)\n2 = Noir (bonne couleur, bon endroit)\n\n");
                wprintw(filsWin, "C'est toi qui les place. Tu n'es pas obligé de les placer selon les positions.\n");
                wrefresh(filsWin);
                
                for(i = 0; i < 5; i++){
                    choix = wgetch(filsWin);
                    if(choix >= '0' | choix <= '2'){
                        pionsInvest[i][essaiActuel-1] = choix;
                        if(send(SockService, &choix, 1, 0) < 0){
                            perror("send()");
                            raise(SIGINT);
                        }
                        else {
                            wprintw(filsWin, "%s%cm%s%s ", EXMNRM, convertPionIntoColor(pionsInvest[i][essaiActuel-1]), INVDOT, RSTCOL);
                            wrefresh(filsWin);
                            
                            if(pionsInvest[i][essaiActuel-1] == '2') cpt++;
                        }
                    }
                    else {
                        wprintw(filsWin, "Ce n'est pas dans les choix. Réessaie.");
                        wrefresh(filsWin);
                        i--;
                    }
                }
                
                if(cpt == 5) {
                    codGagne = FALSE;
                    break;
                }
                else {
                    essaiActuel++;
                    if(essaiActuel > essaisDonnes) {
                        codGagne = TRUE;
                        break;
                    }
                    wprintw(filsWin, "Il a pas encore trouvé.");
                    wrefresh(filsWin);
                    wgetch(filsWin);
                }
            }
            
            switch(codGagne){
                case TRUE:
                    wprintw(filsWin, "C'est gagné ! Il a utilisé tous ses essais.");
                    wrefresh(filsWin);
                    break;
                case FALSE:
                    wprintw(filsWin, "Il a réussi en %d essais, dommage.", essaiActuel);
                    wrefresh(filsWin);
                    break;
                default:
                    wprintw(filsWin, "Ah ben le client est parti.\n");
                    wrefresh(filsWin);
                    break;
            }
            
            wprintw(filsWin, "Appuie sur n'importe quelle touche pour quitter.\n");
            wrefresh(filsWin);
            wgetch(filsWin);
            wrefresh(filsWin);
            delwin(filsWin);
            clear();
            endwin();
            exit(0);
        }
        
        wait(NULL);
        printf("oui");
        close(SockService);     
    }
}

int creerSocket(int type, int *portTCP, struct sockaddr_in *ptr_adresse){
    int desc;
    int longueur = sizeof(struct sockaddr_in);
    
    desc = socket(AF_INET, type, 0);
    if(desc==-1){
        perror("Erreur création de socket");
        return(-1);
    }
    
    ptr_adresse->sin_family = AF_INET;
    ptr_adresse->sin_port = htons(*portTCP);
    ptr_adresse->sin_addr.s_addr = INADDR_ANY;
    if((bind(desc, (struct sockaddr *)ptr_adresse, longueur))==-1){
        perror("Erreur bind");
        close(desc);
        exit(-1);
    }
    
    if(ptr_adresse != NULL)
        getsockname(desc, ptr_adresse, &longueur);
    
    return desc;
}



char convertPionIntoColor(char p){
    switch(p){
        case '0':
            return '\0';
        case '1':
            return '7';
        case '2':
            return '0';
        default:
            return -1;
    }
}


void segVSig(int sig){
    printf("Erreur de segmentation.\n");
    closeEverything();
}

void intSig(int sig){
    printf("Commande d'interruption reçue.\n");
    closeEverything();
}

void pipeSig(int sig){
    printf("\nSIGPIPE reçu : communication interrompue.\n");
    closeEverything();
}

void closeEverything(){
    printf("Fermeture forcée du serveur.\n\n");
    close(SockService);
    close(SockEcoute);
    delwin(filsWin);
    refresh();
    delwin(pereWin);
    refresh();
    clear();
    endwin();
    exit(1);
}

I can also post the client if needed.

EDIT 2: mast_vars.h :

#ifndef _MAST_VARS_H
#define _MAST_VARS_H 1

int essaisDonnes, essaiActuel, choix;
int cpt = 0;

char pret;
char combiGagnante[5];

#endif

EDIT 3: No more essaisDonnes = 0;, and I changed getchar() to getch().


Solution

  • I just figured it out myself:

    dup2() creates a duplicate of the connection's file descriptor into STDIN_FILENO, leaving it open only in stdin after close(), thus reading stdin with getch, getchar or any other functions was basically waiting for the client to send something.

    Removing both solved my problem: getch() now works properly.