[C] threaded Server - Thread Communication

lockdoc

Well-Known Member
Hallo Leute,

ich steh hier grade ein bissel auf dem Schlauch. Ich versuche einen Server zu basteln an denen sich Clients "anmelden" koennen. Der Client soll dann den Server anfragen koennen wer grade alles angemeldet ist. Das prob was ich grade nicht geloest bekomme ist der Server der die client connections in threads schmeisst. Ich weiss dann nicht wie ich in den einzelnen threads nachfragen kann wer grade von den Clients her angemeldet ist.

Main
PHP:
int main(int argc, char **argv)
{
	signal(SIGCHLD, SIG_IGN);

	struct sockaddr_in cli_addr;
	int	sockfd, newsockfd, pid;
	int	cli_len = sizeof(cli_addr);

	pthread_t	thread_id;

	// bind and let server listen
	sockfd = init_server(sockfd);
	
	while (1)
	{
		// accept a new client
		newsockfd = accept_client(sockfd, cli_len, cli_addr);

	    // Create a worker thread
		if (pthread_create(&thread_id, NULL, handle_cli_request, (void *)newsockfd) != 0)
		{
			handle_error("THREADING FAILED!");
		}
	}
	return 0; 
}

init_server
PHP:
int init_server()
{
	int sockfd;
	struct sockaddr_in serv_addr;

	// step 1: create TCP Socket
	if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
		handle_error("Could not open socket");

	// zero  out serv_addr
	bzero((char *) &serv_addr, sizeof(serv_addr));
	
	serv_addr.sin_family		= AF_INET;
	serv_addr.sin_addr.s_addr	= INADDR_ANY;		// listen on all addresses
	serv_addr.sin_port			= htons(SRV_PORT);	// htons convert port_num from host_byte_order to network_byte_order

	// step 2: bind socket
	if ( bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 ) 
		handle_error("Could not bind Server");
	
	// step 3: listen
	listen(sockfd, 5);	// 5 is size of connections queue to wait for

	return (sockfd);
}

accept_client
PHP:
int accept_client(int fd, int cli_len, struct sockaddr_in cli_addr)
{
	int newsockfd = accept(fd,(struct sockaddr *) &cli_addr, &cli_len);
	if ( newsockfd  < 0 )
		handle_error("Client Request not accepted");
	
	return (newsockfd);
}

Ich wuerde also irgendwo gern einen array pflegen in dem die IPs der derzeitigen Clients drin stehen. Und die Function handle_cli_request sollte dann darauf zugreifen koennen. Nur leider hab ich absolut keine Ahnung wie ich das grade umsetzen soll.

Hat Jemand einen Ansatz?
 
Zuletzt bearbeitet:
Der Vorteil, oder auch manchmal der Nachteil von Threads ist doch, das gerade alle Threads den selben heap sehen, d.h. du kannst einfach eine Struktur allokieren und einen Pointer an die Threads uebergeben (oder global setzen), die ein integer fuer die Anzahl der angemeldeten Clients enthaelt und deren Informationen. zb sowas wie:
Code:
struct clients {
int num;
struct sockaddr_in clients[1024]; // oder was und wieviel auch immer 
};

Jetzt muss du nur aufpassen, das du keine race-conditions bekommst, d.h. du solltest mit mutexes arbeiten und bevor du neue Clients zur Struktur hinzufuegst die Threads locken, damit die keinen Unfug treiben, waehrend du was neues reinschreibst und sie lesen. Siehe pthread_mutex_init(3) und pthread_mutex_lock(3) als Einstieg. Thread Synchronisation kann sehr nervraubend sein. ;)
 
Eigentlich ist das Locking unnötig, die einzige Operation die atomar sein muss ist
das erhöhen und auslesen der Anzahl der Clients! Die handle_cli_request-Funktion ließt die Daten ja nur aus.

Sollte doch mit "compare-and-swap" funktionieren.
Oder falls er's mit verketteten Listen macht mit dem Algorithmus für "non-blocking
multithreaded linked-lists".
 
Implementiert man sowas nicht mit mutex? Wie gesagt, in 99,99 Prozent der Faelle sollte es auch ohne gehen, aber ich denke immer an den worst case bei Threads und wenn er den Counter erhoeht hat, aber die Daten in das Array noch nicht geschrieben hat, kriegt man falsche Ergebnisse. ;)
 
Noch sinnvoller ist es, beim Starten des Threads eine eigene vorallozierte Struktur zu übergeben, in der sich der Thread separat austoben kann. So kannst du auf Locking verzichten.
 
CAS lässt sich bequem auch über inline Assembler und cmpxchg oder so implementieren!

Meinte nur, dass pthread_mutex schon nen erheblichen Overhead bildet mit threadqueue,...
 
Danke fuer die Infos, ich versuche das ganze erst einmal mit einer simplen globalen Struktur ... darauf haett ich auch irgendwie selber kommen koennen, aber manchmal kommt man einfach nicht auf sowas simples :-)
 
[Neues Problem]

Ich bin jetzt schon ein Stueckchen weiter, aber es scheint noch ein (fuer mich derzeit) nicht so einfaches Problem entstanden zu sein.

Was ich insgesamt erreichen will
Das ganze ist ein P2P/Server Filesharing Programm (soll es mal werden).
Der Client soll ueber den Server anfragen koennen, ob jemand von den anderen angemeldeten Clients eine bestimmte Datei hat. Hat der Server ein oder mehrere Clients mit der gewuenschten Datei gefunden, wird die Info an den Client, der die Anfrage gestellt hat zugeteilt und dieser kann sich dann direkt an diese Clients wenden.


Kurz zum jetzigem Problem-Aufbau:
Der Server wartet auf eine Client Connection, ist diese entstanden packt er die in einen thread und dieser kuemmert sich dann um den neuen Client.
main
PHP:
int main(int argc, char **argv)
{
...
	while (1)
	{
		newsockfd = accept_client(sockfd, cli_len, cli_addr);

		pthread_create(&thread_id, NULL, handle_cli_request, (void *)newsockfd) != 0;
	}
return 0; 
}

Die Function die sich um den Client selbst kuemmert hat eine Endlosschleife und wartet darauf das der Client eine Anfrage startet, diese wird dann beantwortet.
handle_cli_request
PHP:
[01]  void *handle_cli_request (void *arg)
[02]  {
[66]  ...
[67]	while (1)
[68]	{
[69]		ret	= read(fd, request, BUFFER_LEN);
[70]		
[71]		// ...  je nach input wird hier dann eine information an den client returned
[72]	}
[73]  ...
[74]  }

Das eigentliche Problem:
Der Client sendet jetzt eine Dateianfrage an den Server. Diese Anfrage liest der server in Zeile [69] ein. Jetzt kann ich irgendwo ne globale struct haben, wo diese Aufgabe reingeschrieben wird, damit alle threads darauf Zugriff haben und ihren jeweiligen client fragen koennen und die antwort wiederrum irgendwo reinschreiben... Allerdings Koennen die threads nicht so einfach die Aufgabe zugreifen, da ja alle mit warten in [69] beschaeftigt sind bis ihr jeweiliger Client eine Anfrage reinschickt und wenn er das erstmal ne weile nicht tut, dann warten und warten und warten die dort.

Mir fehlt hier einfach die Erfahrung wie ich das am besten loesen kann
 
Wenn ich das richtig verstehe, wuerdest du ja eigentlich in [69] warten wollen, bis entweder Daten vom Client kommt, oder ein anderer Server Thread eine neue Aufgabe in die globale Struktur geschrieben hat.

Du koenntest zB mittels kqueue pruefen ob an dem Socket was zu lesen ist und zusaetzlich ueber user events, ob eine neue Aufgabe vorhanden ist. Dazu braeuchtest du aber vermutlich fuer jeden Server Thread eine kqeue Event Queue, dessen Deskriptor fuer die anderen Threads zugaenglich sein muesste, dann koennte man das Event mit kevent() antriggern. Waere aber IMO nicht besonders schoen und wirklich portabel ist es auch nicht.

Alternativ kannst du alle Clients in einem Thread bearbeiten, dazu wuerdest du mittels fcntl() die Sockets nicht-blockierend schalten, und dann mit select/poll/kqueue (bzw lieber mit einem Wrapper wie libev oder libevent) testen, ob und auf welchem Socket neue Daten angekommen sind. Dann koenntest du die Anfrage gleich auslesen und diese Aufgabe direkt bearbeiten ohne das an andere Threads weitergeben zu muessen.
 
Zurück
Oben