PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Höhere Protokolle/sockets



exit
06-04-2005, 13:10
Ich habe ein Problem mit C unter Linux. Zugegeben, es ist eine Aufgabe aus meinem Studium, aber ich habe mir schon die Finger wundgetippt und meine sämtlichen Bücher gewälzt, aber keine passende Lösung gefunden. Vielleicht kann mir hier jemand den entscheidenden Tipp geben.

Ich habe eine primitive Client/Server Struktur:
Server


// server.c


// Der server setzt auf den Port 20202/tcp auf
// dieser ist als kaffee deklariert

// dazu zusaezliche Zeilen in /etc/services einfgen:
// kaffee 20202/tcp # Kaffee-Server mit tcp
// kaffee 20202/udp # Kaffee-Server mit udp

// gcc -D_REENTRANT -o server server.c -lpthread -lm
// danach unbedingt noch folgendes als root fuer SUID root ausfuehren
// chown root ./server
// chmod u+s ./server

// bearbeitet durch:
// 381363
// Eingefügter Code ist mit "//****..." kommentiert


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <asm/io.h>
//****Bibliothek für XDR einbinden
#include <rpc/xdr.h>
//**** Buffergrösse definieren
#define BUFSIZE 10000


#define QSIZE 5

// User und Group ID von nobody bzw. nogroup
#define student 501

// Protokol festlegen
char *protocol="tcp";
// Service bzw. Port festlegen
char *service="kaffee";

// Diverse Antwort Strings deklarieren
char *response="Kaffee ist fertig !!\n";
char *resp_anderer_kaffee="Ein anderer Kaffee wird gerade gebrht ... Moment bitte ... \n";
char *resp_kaffee_vorbereitung="Moment bitte noch ... Wasser wird gerade vorbereitet ... \n";
char *resp_kaffee_machen="Der Kaffee wird momentan frisch gebrueht ... \n";

// Variable fuer den Socket
int sck;

// Thread variable fuer Kommunikationsthread
pthread_t th;
// Thread variable fuer Hardware Kontrolle
pthread_t th_hc;
// Thread variable fuer Fuellstandskontrolle
pthread_t th_fs;
// Variable fuer Hardwarekontrolle
int controlvalue=0;
// Basisadresse des lokalen parallelen Ports, hier 378hx
int base = 0x378;

// Mutexe deklarieren
pthread_mutex_t mutex;
pthread_mutex_t controlmutex;

// Fuellstand kaffee, in Prozent
double fuellstand=100;
// Anzahl ausgegebener Kaffeebecher
long kaffeebecher=0;

//**** Datentypen, die mit XDR gesendet werden sollen
// davon brauche ich nur art, anzahl.
enum datatypes
{
MY_ART, MY_ANZAHL
};


// Hardwarecontrol Einheit
void *hardwarecontrol(void *dummy)
{
// Hardware kann nur in 1sec. Intervallen angesprochen werden
// Daher alle 1s Status ausgeben
do
{
// Kontrollwert an Adresse schreiben, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Zur Hardware Ansteuerung root Rechte setzen
seteuid(0);
// Aktuellen control-Wert an Hardware senden
outb(controlvalue, base);
//Danach wieder auf nobody zurueck
seteuid(student);
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Eine Sekunde schlafen (Hardwarebedingt)
usleep(500000);
}while(1);
}


// Fuellstandskontrolle
void *fuellstandskontrolle(void *dummy)
{
do
{
// Kontrollieren ob genug Wasser vorhanden ist, in 1s Abstaenden
if(fuellstand<40)
{
// Mutex setzen
pthread_mutex_lock(&mutex);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Signallampe anschalten fuer Wasser kochen & Auffuellen
controlvalue += 64;
// Wasser einfuellen
controlvalue += 1;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Wasser wird aufgefuellt
// Warten auf Wert von Sensor.... hier durch sleep simuliert
sleep(5);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Einfuellen abstellen
controlvalue -= 1;
// Wasser kochen
controlvalue += 2;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Wasser wird gekocht
// Warten auf Wert von Sensor.... hier durch sleep simuliert
sleep(5);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Wasser kochen abstellen
controlvalue -= 2;
// Signallampe fuer Wasser kochen & Auffuellen ausschalten
controlvalue -= 64;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Fuellstand wieder auf voll setzen, damit Kaffee Produktion wieder freigeben
fuellstand=100;

// Mutex wieder freigeben
pthread_mutex_unlock(&mutex);
}

// Eine Sekunde warten
sleep(1);
}while(1);
}

// Kommunikations Thread
void *communication (void *socket_fd)
{
// Test Variable ob ein Kaffee gerade hergestellt wird (0=Nein 1=ja)
int nochmal=0;

// Lokale Variable fuer uebergebenen Socket
int rcv_sck;
// Uebergebenen Socket in lokale Variable kopieren
rcv_sck = *(int*)socket_fd;

do
{
// Falls momentan kein Kaffee gekocht wird und kein Wasser aufgefuellt wird...
if((controlvalue & 192)==0)
{
// Kaffeekoch Prozess beginnt...

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Zuerst Kontrollampe anschalten, damit visualisiert wird, es wird ein Kaffee zubereitet
controlvalue += 128;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Falls Fuellstand zu niedrig ...
if(fuellstand<40)
{
// entsprechende Nachricht an Client senden
write(rcv_sck, resp_kaffee_vorbereitung, strlen(resp_kaffee_vorbereitung));
// und warten bis wieder voll...
do
{
}while(fuellstand<40);
}

// Nachricht an Client senden, dass Kaffee nun gekocht wird
write(rcv_sck, resp_kaffee_machen, strlen(resp_kaffee_machen));

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Kaffee-Pulver fllen
controlvalue += 4;
// Und Milch in Tasse geben
controlvalue += 32;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Simulieren....
sleep(2);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Kaffee Pulver fllen abbrechen
controlvalue -= 4;
// Milch abstellen
controlvalue -= 32;
// Wasser durch Kaffeepulver leiten und direkt in Tasse
controlvalue += 16;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Simulieren....
sleep(4);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Kaffeeeinfuellung stoppen
controlvalue -= 16;
// Kaffee Pulver entfernen
controlvalue +=8;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Simulieren....
sleep(2);

// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Kaffee-Pulver entfernt
controlvalue -= 8;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);


// Mutex setzen
pthread_mutex_lock(&mutex);
// wieder einen Kaffee ausgegeben
kaffeebecher++;
// Fuellstand absenken (Simulation... andernfalls Fuehler)
fuellstand -= 10;
// Mutex wieder freigeben
pthread_mutex_unlock(&mutex);

// Kaffee ist ausgegeben ... daher Lampe aus
// Kontrollwert setzen, dabei Mutex locken
pthread_mutex_lock(&controlmutex);
// Letztes Bit bzw. letzte Lampe ausschalten
controlvalue -= 128;
// Mutex wieder freigeben
pthread_mutex_unlock(&controlmutex);

// Antwort an den Client schicken
write(rcv_sck,response,strlen(response));

// Ausgeben wieviel Becher schon ausgeschenkt wurden
printf("Ausgeschenkte Becher Kaffee: %i\nAktueller Fuellstand des Wasser: %.2f %\n\n", kaffeebecher, fuellstand);

//Nicht nochmal kochen
nochmal=0;
}

// Wenn gerade Kaffee gekocht wird, entsprechende Meldung an den client senden
if((controlvalue & 128)==128)
{
// Nachricht an Client senden
write(rcv_sck, resp_anderer_kaffee, strlen(resp_anderer_kaffee));

// Solange warten bis Kaffeemaschine wieder freigegeben ist
do
{
usleep(100000);
}while((controlvalue & 128)!=0);

// Nochmal versuchen Kaffee zu kochen
nochmal=1;
}

// Wenn gerade Wasser aufgefuellt und gekocht wird, entsprechende Meldung an den client senden
if((controlvalue & 64)==64)
{
// Nachricht an Client senden
write(rcv_sck, resp_kaffee_vorbereitung, strlen(resp_kaffee_vorbereitung));

// Solange warten bis Wasser aufgefllt und gekocht ist
do
{
usleep(100000);
}while((controlvalue & 64)!=0);

// Nochmal versuchen Kaffee zu kochen
nochmal=1;
}

}while(nochmal==1);

// diesen Socket schlie�n
close(rcv_sck);

// Thread beenden
pthread_exit (NULL);
}


// Funktion zur Behandlung, wenn CTRL-C gedrueckt wurde
void quit()
{
// Socket schlie�n
close (sck);

// Zur Hardware Ansteuerung root Rechte setzen
seteuid(0);
// Hardwareport schliessen und zuvor alles auf 0 setzen
outb(0, base);
ioperm(base,3,0);
// Danach wieder zu nobody wechseln
seteuid(student);

// Meldung ausgeben
printf("\n\n%s server ber %s beendet!!\n\n", service, protocol);
// Server beenden
exit(0);
}


// Hauptfunktion
main()
{
// Variablen bzw. Strukturen zur Kommunikation
struct servent *service_ptr;
struct sockaddr_in server_addr, client_addr;
int rcv_sck, rcv_len, resp;
int child_end;
//**** XDR stream anlegen
XDR xdrs;
//****Datebtypen für XDR
enum datatypes choice;
//**** Buffer zum empfangen von nachrichten anlegen
char buffer[BUFSIZE], recbuffer[BUFSIZE];

//**** Ãœbersetzungsdinger von XDR anlegen (ENCODE)
xdrmem_create(&xdrs, buffer, BUFSIZE, XDR_ENCODE);

//**** Ãœbersetzungsdinger von XDR anlegen (DECODE)
xdrmem_create(&xdrs, recbuffer, BUFSIZE, XDR_DECODE);

// Zuerst den ganzen Prozess auf 'nobody' Rechte beschraenken
seteuid(student);
setegid(student);

// Behandlung des CTRL-C Signals --> aufrufen der Funktion quit
signal(SIGINT, quit);

// Den entsprechenden Service bzw. Port der zu dem gewaehlten Protokol gehoert
// ermitteln und der entsprechenden Struktur uebergeben
if(service_ptr=getservbyname(service,protocol))
{
bzero((char *)&server_addr, sizeof server_addr);
// Request an jede der locaklen Netzwerkkarten inkl. loopback erlauben
server_addr.sin_addr.s_addr = INADDR_ANY;
// IP Familie v4
server_addr.sin_family = AF_INET;
// Der entsprechende Port
server_addr.sin_port = service_ptr->s_port;

// Socket erstellen
if ((sck=socket(PF_INET,SOCK_STREAM, IPPROTO_TCP))<0)
// Bei Fehler entsprechende Meldung ausgeben
perror("Cannot open socket");
// Socket an die vorher definierten Server Adressen binden
if (bind(sck,(struct sockaddr *)&server_addr, sizeof server_addr)<0)
// Bei Fehler entsprechende Meldung ausgeben
printf("Cannot bind socket %d to %s service\n",sck, service);
// Das lauschen auf dem Port/Socket generell gestatten bzw. freigeben
if (listen(sck,QSIZE)<0)
// Bei Fehler entsprechende Meldung ausgeben
perror("Cannot listen");

// Mutexe zuerst einmal initialisieren
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&controlmutex, NULL);

// Zur Hardware Ansteuerung root Rechte setzen
seteuid(0);
// Hardwareport an Adresse 'base' oeffnen
ioperm(base,3,1);
// Und alles auf null setzen
outb(0, base);
// Danach wieder zu nobody wechseln
seteuid(student);

// Hardware Steuerung starten
pthread_create (&th_hc, NULL, hardwarecontrol, NULL);
// Fuellstandsueberwachung starten
pthread_create (&th_fs, NULL, fuellstandskontrolle, NULL);


// concurrent server
while(1)
{
rcv_len=sizeof(struct sockaddr_in);

// Auf Connect warten, im blockierenden Zustand
if ((rcv_sck=accept(sck, (struct sockaddr *)&client_addr, &rcv_len))<0)
{
// Falls Fehler, Meldung ausgeben
perror("Error while connecting with client");
}
else
{
// Andernsfalls, bei Erfolg, communication Thread starten
// den Kommunikations Socket uebergeben
// und wieder auf weiteren Connect warten
pthread_create (&th, NULL, communication, &rcv_sck);
}
}

// Socket schliessn
close (sck);
}
// Bei Fehler entsprechende Meldung ausgeben
else perror("Service not found");
}

exit
06-04-2005, 13:10
Client:


// client.c

// Der client kontaktiert den Server auf den Port 20202/tcp
// dieser ist als kaffee deklariert

// dazu zusaezliche Zeilen in /etc/services einfgen:
// kaffee 20202/tcp # Kaffee-Server mit tcp
// kaffee 20202/udp # Kaffee-Server mit udp

// gcc -o client client.c


// bearbeitet durch:
// 381363
// Eingefügter Code ist mit "//****..." kommentiert


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
//****Bibliothek für XDR einbinden
#include <rpc/xdr.h>


// Puffergroesse fuer den Datenempfang vom Server
#define BUFSIZE 10000

// Variablen fuer die verbindung festlegen
//char *server="kaffeeserver";
char *server="localhost";
char *protocol="tcp";
char *service="kaffee";

//**** Buffer zum empfangen von nachrichten anlegen
char buffer[BUFSIZE], recbuffer[BUFSIZE];

//**** Datentypen, die mit XDR gesendet werden sollen
// davon brauche ich nur art, anzahl.
enum datatypes {
MY_ART, MY_ANZAHL
};



main()
{
// Variablen bzw. Strukturen die fuer die Verbindung zum Server benoetigt werden
struct hostent *host_ptr;
struct protoent *protocol_ptr;
struct servent *service_ptr;
struct sockaddr_in sck_addr;

char eingabe[3];
int sck,resp;
//****XDR-Stream und andere neue Variablen
XDR xdrs;
enum datatypes choice;
int sock,zeichen;


//***** neue Variablen für die bestellung****
int kaffeeart=0, anzahl=0;

//****Realisation der Eingabe im Format <Kaffeeart>:<Anzahl>

printf("\nWillkommen im Clinet des KaffeeServers! \n\n");
printf("Bitte Wählen Sie aus: \n");
printf("1: Kaffee mit Milch \n");
printf("2: Kaffee mit Zucker\n");
printf("3: Kaffee mit Milch und Zucker\n");
printf("Syntax: <KaffeeArt>:<Anzahl>\n\n");
printf("Wahl: ");

//****Wahl einlesen
scanf(eingabe);

//****Wahl umwandeln
kaffeeart = atoi(&eingabe[0]);
anzahl = atoi(&eingabe[2]);

fflush(stdout);

// Hostname aufloesen und der entsprechenden Struktur uebergeben
if (host_ptr=gethostbyname(server))
// Protokoll "aufloesen" bzw. ermitteln und der entsprechenden Struktur ubergeben
if (protocol_ptr=getprotobyname(protocol))
// Den entsprechenden Service bzw. Port der zu dem gewaehlten Protokol gehoert
// ermitteln und der entsprechenden Struktur uebergeben
if (service_ptr=getservbyname(service,protocol))
{
// Der Struktur sockaddr_in die entsprechenden Parameter zuweisen
memcpy( &sck_addr.sin_addr, host_ptr->h_addr, host_ptr->h_length);
sck_addr.sin_family=host_ptr->h_addrtype;
sck_addr.sin_port=service_ptr->s_port;

// Socket erstellen
if ((sck=socket(PF_INET, SOCK_STREAM, protocol_ptr->p_proto))<0)
// Bei Fehler entsprechende meldung ausgeben
perror("Cannot open socket");

//**** Ãœbersetzungsdinger von XDR anlegen (ENCODE)
xdrmem_create(&xdrs, buffer, BUFSIZE, XDR_ENCODE);

//**** Ãœbersetzungsdinger von XDR anlegen (DECODE)
xdrmem_create(&xdrs, recbuffer, BUFSIZE, XDR_DECODE);


// Verbindung ueber Socket und die vorher ermittelten Bedingungen (Protokol, etc.) zum Server aufbauen
if (connect(sck, (struct sockaddr *)&sck_addr, sizeof sck_addr)<0)
// Bei Fehler entsprechende meldung ausgeben
perror("Cannot establish connection");

//**** Art des bestellten Kaffees dem stream hinzufügen
choice = MY_ART;
xdr_enum(&xdrs, (enum_t *)&choice);
xdr_int(&xdrs, &kaffeeart);

//**** Abzahl der bestellten Kaffees dem stream hinzufügen
choice = MY_ANZAHL;
xdr_enum(&xdrs, (enum_t *)&choice);
xdr_int(&xdrs, &anzahl);

//**** Die Daten an den Server senden
/* Send the data */
write(sock, buffer, sizeof(buffer));


//**** Auf bestätigung des Servers warten

// Solange Daten vom Server ueber den Port bzw. Socket uebermittelt werden,
// diese in einen buffer schreiben und auf Bildschirm ausgeben
// Hierbei wird der Socket wie ein Filedescriptor behandelt
while ((resp=read(sck, recbuffer, BUFSIZE))>0)
write(1, recbuffer, resp);

// Socket schliesen
close(sck);
}
// Bei Fehler entsprechende meldung ausgeben
else perror("Service not found");
// Bei Fehler entsprechende meldung ausgeben
else perror("Protocol not found");
// Bei Fehler entsprechende meldung ausgeben
else perror("Host not found");
}


So, soweit so gut... nun soll der Client soll User die Möglichkeit bieten, das höhere Protokoll <Art des Kaffees>:<Anzahl der Becher> einzugeben.
Die Art der Kaffees ist auf drei beschränkt: 1, 2, 3 (alternativ: Kaffee_mit_Milch, Kaffee_mit_Zucker, Kaffee_mit_Milch_und_Zucker).
Die Datenübertragung soll XDR-kodiert erfolgen.

Und ich bekomme es einfach nicht gebacken...! Falls mir da wer helfen kann wäre ich wirklich sehr sehr dankbar...