Forum: PC-Programmierung Linux: Server-Anwendung


von Dennis S. (eltio)


Lesenswert?

Hallo zusammen,

nach welchen Schlagworten muss ich suchen, wenn ich unter Linux eine 
nicht blockierende (Server-)Anwendung schreiben möchte: Konkret: auf dem 
Raspberry PI soll eine Anwendung laufen, mit der remote über ein 
Socket-Interface die Stati der Eingänge ausgelesen werden können.

Kann ich alles in eine Endlosschleife schmeißen, auf dem Port lauschen 
und der Scheduler erledigt den Rest?

Gruß
Dennis

Edit: Interessant wäre auch noch wie man "Starter-Skripte" realisiert, 
in der Form:
1
 ./appl status
2
 ./appl start
3
...
Einfach über "argv"?

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Dennis S. schrieb:
> nach welchen Schlagworten muss ich suchen, wenn ich unter Linux eine
> nicht blockierende (Server-)Anwendung schreiben möchte:
Welche Sprache solls denn sein? C? Python? node.js? php? Ruby?
Wie waers mit "linux non blocking server"?
hier, erste treffer: 
http://stackoverflow.com/questions/8195724/non-blocking-client-and-server-sockets-in-c
http://www.binarytides.com/socket-programming-c-linux-tutorial/

Dennis S. schrieb:
> und der Scheduler erledigt den Rest?
Nein , macht er nicht. Du musst schon selbst dafuer sorgen, dass das 
ganze nicht blockiert, einfach das non-blocking flag nutzen...

Dennis S. schrieb:
> "Starter-Skripte"
Was soll das sein?

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Dennis S. schrieb:
> nach welchen Schlagworten muss ich suchen, wenn ich unter Linux eine
> nicht blockierende (Server-)Anwendung schreiben möchte: Konkret: auf dem
> Raspberry PI soll eine Anwendung laufen,
Linux Daemon


> mit der remote über ein
> Socket-Interface die Stati der Eingänge ausgelesen werden können.
Unix Socket Programming Howto oder so ähnlich

> Kann ich alles in eine Endlosschleife schmeißen, auf dem Port lauschen
> und der Scheduler erledigt den Rest?
So ähnlich

> Edit: Interessant wäre auch noch wie man "Starter-Skripte" realisiert,
> in der Form: ./appl status
>  ./appl start
> ...
> Einfach über "argv"?
Ja, auswerten über argc/argv.
siehe z.B. BusyBox.

von Andreas E. (hismastersvoice)


Lesenswert?

Moin,
google mal nach "tutorial linux network programming" und suche Dir eines 
heraus, was Dir zusagt. Ohne Tutorial wirst Du nicht weit kommen.

Ansonsten man pages zu poll oder epoll, listen, connect, read, write 
usw. lesen.

Als Buch ist Richard Stevens "Unix Network Programming" zu empfehlen, da 
didaktisch sehr gut, für den Einstieg, je nach Kenntnisstand, aber 
vielleicht etwas erschlagend.

Das Starter Skript ist erstmal Dein geringstes Problem, aber status und 
start sind nicht Parameter Deiner Anwendung, die wohl mehr im 
Hintergrund als daemon laufen wird (siehe man page daemon), sondern des 
Skriptes.

Hier empfiehlt sich auch Richard Stevens 'Advanced Programming in the 
Unix System Environment', das auch Daemon Prozesse und alles um 
Unix/Linux Systeme gut erklärt. Wirst Du langfristig brauchen, wenn Du 
effiziente Server Prozesses schreiben willst.

In eine Endlosschleife kann man alles 'schmeißen', macht man aber nicht 
mehr so (siehe epoll, schon erwähnt).

In diesem Sinne,
frohes Schaffen.

von Dennis S. (eltio)


Lesenswert?

Kaj schrieb:
> Welche Sprache solls denn sein? C? Python? node.js? php? Ruby?
Ich war mir sehr sicher den "C"-Filter hier aktiviert zu haben, scheint 
aber nicht geklappt zu haben. Also: C!

> Wie waers mit "linux non blocking server"?
Danke, aber sehr allgemein.

> Nein , macht er nicht. Du musst schon selbst dafuer sorgen, dass das
> ganze nicht blockiert, einfach das non-blocking flag nutzen...
Dann habe ich wohl das Konzept vom Scheduler nicht verstanden: warum 
kann ein präemptiver Scheduler (der in Linux ja wohl vorhanden ist) 
plötzlich nur noch arbeiten wenn die Applikationen kooperativ ist?

> Was soll das sein?
Siehe mein erklärendes Beispiel.

Vielen Dank an alle für die Literatur-Hinweise. Das wird mir 
weiterhelfen!

Gruß
Dennis

von Thorsten (Gast)


Lesenswert?

Der Rest wird schon noch weiterlaufen, aber in einer pollenden 
Endlosschleife hast Du permanent 100% CPU Last und das ist im 
Allgemeinen nicht besonders effizient.

von Planlos (Gast)


Lesenswert?

Thorsten schrieb:
> aber in einer pollenden
> Endlosschleife hast Du permanent 100% CPU Last und das ist im
> Allgemeinen nicht besonders effizient.

Darum: (e)"poll" oder den Vorgänger "select" verwenden.
Das verlagert das Warten auf Netzwerk-Ereignisse in den Kernel, 0% 
CPU-Last solange nichts zu tun ist.
Oder du forkst bei jeder neuen Verbindung, und verwendest innerhalb des 
geforkten Childs Blocking IO.

Die Fork-Lösung ist m.M.n viel einfacher (zu verstehen).
Macht halt schnell schlapp, wenn viele hundert Clients gleichzeitig was 
wollen.

Beispiel (von 
http://www.cs.ucsb.edu/~almeroth/classes/W01.176B/hw2/examples/tcp-server.c):
1
/* Sample TCP server */
2
3
#include <sys/socket.h>
4
#include <netinet/in.h>
5
#include <stdio.h>
6
7
int main(int argc, char**argv)
8
{
9
   int listenfd,connfd,n;
10
   struct sockaddr_in servaddr,cliaddr;
11
   socklen_t clilen;
12
   pid_t     childpid;
13
   char mesg[1000];
14
15
   listenfd=socket(AF_INET,SOCK_STREAM,0);
16
17
   bzero(&servaddr,sizeof(servaddr));
18
   servaddr.sin_family = AF_INET;
19
   servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
20
   servaddr.sin_port=htons(32000);
21
   bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
22
23
   listen(listenfd,1024);
24
25
   for(;;)
26
   {
27
      clilen=sizeof(cliaddr);
28
      connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
29
30
      if ((childpid = fork()) == 0)
31
      {
32
         close (listenfd);
33
34
         for(;;)
35
         {
36
            n = recvfrom(connfd,mesg,1000,0,(struct sockaddr *)&cliaddr,&clilen);
37
            sendto(connfd,mesg,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
38
            printf("-------------------------------------------------------\n");
39
            mesg[n] = 0;
40
            printf("Received the following:\n");
41
            printf("%s",mesg);
42
            printf("-------------------------------------------------------\n");
43
         }
44
         
45
      }
46
      close(connfd);
47
   }
48
}

von T.roll (Gast)


Lesenswert?

Dennis S. schrieb:
> Ich war mir sehr sicher den "C"-Filter hier aktiviert zu haben, scheint
> aber nicht geklappt zu haben. Also: C!

Der wirkt nur auf die Threadliste/Suche → "Zeige alles mit dem 
Buchstaben 'C' im Titel"

von Dennis S. (eltio)


Lesenswert?

T.roll schrieb:
> Der wirkt nur auf die Threadliste/Suche → "Zeige alles mit dem
> Buchstaben 'C' im Titel"

Danke für den Hinweis, dessen war ich mir nicht bewusst!

von Sheeva P. (sheevaplug)


Lesenswert?

Planlos schrieb:
> Thorsten schrieb:
>> aber in einer pollenden
>> Endlosschleife hast Du permanent 100% CPU Last und das ist im
>> Allgemeinen nicht besonders effizient.
>
> Darum: (e)"poll" oder den Vorgänger "select" verwenden.
> Das verlagert das Warten auf Netzwerk-Ereignisse in den Kernel, 0%
> CPU-Last solange nichts zu tun ist.
> Oder du forkst bei jeder neuen Verbindung, und verwendest innerhalb des
> geforkten Childs Blocking IO.
>
> Die Fork-Lösung ist m.M.n viel einfacher (zu verstehen).
> Macht halt schnell schlapp, wenn viele hundert Clients gleichzeitig was
> wollen.

Deswegen hat der liebe Gott die Threads erfunden. Der Nachteil dabei 
ist, daß der Requesthandler dabei im Adressraum des Hauptprogramms 
abläuft und Programmierfehler dann nicht mehr nur den einen 
Requesthandler, sondern das ganze Programm und alle seine Subthreads 
bedrohen. Diesen Nachteil hat aber auch die Lösung mit select(2), 
poll(2) oder epoll(7); deswege würde ich die fork(2)-Lösung bevorzugen, 
wenn keine hohe Last erwartet wird.

Wie man Startskripte erstellt, hängt vom Betriebssystem beziehungsweise 
vom verwendeten Init-System ab. Für das SystemV-Init von Debian-Systemen 
gibt es üblicherweise eine Vorlage in /etc/init.d/skeleton, das übliche 
Techniken zur Realisierung eines solchen Skripts demonstriert und als 
Basis für eigene Implementierungen dienen kann.

In diesem Zusammenhang sollte man sich außerdem anschauen, wie man ein 
Programm zu einem Daemon macht: Abkoppeln vom Parentprozess und Starten 
einer eigen Prozessgruppe, Abkoppeln vom kontrollierenden Terminal, und 
das Wechseln zu einer anderen Benutzer- und Gruppenid mit set(e)uid(2) 
und set(e)gid(2). Für einige dieser Aufgaben kennen manche Systeme den 
Befehl daemon(3), oder das Kommandozeilenprogramm daemon(1) aus dem 
Paket daemon, welches auf der libslack-Bibliothek basiert.

von haeh (Gast)


Lesenswert?

Also was willst Du jetzt in einer funktionierenden Linux Umgebung den 
Webserver "pimpen" oder selber was coden ?
Wenn Du schon Linux mit z.B. Apache hast kannst Du via CGI beliebige 
Programme nutzen (Sicherheit mal außen vor).
Wenn's "sicher" sein soll weil DMZ bau Dir selber ein Programm das via 
SSL/TLS auf einem im RFC nicht genannten Port Deine Signale liefert.
Geht auch via PHP, JAVA & Co.
Wenn Du kein Feedback/Kontrolle brauchst wäre es wohl am sinnvollsten 
einfach ein HTML auf der Serverseite zu bauen und anzuzeigen ...

von Sheeva P. (sheevaplug)


Lesenswert?

haeh schrieb:
> Also was willst Du jetzt in einer funktionierenden Linux Umgebung den
> Webserver "pimpen" oder selber was coden ?
> Wenn Du schon Linux mit z.B. Apache hast kannst Du via CGI beliebige
> Programme nutzen (Sicherheit mal außen vor).
> Wenn's "sicher" sein soll weil DMZ bau Dir selber ein Programm das via
> SSL/TLS auf einem im RFC nicht genannten Port Deine Signale liefert.
> Geht auch via PHP, JAVA & Co.
> Wenn Du kein Feedback/Kontrolle brauchst wäre es wohl am sinnvollsten
> einfach ein HTML auf der Serverseite zu bauen und anzuzeigen ...

Nur um ein paar GPIOs zu exponieren, sind Apache/CGI/PHP/Java/HTTP/HTML 
wohl mit schwerer Artillerie auf unbewaffnete Haussperlinge geschossen, 
zumal der OP ja selbst nach einem schlichten Socket-Interface fragt.

Mir ist eben eingefallen, daß so ein Linux ja bereits eine ausgesprochen 
komfortable Möglichkeit für so etwas dabei hat, nämlich den guten alten 
Inetd. Der kümmert sich nicht nur darum, das richtige Programm zu 
starten, sondern auch um die gesamte Netzwerkkommunkation.

Der Ablauf ist dann: der inetd lauscht auf einem Port auf Verbindungen. 
Wenn ein Client eine solche aufbaut, startet inetd das für diesen Port 
konfiguierte Programm und übergibt ihm die auf dem Socket empfangenen 
Daten (so vorhanden) via STDIN. Und alles, was das Programm auf STDOUT 
oder STDERR schreibt, schickt inetd über den Socket zum Client zurück.

Als Protokoll auf Applikationsebene bietet sich hier das 
leichtgewichtige JSON an. Dafür gibt es kleine Parser für jede relevente 
Programmiersprache und zudem ist das auch von Menschen gut lesbar (think 
debugging!). Und das in eine Webseite oä. einzubinden, ist dank AJAX ein 
Kinderspiel.

von Dennis S. (eltio)


Lesenswert?

Sheeva P. schrieb:
> Mir ist eben eingefallen, daß so ein Linux ja bereits eine ausgesprochen
> komfortable Möglichkeit für so etwas dabei hat, nämlich den guten alten
> Inetd.

Interessante Möglichkeit! Das werde ich mal ausprobieren.

Gruß
Dennis

von elowolo (Gast)


Lesenswert?

Sheeva P. schrieb:
> Nur um ein paar GPIOs zu exponieren, sind Apache/CGI/PHP/Java/HTTP/HTML
> wohl mit schwerer Artillerie auf unbewaffnete Haussperlinge geschossen,
> zumal der OP ja selbst nach einem schlichten Socket-Interface fragt.
Eines davon läuft aber heutzutage sowieso schon, also warum noch wie vor 
100 Jahren mit C auf Sockets rumzuwerkeln? Zudem geht die Entwicklung 
mit Webtechnologien viel schneller und einfacher von der Hand. Sockets 
in C? Das mache ich nur noch wenn ich nen schmallbrüstigen MC habe und 
selbst dort sind die Zeiten  für sowas vorbei, die Turnaroundzeiten 
gehen mit embeddedlinux und den entspr. Webapplikationen viel schneller. 
Zudem kann ich das alles auf einem PC entwickeln und wenns fertig ist 
einfach rüberkopieren, samt Konfig und entspr. Updatefunktionen, da muss 
ich nicht mal was kompilieren.

> Mir ist eben eingefallen, daß so ein Linux ja bereits eine ausgesprochen
> komfortable Möglichkeit für so etwas dabei hat, nämlich den guten alten
> Inetd. Der kümmert sich nicht nur darum, das richtige Programm zu
> starten, sondern auch um die gesamte Netzwerkkommunkation.
inetd ist eine wandelnde Dauersicherheitslücke und deshalb meist per 
default deaktiviert oder gar nicht mehr installiert. Das war schon 
seiner Zeit nur ein übler Hack, das nutzt zu recht keiner mehr.
Zudem hat man oft schnell weitere Anforderungen: 
Datenbankschnittstellen, Email, Webservices von anderen Webservern,... 
geht alles mit zeitgemässer Webtechnologie. Da fummelst du dich mit 
Sockets in C zu Tode.

> Als Protokoll auf Applikationsebene bietet sich hier das
> leichtgewichtige JSON an. Dafür gibt es kleine Parser für jede relevente
> Programmiersprache und zudem ist das auch von Menschen gut lesbar (think
> debugging!). Und das in eine Webseite oä. einzubinden, ist dank AJAX ein
> Kinderspiel.
Und wenn man auf dem Server schon ein entspr. Webtechnologie einsetzt 
muss man sich nicht mal gross mit JSON von Hand befassen, da gibts 
komfortable Generatoren, Parser,... sogar schon in der Datenbank. Das 
fummelt heute keiner mehr selber zusammen und schon gar nicht auf 
Lowlevelebe mit Sockets, in C, nicht mal HTTP fasst man heute noch 
direkt an. Wir sind nicht mehr in den 90ern.

von Thomas (Gast)


Lesenswert?

Dennis S. schrieb:
> Edit: Interessant wäre auch noch wie man "Starter-Skripte" realisiert,

Bei jeder guten Linux-Distri findest Du eine Datei /etc/init.d/skeleton 
die als Vorlage für eigene Start-Scripte dienen soll.

Das klappt auch beim Raspi.

von (prx) A. K. (prx)


Lesenswert?

Thomas schrieb:
> Bei jeder guten Linux-Distri findest Du eine Datei /etc/init.d/skeleton
> die als Vorlage für eigene Start-Scripte dienen soll.

Seit systemd ist das nicht mehr so selbstverständlich. Darin ist 
/etc/init.d nur noch eine historische Altlast.

von Thomas (Gast)


Lesenswert?

A. K. schrieb:
> Seit systemd ist das nicht mehr so selbstverständlich. Darin ist
> /etc/init.d nur noch eine historische Altlast.

Korrekt.
Bei meinem Raspi ist aber noch init.d angesagt.

von LuaUser (Gast)


Lesenswert?


von Dennis S. (eltio)


Lesenswert?

@elowolo: Ich muss dir "leider" widersprechen: wenn man es kann geht so 
eine Miniaufgabe mit Sockets in C ruckzuck.

von Konrad S. (maybee)


Lesenswert?

elowolo schrieb:
> inetd ist eine wandelnde Dauersicherheitslücke

Nicht der inetd ist das Problem, sondern was reingehängt wird. Einfach 
nur eine /bin/sh bringt gewisse Probleme mit sich. ;-)

von Gerd E. (robberknight)


Lesenswert?

elowolo schrieb:
>> Mir ist eben eingefallen, daß so ein Linux ja bereits eine ausgesprochen
>> komfortable Möglichkeit für so etwas dabei hat, nämlich den guten alten
>> Inetd. Der kümmert sich nicht nur darum, das richtige Programm zu
>> starten, sondern auch um die gesamte Netzwerkkommunkation.
> inetd ist eine wandelnde Dauersicherheitslücke und deshalb meist per
> default deaktiviert oder gar nicht mehr installiert. Das war schon
> seiner Zeit nur ein übler Hack, das nutzt zu recht keiner mehr.

Also der xinetd ist mir jetzt nicht als sicherheitstechnisch besonders 
problematisch aufgefallen:
http://www.cvedetails.com/vendor/473/Xinetd.html

Ich finde die Idee den zusammen mit einem kleinen Skript zu verwenden 
eigentlich gar nicht schlecht. Ist natürlich nicht besonders performant, 
aber dafür sehr schnell fertig, ohne daß ich mich groß um Dinge wie 
Non-blocking I/O, Threads oder forken kümmern muss. Und der Serverdienst 
für SysV-Init oder systemd ist auch schon fertig konfiguriert.

So kommt auch jemand, der sich mit diesen ganzen Techniken noch nicht so 
auskennt, schnell zum Ziel.

Wenn ich diese Aufgabe lösen müsste, würde ich es aber wahrscheinlich 
ganz in Python machen und keinen inetd verwenden.

von Lukas K. (carrotindustries)


Lesenswert?

Sheeva P. schrieb:
> In diesem Zusammenhang sollte man sich außerdem anschauen, wie man ein
> Programm zu einem Daemon macht: Abkoppeln vom Parentprozess und Starten
> einer eigen Prozessgruppe, Abkoppeln vom kontrollierenden Terminal, und
> das Wechseln zu einer anderen Benutzer- und Gruppenid mit set(e)uid(2)
> und set(e)gid(2). Für einige dieser Aufgaben kennen manche Systeme den
> Befehl daemon(3), oder das Kommandozeilenprogramm daemon(1) aus dem
> Paket daemon, welches auf der libslack-Bibliothek basiert.

Den ganzen hokuspokus kann man sich mit systemd sparen. Man lässt sich 
von systemd starten, der kümmert sich darum, dass man als richtiger 
Benutzer läuft, die Ausgaben im Log landen, etc.
http://0pointer.de/public/systemd-man/daemon.html

von Dennis S. (eltio)


Lesenswert?

Edit: Hab den Beitrag vom 14.09.2015 10:19 vergessen...

: Bearbeitet durch User
von Sheeva P. (sheevaplug)


Lesenswert?

elowolo schrieb:
> Eines davon läuft aber heutzutage sowieso schon, also warum noch wie vor
> 100 Jahren mit C auf Sockets rumzuwerkeln? Zudem geht die Entwicklung
> mit Webtechnologien viel schneller und einfacher von der Hand.

Webtechnologien sind ganz nett, aber nicht die Antwort auf die Frage.

Ich selbst würde für sowas wahrscheinlich Python mit dem Flask-Framework 
benutzen, das bringt alles Nötige (inklusive Webserver) schon fertig 
mit, wenn ich denn auf Webtechnologien setzen will. Oder wenn es ganz 
schnell und klein werden soll, würde ich C++ mit Webtoolkit benutzen.

Aber hier waren Sockets gefragt. Da gibt es für Python ein wunderbares, 
extrem komfortables Framework namens SocketServer, der direkt Threading 
und ein paar andere Schmankerln mitbringt. Und wenn es ganz schnell und 
klein werden soll, benutze ich C++11 und Boost::ASIO, das hat ebenfalls 
alles gleich mit dabei.

Du siehst: ich kenne da eine ganze Reihe von Möglichkeiten, mit denen 
ich selbst sowas umsetzen würde. Aber C++, Python, und Webtechnologien 
waren hier allesamt nicht gefragt.

> inetd ist eine wandelnde Dauersicherheitslücke und deshalb meist per
> default deaktiviert

Weder, noch. Wenn man es richtig macht, sind inetd (und xinetd) für 
solche Aufgaben immer noch prima geeignet.

> Datenbankschnittstellen, Email, Webservices von anderen Webservern,...
> geht alles mit zeitgemässer Webtechnologie. Da fummelst du dich mit
> Sockets in C zu Tode.

Darum geht es hier aber nicht. Ich weiß auch nicht, wo Dein Problem ist, 
ich arbeite schon lange mit Sockets in C und diversen anderen Sprachen, 
und hatte es dabei noch nie nötig, mich "zu Tode" zu "fummeln".

> Und wenn man auf dem Server schon ein entspr. Webtechnologie einsetzt
> muss man sich nicht mal gross mit JSON von Hand befassen, da gibts
> komfortable Generatoren, Parser,...

Vielen Dank für Deine Belehrungen, aber daß es für JSON kleine Parser 
gibt, hatte ich selbst schon geschrieben, als ich JSON als Datenformat 
vorgeschlagen habe. Aber nur, um ein paar GPIOs per JSON zu exponieren, 
braucht man keinen Generator -- so etwas ist mit "printf("{'gpio1': %d, 
'gpio2': %d}, ...);" schneller von Hand geschrieben als Du eine passende 
Library gefunden hast.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.