Sockets-Двоен печат от страна на клиента

Разработвам просто приложение за чат, използващо сокети. Управлява се клиент-сървър. Всичко работи перфектно, с изключение на този бъг, който не мога да намеря: ако аз съм първият клиент, който се свързва със сървъра, всяко съобщение, което получавам, се отпечатва само веднъж на екрана (както трябва да бъде). Въпреки това, ако аз съм вторият клиент, който се свързва, всяко съобщение, което получавам, се отпечатва два пъти, третият клиент вижда съобщенията си три пъти и т.н. Направо полудявам от това:

Когато получа нещо през сокета, го отпечатвам така:

printf (" >%s \n", received);

Следователно, ако получа съобщението здравей, изпратено от потребителя "charles", отпечатвам съобщението ">charles: здравей", това, което ме учудва, е, че дублиращите се разпечатки нямат знака ">", който добавих (дубликатът ще be "charles: здравей"), като по този начин не мога да намеря къде се прави отпечатването (printf() по-горе е единственото printf(), което имам в моя код).

Ще оценя всякаква помощ. Ето частта от кода, която получава от сокета:

while (1) {
    recv(newsockfd, recibido, 255, 0);
    printf (">%s \n",recibido);
}

Този код се изпълнява от нишка.

Благодаря момчета.

Ето пълния код на клиента:

void *Atender(void *estructura){
   DATOS *info;
   info = (DATOS *)estructura;
   int newsockfd=info->socket;
   char recibido[255];
   memset(recibido,1,255);
   while (1){
       recv(newsockfd, recibido, 255, 0);
       printf (">%s \n",recibido);
       memset(recibido,0,255);
   }
}

main(int argc, char *argv[]) {
    int sockfd;
    struct sockaddr_in serveraddr;
    char *server;
    Hilos = malloc((200)*sizeof(pthread_t));

    /* Remember the program name for error messages. */
    programname = argv[0];

    //Realizo lectura e imprimo para chequear que se leyo todo bien.
    lectura(argv,argc);

    /* Who's the server? */
    server=host;


    /* Get the address of the server. */
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(server);
    serveraddr.sin_port = htons(atoi(puerto));

    /* Open a socket. */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        fatalerror("can't open socket");

    /* Connect to the server. */
    if (connect(sockfd, (struct sockaddr *) &serveraddr,
        sizeof(serveraddr)) < 0)
        fatalerror("can't connect to server");

    enviarInfoInicial(sockfd,nombre,"Actual");

    char buff[200];
    memset(buff,1,200);


    //Creacion del hilo que se quedara siempre escuchando
    DATOS d;
    d.socket=sockfd;
    pthread_create(&Hilos[iterador_hilos],NULL,Atender,(void *)&d);
    iterador_hilos++;

    //El que no es un hilo se queda siempre esperando por si se quiere
    //introducir un comando
    while (1) {
        fgets(buff, sizeof buff, stdin);
        mandarInstruccion(sockfd,buff);
        if ((buff[0] == 'f') & (buff[1] == 'u') & (buff[2] == 'e')){
            printf("Cchat se ha cerrado. \n");
            close(sockfd);
            exit(0);
        }

        memset(buff,1,200);
    }
}

Кодът е на испански, но общата идея е разбираема. Структурата DATOS е просто структурата, която предавам на създадената нишка, и съдържа само сокета, от който тази нишка ще чете (while(1) публикувано по-рано). "ATENDER" е процедурата, която нишката ще изпълни.

Ето съответния код от сървъра:

void EjecutarInstruccion(char tipo[],char argumento[],char usuario[]) {
    if (!strcmp(tipo,"men")) {
        char sala[20];
        char total[300];
        memset(total,0,300);
        strcpy(sala,ObtenerSalaUsuario(usuario));
        int i=0;
        for (i;i<200;i++) {
            if (!strcmp((lusuarios[i].sala),sala)) {
                strcat(total,usuario);
                strcat(total,": ");
                strcat(total,argumento);
                send (lusuarios[i].socket, total, strlen(total)+1, 0 );
            }
        }
    }
}

ОТ страната на сървъра имам масив от потребители, всеки потребител съдържа своето потребителско име, стаята за чат, в която се намира, и неговия сокет (fd). Когато сървърът трябва да изпрати съобщение, преминавам през масива от потребители и изпращам съобщението до този потребител само ако той е в същата стая за чат. Благодаря.


person CDS18    schedule 24.11.2013    source източник
comment
Ще трябва да публикувате своя клиентски и сървърен код, ако искате да открием грешка в него.   -  person simonc    schedule 24.11.2013
comment
Добре, ще го добавя веднага   -  person CDS18    schedule 24.11.2013
comment
Трябва да публикувате съответния код на сървъра, тъй като това е, което вероятно изпраща същото съобщение.   -  person Duck    schedule 24.11.2013
comment
Наистина проверих сървъра. изпраща съобщението само веднъж. Въпреки това ще го добавя към публикацията.   -  person CDS18    schedule 24.11.2013


Отговори (1)


Не проверявате колко байта е върнал recv. Не е задължително да чете всички данни, които са неизпълнени. Вероятно трябва да четете в цикъл, докато не бъдат получени всички байтове.

person usr    schedule 24.11.2013
comment
Продадено е момчета. някакъв memset, който липсваше. благодаря ти все пак. - person CDS18; 24.11.2013
comment
Същото за send(). Следователно OP игнорира съответните стойности, върнати от recv() и send(). - person alk; 24.11.2013