[C] Serwer HTTP - dostarczenie pliku index.html

0

Witam,

Dostałem takie zadanie. Stworzyć serwer HTTP w języku C, który będzie dostarczał plik index.html w standardzie HTTP 0,9.. Czyli wyświetlał cały kod na ekranie.

Znalazłem kod serwera na jednej ze stron:

[code]
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main()
{
int sock, polaczenie, bajty_odebrane, true=1; //deklarujemy potrzebne rzeczy: sock - tutaj będziemy mieli informację o tym który port i adres ip są ze sobą powiązane
// polaczenie - tutaj będzie informacja o aktualnie otwartym połączeniu
// bajty_odebrane - zmienna pomocnicza przy odbieraniu danych
// true - zmienna używana przy ustawianiu opcji gniazda sock
char dane_wysylane[1024], dane_odbierane[1024]; //zmienne, w których będziemy przetrzymywać odebrane dane i dane do wysłania, dodatkowo ograniczamy ich długość do 1024

struct sockaddr_in adres_serwera,adres_klienta; //deklarujemy strukturę sockaddr_in, w której będziemy trzymać adresy serwera i klienta
int sin_size;

if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) //ustawiamy gniazdo sock na używanie protokołu IPv4 (AF_INET) oraz wybieramy typ gniazda (odpowiednio SOCK_STREAM i 0)
{ //jeśli funkcja sock zwróci błąd wyświetli się błąd i program się zamknie
perror("Socket");
exit(1);
}

if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int)) == -1) //ustawiamy opcję SO_REUSEADDR w gnieździe sock na wartość zmiennej true, czyli 1, podobnie jak wyżej
{ //jak wystąpi błąd pojawi się komunikat i program się zakończy
perror("Setsockopt"); //SO_REUSEADDR - Normalnie funkcja bind() zwraca błąd jeśli spróbujemy związać gniazdo z adresem,
exit(1); //który jest aktualnie w użyciu. Załóżmy, że gniazdo o adresie 127.0.0.1 i porcie 1111 jest
} //aktualnie w stanie TCP_FIN. Jeśli chcielibyśmy z tym adresem skojarzyć jakieś inne gniazdo
//nie czekając na zakończenie czasochłonnego procesu zamykania połączenia to musimy włączyć
//właśnie tą opcję.
//Ważna uwaga: nawet SO_REUSEADDR nie pomoże jeśli jakies gniazdo
//nasłuchuje (LISTEN) na danym adresie.

//ustawiamy adres serwera
adres_serwera.sin_family = AF_INET; //wybieramy protokuł IPv4
adres_serwera.sin_port = htons(5000); //ustawiamy port, w tym przypadku jest to port 5000
adres_serwera.sin_addr.s_addr = INADDR_ANY; //w srukturze serwera ustawiamy pozycję sin_addr.s_addr na INADDR_ANY, aby każdy mógł coś do nas napisać
bzero(&(adres_serwera.sin_zero),8);

if (bind(sock, (struct sockaddr *)&adres_serwera, sizeof(struct sockaddr)) == -1) //bindujemy (wiążemy) do naszego gniazda sock, adres naszego serwera, tak jak wcześniej gdy błąd -> komunikat i zkończenie
{
perror("Unable to bind");
exit(1);
}

if (listen(sock, 5) == -1) //sprawiamy, iż nasz serwer zacznie nasłuchiwać na gnieździe sock, to jest na porcie 5000 o adresie IP serwera, dodatkowo ustawiliśmy długość kolejki połączeń na 5
{
perror("Listen");
exit(1);
}

printf("\nSerwer TCP oczekuje na klienta na porcie 5000"); //komunikat w oczekiwaniu na podłączenie się jakiś klientów
fflush(stdout); //zapisujemy dane bufora stdout (to co wyświetla się na ekranie), przez co go opróżniamy, żeby bez problemu zacząć pracę z klientem

while (1)
{
sin_size = sizeof(struct sockaddr_in);

  polaczenie = accept(sock, (struct sockaddr *)&adres_klienta,&sin_size);   //funkcja accept() blokuje dalcze działanie programu, dopóki nei podłączy się jakiś klient, wtedy następuje
                                                           //wpisanie danych klienta do struktury wskazywanej przez adres_klienta. sin_size, to wielkość struktury sockaddr_in
                                                           //ponadto funckja accept() zwróci do zmiennej polaczenie dyskryptor gniazda, którego będziemy używać do komunikacji z tym klientem
  
  printf("\nPołączenie z (%s, %d)",inet_ntoa(adres_klienta.sin_addr),ntohs(adres_klienta.sin_port));   //komunikat o udanym połączeniu z klientem o adresie IP i na jakim porcie
  
  while (1)
  {
     printf("\n WYŚLIJ (q albo Q aby zakończyć): ");      //linia zachęty, do wprowadzenia danych do wysłania, q albo Q zakańcza program
     gets(dane_wysylane);   //przechwytujemy sąg znaku wpisywany z klawiatury, do zmiennej dane_wysylane. wprowadzanie danych kończy się enterem
     
     if (strcmp(dane_wysylane, "q") == 0 || strcmp(dane_wysylane, "Q") == 0)      //pętla warunkowa kończąca program jeśli wprowadziliśmy z klawiatury q lub Q
     {
        send(polaczenie, dane_wysylane, strlen(dane_wysylane), 0);   //funkcja send wysyłająca do klienta informację o tym iż zakończyliśmy działanie programu
        close(polaczenie);      //zamknięcie połączenia z klientem
        break;            //wyjście z pętli while
     }
     
     else
     {
        send(polaczenie, dane_wysylane, strlen(dane_wysylane), 0);   //wysyłanie komunikatu, jeśli nie był on q lub Q, o długości strlen(dane_wysylane)
     }
     
     bajty_odebrane = recv(polaczenie, dane_odbierane,1024,0);   //odebranie danych od klienta i wpisywanie ich do zmiennej pomocniczej bajty_odebrane
     
     dane_odbierane[bajty_odebrane] = '\0';   //jako, że nie wiemy jak długi komunikat odebraliśmy, skracamy cały string bajty_odebrane, który ma długość 1024 do momentu kiedy znakiem będzie \0,
                                   ///ponieważ ten znak na końcu dodaje funkcja gets, której używamy
     
     if (strcmp(dane_odbierane, "q") == 0 || strcmp(dane_odbierane, "Q") == 0)   //sprawdzamy, czy przypadkiem klient nie zakończył swojego działania
     {
        close(polaczenie);   //zamykamy połączenie jeśli klient zakończył swoje działanie
        break;   //wychodzimy z pętli while
     }
     
     else
     {
        printf("\n ODEBRANO = %s", dane_odbierane);   //wyświetlamy na wyjściu otrzymany komunikat jeśli, jeśli klient nie zakończył swojego działania
     }
     
     fflush(stdout);   //zapisujemy bufory wyjścia i je tym samym opróżniamy
  }

}

close(sock); //zamykamy gniazdo sock
return 0; //kończymy działanie programu
}
[/code]

No ale nie ma gdzieś tutoriala jak własnoręcznie napisać kod sewera?

I jak zaimplementować tam opcję tego wyświetlania klientowi pliku index.html..

Może ktoś ma jakiś najprostszy kod serwera bo ten to chyba długaśny strasznie.. Nie mam całkowicie pojęcia jak się za to zabrać.. Proszę o jakieś wskazówki. I nie to że nie szukałem. Szukam cały czas. Próbuje ale bezskutecznie :(

0

Masz komentarze do wszystkiego, to co pozostało zrobić to przerobić główną pętlę, aby obsługiwała (banalny) protokół HTTP/0.9 zamiast tego co jest obecnie.
Jak wyświetlać plik index.html? Wczytać go z pliku i przesłać do klienta socketem, gdy o niego poprosi.

0

Witam.. Po ciężkich bojach.. a także cisnących terminach na uczelni :) wiadomo jak to jest :P

Zacząłem czytać poradnik Beej’s Guide to Network Programming o używaniu gniazd internetowych i stworzyłem prosty sewer HTTP. Udało mi się nawet rozwiązać sprawę wczytywania pliku index.html :) Najpierw wczytuje go do tablicy a potem wysyłam do clienta :) Moim clientem może być np przeglądarka firefox. Wystarczy w jej polu gdzie się wpisuje adres wpisać IP ustawione w serwerze.

tak więc tutaj jest kod serwera:

[code]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define MYPORT 2000 // port z ktorym beda sie laczyc clienci..
#define BACKLOG 10 // ilosc polaczen oczekujacych w kolejce

void sigchld_handler(int s)
{
while(wait(NULL) > 0);
}

int main(int argc, char *argv[])
{
FILE *plik;

int sockfd, new_fd; 	// nasluchiwanie na sockfd na nowe polaczenie

struct sockaddr_in my_addr; 	// moj adres
struct sockaddr_in their_addr; 	// adres clienta
struct sigaction sa;

int sin_size;
int yes=1, i;
char dane[999999];		//tablica znaków przechowuje plik index html

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}

my_addr.sin_family = AF_INET; // rodzaj gniazda z ktorego kozysta TCP/IP
my_addr.sin_port = htons(MYPORT); // numer portu
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // moje IP
memset(&(my_addr.sin_zero), '\0', 8); // zerowanie reszty struktury

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
exit(1);
}

if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}

sa.sa_handler = sigchld_handler; // zbieranie martwych procesow
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;

if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}

plik = fopen("index.html","r");

while(feof(plik)==0) // petla wpisujaca plik index do tablicy
{
fscanf(plik, "%c", &dane[i]);
i++;

}

fclose(plik); // zamkniecie pliku

while(1) // głowna petla
{
sin_size = sizeof(struct sockaddr_in);

if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) 
	{
		perror("accept");
		continue;
	}

printf("server: polaczono z %s\n", inet_ntoa(their_addr.sin_addr));

if (!fork())
{ // to jest proces-dziecko
close(sockfd); // dziecko nie potrzebuje gniazda nasłuchujacego

if (send(new_fd, dane, strlen(dane), 0 == -1))
	perror("send");

close(new_fd);
exit(0);

}

close(new_fd); // rodzic nie potrzebuje tego
}
return 0;
}
[/code]

żeby się połączyć z serwerem trzeba wpisać w przeglądarkę
[code]127.0.0.1:2000[/code]
bo na takie IP i port jest serwer.

Przykładowa strona index.html której serwer szuka w katalogu:

[code]<HTML>

<head> <title>Przykadowa strona</title> <meta http-equiv="content-type" content="text/html; charset=Windows-1250"> <meta name="Author" content="Mateusz Stokowiec"> </head> <body bgcolor="#888888">

<cen­ter>

<marquee>

<font color=“TUTAJ WPISUJEMY KOLOR TEKSTU, KTÓRY MA SIĘ PRZESUWAĆ”>--->>> Serwer Online <<--- </span>

</marquee></p>
Witam. Udało się otworzyć stronę index.html. Serwer działa jak należy..

Pozdrawiam Mateusz Stokowiec

</body> <html>[/code]

Czy ktoś mógłby mi sprawdzić poprawność kodu. I powiedzieć czy to jest kod serwera w standardzie HTTP/0.9? I co należałoby w nim zmienić żeby był on w tym standardzie. Niestety nie znam się za dobrze na programowaniu, a to jest taki projekt który muszę stworzyć :) Jak w ogóle sprawdzić jaki to standard?

1 użytkowników online, w tym zalogowanych: 0, gości: 1