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()
.
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.