GeorgeZv Ответов: 1

Один сервер для нескольких клиентов (чат) в C


Я написал серверно-клиентский чат для Окна (используя потоки и неблокирующий сокет), он прекрасно работает с одним сервером, но все еще не поддерживает несколько клиентов. Сервер работает как echo (но только для последнего подключенного клиента). Client. c использует функцию CreateThread и неблокирующие сокеты.

Я сделал 3 функции, чтобы сохранить значения сокетов подключенных клиентов в списке, распечатать их и удалить, когда клиент ушел. Это прекрасно работает:

    struct listElement
{
    SOCKET socket;
    struct listElement *next;
};

struct clientList
{
    struct listElement* head;
};

/*my implementation function, print and remove*/
int pushBackClient(struct clientList *list, SOCKET socket)
void print(struct clientList *list)
int removeFromList(struct clientList *list, SOCKET socket)


Что ж, эта часть сделана. Теперь я могу собирать сокеты каждого подключенного клиента, когда клиент отключился, его сокет также удаляется.

Но мультиклиентной поддержки по-прежнему нет. Как я должен понять, что? Я думаю, что мой код необходим чтобы изменить несколько строк, чтобы достигнуть моих программ. И следующий вопрос вытекает из последнего: как организовать мой сервер для отправки Эхо-сообщения всем клиентам, кроме клиента, который отправил сообщение?

Что я уже пробовал:

server.c

    /* before this cycle there are socket(), listen() and bind() functions */
	while (1)
			{
				FD_ZERO(&readSet);
				FD_SET(listeningSocket, &readSet);
			
				if (newSocketDescriptor)
				{
					FD_SET(newSocketDescriptor, &readSet);
				}
			
				tv.tv_sec = 5;
				retVal = select(listeningSocket + 1, &readSet, NULL, NULL, 0);
				if (retVal == SOCKET_ERROR)
				{
					printf("Select error");
					break;
				}
				else if (retVal == 0)
				{
					printf(". . .\n");
					continue;
				}
				else
				{
					if ((FD_ISSET(listeningSocket, &readSet)) != 0)
					{
						if ((newSocketDescriptor = accept(listeningSocket, (struct sockaddr *)&clientAddr, &clientAddrSize)) == SOCKET_ERROR)
						{
							printf("Accept error ");
							break;
						}
						/* newSocketDescriptor - new connected client, then implement the list */
						pushBackClient(&clientList, newSocketDescriptor);
						print(&clientList);

						FD_ZERO(&readSet);
						FD_SET(newSocketDescriptor, &readSet);
						nclients++; 
						PRINTUSERS

						HOSTENT *hst = gethostbyaddr((const char *)&serverAddr.sin_addr.s_addr, 4, AF_INET);
						printf("Welcome %s (%s:%d) new connected\n", hst->h_name, inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
					}
				
					// READ
					if (FD_ISSET(newSocketDescriptor, &readSet) != 0)
					{
						if ((numBytes = recv(newSocketDescriptor, &bufferData[0], sizeof(bufferData), 0)) == SOCKET_ERROR)
						{
								printf("Recv failed\n");
								break;
						}
						else
						{
							if (!strcmp(&bufferData[0], "quit\n"))
							{
								removeFromList(&clientList, newSocketDescriptor); // if server gets "quit" - remove socket from the list
								newSocketDescriptor = 0;
								nclients--; PRINTUSERS
								printf("Recv failed\n");
								printf("It seems client (%s:%d) has disconnected\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
								FD_ZERO(&readSet);
							}
							else
							{
								bufferData[numBytes] = '\0';
								Sleep(250);
								printf("Client -> Server: %s", &bufferData[0]);
								FD_ZERO(&writeSet);
								FD_SET(newSocketDescriptor, &writeSet);
							}
						}
					}
					// WRITE (send)
					if (FD_ISSET(newSocketDescriptor, &writeSet) != 0)
					{
						if (send(newSocketDescriptor, &bufferData[0], strlen(&bufferData[0]), 0) == SOCKET_ERROR)
						{
							printf("Send error");
							break;
						}
						bufferData[numBytes] = '\0';
						Sleep(250);
						printf("Server -> Client: %s", &bufferData[0]);
					}
				}
			}

Thanks for attention and advices

1 Ответов

Рейтинг:
0

KarstenK

Вам нужно некоторое управление клиентами, расширяя структуры для сохранения всех данных. Если вы знаете, кто послал передачу, то вы знаете, кому не посылать передачу.

Возможным решением может быть то, что клиенты получают идентификатор от сервера, который сохраняют для сеанса и отправляют с каждым сообщением.