Сокеты-Двойная печать на стороне клиента

Я разрабатываю простое приложение для чата с использованием сокетов. Это клиент-сервер. Все работает отлично, за исключением этой ошибки, которую я не могу найти: если я первый клиент, который подключается к серверу, каждое полученное сообщение печатается на экране только один раз (как и должно быть). Тем не менее, если я второй клиент, который подключается, каждое сообщение, которое я получаю, печатается дважды, третий клиент видит свои сообщения три раза и так далее. Я действительно схожу с ума из-за этого:

Когда я получаю что-то через сокет, я печатаю это так:

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

Следовательно, если я получаю сообщение hello, отправленное пользователем «charles», я печатаю сообщение «>charles: hello», что меня поражает, так это то, что дубликаты отпечатков не имеют добавленного мною символа «>» (дубликат будет быть "charles: hello") , из-за чего я не могу найти, где выполняется печать (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
Продали ребята. какой-то мемсет, который отсутствовал. спасибо в любом случае. - person CDS18; 24.11.2013
comment
То же самое для send(). Следовательно, OP игнорирует соответствующие значения, возвращаемые recv() и send(). - person alk; 24.11.2013