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