Linux Server + Multithreading
-
Hallo
Ich habe mal generellt eine Frage zum Thema Multithreading + Server.
Ich arbeite mich gerade an einen Linux server heran, der von mehreren Clients Verbindungen annimmt und dann entsprechend behandelt.
Doch irgendwie habe ich gerade das typische Brett vor dem Kopf.http://www.linuxhowtos.org/C_C++/socket.htm?styleswitcher=line
Als Referenz benutze ich den oben genannten Link.So, also.
Nachdem der Server initialisiert und gestartet wurde, wartet er ja nun auf Verbindungen.
Der Server bleibt solange "gefroren" bis eine neue Verbindung eintritt.
Diese weist er dann einem Socket zu.newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
Und nun wartet der Server dann mit
n = read(newsockfd,buffer,255);
auf den Eingang einer Nachricht vom Client.
Schön und gut, ich habe mir aber vorgestellt wie es denn wäre wenn der Server generell auf irgend eine Incoming Message wartet.
Also ganz egal ob von Client1, Client2 oder Client3.Ich habe nun nämlich 3 Threads.
Der eine ist Main
Dieser behandelt ganze allgemeine Dinge wie Datenbank am Anfang öffnen und am Schluss wieder schliessen.Der zweite ist tAccept.
Dieser wartet auf eingehende Sockets und weist diese dann zu.
Schlussendlich speichert er dann alle Verbindungen in einem Array, damit broadcast Nachrichten gesendet werden können.Der dritte und letzte ist tHandleRequest.
Wie der Name vielleicht schon verratet, bearbeitet dieser Anfragen.
Wenn aber der Thread tHandleRequest auf eingehende Nachrichten von Client 1 wartet, werden die anderen Clients dahinter gestellt, weil der Thread solange gefroren bleibt.Wie kann ich nun machen, dass der Thread trotzdem die anderen Clientanfragen bearbeitet?
Für jedes Socket einen eigenen Thread finde ich ziemlich sinnlos, da in der finalen Applikation gut und gern ~500 offene Verbindungen bestehen können.Ich hoffe ihr habt mein Problem verstanden!
Beste Grüsse
-
dafür kann man polling verwenden. Portabel aber nicht sehr effizient ist man: select(2) und sehr effizient aber nur für Linux ist man: epoll (für *BSD oder OS X kqueue). Gibt aber natürlich auch Frameworks, die so etwas abstrahieren und du immer die beste Pollingmöglichkeit nimmst (zB libevent (C) oder Boost.Asio (C++))
-
Hallo,
hmm.. Auf den ersten Blick bin ich jetzt schon fast ein bisschen überfordert mit der Antwort.
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) == -1) { fprintf(stderr, "epoll set insertion error: fd=%d\n", client); return -1; }
Hier geht er doch immer nur das eine Socket durch, oder irre ich mich da?
Bei mir sieht die Annahme der einzelnen Sockets bisher so aus:
while(true) { // Wait for an incoming request newsockfd = accept(sockfd, (strcut sockaddr *) &client_addr, &clilen); // Add the Socket into the Clientlist AddSocketToArray(newsockfd); }
Nun will ich dass in einem 2ten Thread die Sockets sagen wir mal jede Sekunde abgefragt werden ob sie was senden wollen.
Kann ich da nicht einfach ne for schleife drüber laufen lassen und jedes socket mittels
n = read(newsockfd,buffer,255);
auf eingehende Nachrichten überprüfen?
Gruss
-
Mehr über epoll erfährst du hier.
Du musst dir das so vorstellen: Mit epoll_ctl() fügst du zu überwachende File descriptors (also Sockets) zu epoll hinzu. Dann musst du irgendwo eine "Main Loop" haben, die epoll_wait() aufruft und die File descriptors zurückliefert, bei denen die gewünschten Aktionen aufgetreten sind (z.B. EPOLLIN wenn auf dem Socket Daten zum lesen bereitstehen, oder wenn das Socket ein Listener ist der auf eingehende Verbindungen wartet (accept eben), dann liefert epoll auch EPOLLIN wenn ein neuer Client verbunden hat). Selbst wenn du eine Serverapplikation mit mehreren Threads bauen willst, empfehle ich dir nur eine Main Loop zu haben, die den Status der Sockets abfragt - im Gegenzug aber mehrere Threads, die die Aufgaben der Clients bearbeiten.
-
Hallo..
Hmm, leider sind immer noch einige Dinge unklar.
Ich post jetzt einfach mal den Code von der Seite und kommentier ihn so wie ich ihn verstanden habe:
struct epoll_event ev, *events; // Neues Epoll wird definiert. for(;;) { // Main Loop (similiar zu while(true)) nfds = epoll_wait(kdpfd, events, maxevents, -1); // Ein Socket will etwas.. :x for(n = 0; n < nfds; ++n) { // ??? if(events[n].data.fd == listener) { // ??? client = accept(listener, (struct sockaddr *) &local, // Client wird akzeptiert &addrlen); if(client < 0){ // Wenn fehlgeschlagen... perror("accept"); // error.. continue; } setnonblocking(client); // ??? ev.events = EPOLLIN | EPOLLET; // ??? ev.data.fd = client; // ??? if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { // Fügt irgendwas dem epoll hinzu ???? bzw. wenn das hinzufügen fehgeschlagen ist fprintf(stderr, "epoll set insertion error: fd=%d0, client); return -1; } } else do_use_fd(events[n].data.fd); // ???? } }
Überall wo ein ??? drinsteht versteh ich nicht ganz was da gemacht wird.
Threads brauch ich rein theoretisch trotzdem min. 3
1 Mainthread
2 epoll thread
3 handlethread (accept, antworten etc.)..gruss
-
Zeile 4: man geht die Liste aller Events durch
Zeile 5: man schaut, ob das Event auf dem Listen-Socket passiert ist
Zeile 12: Man setzt das socket in den Nonblocking-Mode (siehe man: socket(7))
danach wird das neu akzeptierte Socket zu epoll hinzugefügtZeile 22: hier fügst du Code ein, um Ereignisse auf den übrigen Sockets zu behandeln.
-
Die if Abfrage ab Zeile 5 kann ich mir ja ansich sparen, da ich ja nur clients in der Liste habe, oder ist dem nicht so?
Bei Zeile 22 soll ich dann also (bspw mittels switch case) auf die folgenden Events antworten?
+--------------------------------------------------------------------+
| I/O events |
+-----------+-----------+--------------------------------------------+
|Event | Poll flag | Occurrence |
+-----------+-----------+--------------------------------------------+
|Read | POLLIN | New data arrived. |
+-----------+-----------+--------------------------------------------+
|Read | POLLIN | A connection setup has been completed (for |
| | | connection-oriented sockets) |
+-----------+-----------+--------------------------------------------+
|Read | POLLHUP | A disconnection request has been initiated |
| | | by the other end. |
+-----------+-----------+--------------------------------------------+
|Read | POLLHUP | A connection is broken (only for connec- |
| | | tion-oriented protocols). When the socket |
| | | is written SIGPIPE is also sent. |
+-----------+-----------+--------------------------------------------+
|Write | POLLOUT | Socket has enough send buffer space for |
| | | writing new data. |
+-----------+-----------+--------------------------------------------+
|Read/Write | POLLIN| | An outgoing connect(2) finished. |
| | POLLOUT | |
+-----------+-----------+--------------------------------------------+
|Read/Write | POLLERR | An asynchronous error occurred. |
+-----------+-----------+--------------------------------------------+
|Read/Write | POLLHUP | The other end has shut down one direction. |
+-----------+-----------+--------------------------------------------+
|Exception | POLLPRI | Urgent data arrived. SIGURG is sent then. |
+-----------+-----------+--------------------------------------------+bspw mit
[cpp]
switch(events[n]){
case "POLLIN":
read(events[n].data.fd, buffer, 255)
break;default:
break;
}Irgendwie habe ich schwer das Gefühl ich bringe etwas durcheinander.
Gruss
-
Hier die 2 Dinge nochmals besser formatiert.
+--------------------------------------------------------------------+ | I/O events | +-----------+-----------+--------------------------------------------+ |Event | Poll flag | Occurrence | +-----------+-----------+--------------------------------------------+ |Read | POLLIN | New data arrived. | +-----------+-----------+--------------------------------------------+ |Read | POLLIN | A connection setup has been completed (for | | | | connection-oriented sockets) | +-----------+-----------+--------------------------------------------+ |Read | POLLHUP | A disconnection request has been initiated | | | | by the other end. | +-----------+-----------+--------------------------------------------+ |Read | POLLHUP | A connection is broken (only for connec- | | | | tion-oriented protocols). When the socket | | | | is written SIGPIPE is also sent. | +-----------+-----------+--------------------------------------------+ |Write | POLLOUT | Socket has enough send buffer space for | | | | writing new data. | +-----------+-----------+--------------------------------------------+ |Read/Write | POLLIN| | An outgoing connect(2) finished. | | | POLLOUT | | +-----------+-----------+--------------------------------------------+ |Read/Write | POLLERR | An asynchronous error occurred. | +-----------+-----------+--------------------------------------------+ |Read/Write | POLLHUP | The other end has shut down one direction. | +-----------+-----------+--------------------------------------------+ |Exception | POLLPRI | Urgent data arrived. SIGURG is sent then. | +-----------+-----------+--------------------------------------------+
switch(events[n]){ case "POLLIN": read(events[n].data.fd, buffer, 255) break; default: break; }
-
Hallo, das Problem besteht leider weiterhin.
Gruss