Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C] Serwer HTTP - dostarczenie pliku index.html

Ostatnio zmodyfikowano 2011-01-26 03:43
Autor Wiadomość
StoQ
Temat założony przez niniejszego użytkownika
[C] Serwer HTTP - dostarczenie pliku index.html
» 2010-12-21 19:10:41
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:

C/C++
#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
}

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 :(
P-25387
SeaMonster131
» 2010-12-21 19:44:08
Jeżeli wywalisz wszystkie komentarze to kod w sumie będzie krótki hehe :)
P-25388
DejaVu
» 2010-12-21 19:56:18
True, kod jest krótki. W dodatku skoro są komentarze to znaczy, że wystarczy je przeczytać żeby zrozumieć mniej więcej o co chodzi.

/edit:
Wywołanie z przeglądarki: http://localhost:5000/ - myślę że zadziała. Serwer wystarczy uruchomić.
P-25389
StoQ
Temat założony przez niniejszego użytkownika
» 2011-01-26 02:58:05
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:

C/C++
#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;
}

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

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

<HTML>



<HEAD>



<TITLE>Przykadowa strona</TITLE>



<META http-equiv="content-type" content="text/html; charset=Windows-1250">

<META NAME="Author" CONTENT="">

</HEAD>



<BODY BGCOLOR=#888888>



<cen­ter>



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



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



Pozdrawiam M



</BODY>



<HTML>

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?
P-27090
DejaVu
» 2011-01-26 03:43:26
P-27091
« 1 »
  Strona 1 z 1