Задача: Реализация Модельного веб-сервер

BashOrgRu

Well-known member
15.03.2009
77
0
#1
Дали задание, написать модельный веб-сервер без CGI: "Сайты, в которых информация (ресурсы) представляет собой набор файлов (обычно – в формате HTML – т.н. веб-страницы), называются статическими. ". То есть нужно сделать реализация модельного веб-сервера, поддерживающуго описанное ниже подмножество протокола HTTP.
HTTP-запрос
HTTP-запрос отправляется веб-клиентом к веб-серверу и имеет вид:
<заголовок запроса> <конец строки>
<HTTP- заголовок > <конец строки>

<HTTP- заголовок > <конец строки>
< конец строки >
<тело запроса>

Здесь и далее <конец строки> - это символ с кодом 10 (\n).
Структура заголовка запроса имеет вид:
< HTTP -метод> URI HTTP/<версия протокола>

Метод GET
Этот метод используется для запроса содержимого ресурса с
сервера. Файл ресурса определяется URI из запроса. Если URI корректен,
то сервер обязан вернуть содержимое запрашиваемого файла, если это
файл данных.
Примеры заголовков запроса GET:
GET /index.html HTTP/1.1
По этому запросу сервер возвращает текст файла index.html из
домашней директории сервера.

Метод HEAD

HTTP-ответ
Ответ веб-сервера имеет следующую структуру:
<заголовок ответа> <конец строки>
<HTTP- заголовок > <конец строки>

<HTTP- заголовок > <конец строки>
< конец строки >
<тело ответа>

Заголовок ответа выглядит так:
HTTP/<версия протокола> <код состояния> <пояснение>


Заголовок Date

Заголовок Host

Заголовок Referer

Заголовок User-agent

Заголовок Server

Заголовок Content-length

Заголовок Content-type

Заголовок Allow

Заголовок Last-modified

"Рекомендуется начать реализацию с двух простых программ, которые пригодятся при тестировании. Первая программа – «псевдо-сервер», цель которого – запись реальных запросов, посылаемых веб-клиентами например, различными веб-браузерами). Такой сервер должен принять запрос, записать его в лог-файл, выдать ответ с кодом 501 «Not Implemented» и немедленно закрыть соединение. Ответ можно заготовить заранее как текстовый файл и выдавать его в сокет по мере надобности. Сохраненные запросы можно использовать для отладки сервера. Для того, чтобы посылать эти запросы к серверу, понадобится еще одно простое приложение – «псевдо-браузер». Это консольное приложение, которое устанавливает связь с сервером, посылает ему заранее заготовленный запрос (тут-то пригодится «псевдо-сервер», хотя тестовые запросы можно приготовить и «вручную») и записывает ответ сервера."


Прошу помочь хоть какой-нибудь информацией по поводу этого или примеров программ. От нас требуют, чтобы мы уже сдавали этот модельный веб-сервер, хотя ни на лекциях, ни на семинарах мы ещё не дошли до сокетов.
На всякий случай, если я непонятно описал задачу, то присоединяю файл, в котором описано подробно задание, и из этого задания мне надо сделать первый этап.
Заранее блогадарен.
 

Вложения

lazybiz

Well-known member
03.11.2010
1 339
0
#2
Прошу помочь хоть какой-нибудь информацией по поводу этого или примеров программ.
C++:
// обычная подготовка TCP/IP сервера
int serverSocket = socket (AF_INET, SOCK_STREAM, 0);
...
struct sockaddr_in ServerAddress;
// заполнить ServerAddress
// ...
if (bind(serverSocket, &ServerAddress, sizeof(ServerAddress)) < 0)
... // фатальная ошибка
if (listen(serverSocket, BACK_LOG) < 0)
... // фатальная ошибка

// главный цикл
for (;;) {
struct sockaddr_in ClientAddress;
size_t ClAddrLen = sizeof(ClientAddress);

// ждем очередного клиента
int clSocket = accept((serverSocket, &ClientAddress, &ClAddrLen);
if (clSocket < 0) ... // ошибка - если будет повторяться, то фатальная
// собственно обработка запроса. Должна включать в себя корректный разрыв связи (shutdown - close)
ProcessClientRequest(clSocket, &ClientAddress);
}
Ты не поверишь где я это нашел..
 

BashOrgRu

Well-known member
15.03.2009
77
0
#3
ну это да) Мне примеры того что должно быть на входе и на выходе у сервера, то есть ему как я понимаю подают на ввод команду либо GET либо HEAD, а в ответ что-то получают. Так вот нужен пример того что на ввод и что на вывод должен делать сервер, а не абстрактное объяснения, как в том файле. И ещё, как я понял, сервер должен работать со статическими веб-сайтами, то есть в папке с сервером должны лежать файлы типа html и он по запросам клиента должен их отправлять? Или как?
 

DarkKnight

Well-known member
01.08.2010
653
0
#4
А уже есть свои наработки какие-нибудь???
И вообще вам сам принцип работы веб-сервера понятен???
Может быть когда-нибудь сталкивались с рабочим каким-нибудь (например с Apachем) ?
P.S. Это что бы точно знать с чего начать вам помогать :)
 

BashOrgRu

Well-known member
15.03.2009
77
0
#5
Наработок нет, так как не понятно, что нужно делать. Почитав разные мануалы, только примерно понимаю приницип работы веб-сервера. Не, не сталкивался)

Добавлено: Как бы работу с сокетами частично понимаю, но вот что должен получить и отправлять не могу никак понять)
 

DarkKnight

Well-known member
01.08.2010
653
0
#6
Щас у меня пол первого утра, так что сил что то писать нету :))) Я завтра тебе помогу... И досканально попробую объяснить.....
Вообщем за выходные что-нить общими усилями родим ;-)
 

BashOrgRu

Well-known member
15.03.2009
77
0
#7
Спасибо огромное! Мне вприницпе просто надо досконально объянсить что должен делать сервер) Первым заданием семестра - написанием шела для Unix, справился легко, так как знал, что там вобще от меня требуются, а тут вобще тьма...
 

DarkKnight

Well-known member
01.08.2010
653
0
#8
Вот... Для начала примерно так это должно выглядить....

C++:
#include <iostream> 
#include <WinSock.h> //Заголовочный файл сокетов

#pragma comment(lib,"wsock32.lib") //Подключим библиотеку

using namespace std;

char buffer[] = "HTTP/1.1 200 Ok\nContent-Type: text/html\n\n<HTML><HEAD><TITLE>ModalServer 1.1b</TITLE></HEAD>\n<BODY bgcolor='green' text='yellow'>\n<H1>Я пока еще не умею обрабатывать запросы</H1>\n</HTML>";

int main (void)
{

setlocale(LC_ALL,"Russian"); //Установим локаль
WSADATA wsaData; //WSA заглушка
WORD WINSOCK_VERSION = MAKEWORD(2,2); //Версия сокетов

cout<< "....Загрузка WebServerа...."<< endl;
Sleep(1000); //Сделаем паузу так будет красивее

//Инициализация WSA
if (!WSAStartup(WINSOCK_VERSION,&wsaData))
cout<< "Инициализация WSA выполнена успешно"<< endl;
else{
cout<< "Error: Ошибка инициализации WSA"<< endl;
return -1;
}

int ServerSocket = socket (AF_INET, SOCK_STREAM, 0); //Наш сокет
struct sockaddr_in ServerAddress; //Структура sockaddr_in
ServerAddress.sin_addr.s_addr = INADDR_ANY; 
ServerAddress.sin_port = htons(8888); //Будем отслеживать порт 8888
ServerAddress.sin_family = AF_INET;

if (bind(ServerSocket,(LPSOCKADDR)&ServerAddress, sizeof(ServerAddress))== SOCKET_ERROR)
{
cout<< "Не могу забиндить \n";
} else cout<< "Bind is OK"<< endl;

if (listen(ServerSocket,3) != SOCKET_ERROR)
{
cout<< "Прослушивание выполнено " << endl;
}
struct sockaddr_in clAddr;

int clAddrLen = sizeof(clAddr);
while (1)
{
int clSocet = accept(ServerSocket,(LPSOCKADDR)&clAddr,&clAddrLen);
char *Rv = new char[1024];
memset(Rv,0,1024);
recv(clSocet,Rv,1024,0);
cout<< "Запрос к серверу :"<< endl;
cout<< Rv<< endl;
cout<< "------------------------------------------------------------"<< endl;
send(clSocet,buffer,sizeof(buffer),0);
cout<< "Ответ послан"<< endl;

shutdown(clSocet,0);
closesocket(clSocet);

}
//Завершим WSA
WSACleanup();


}
 

Вложения

BashOrgRu

Well-known member
15.03.2009
77
0
#9
DarkKnight125, спасибо, теперь уже более менее ясно. Только тот код, что ты написал, сделан на C++(который пока не знаю), а мне надо на С сделать, так что какие команды из твоего кода не доступны в C?)
 

BashOrgRu

Well-known member
15.03.2009
77
0
#11
GCC в Unix'е
я так подозреваю, что кроме команд типа " cout<< "Запрос к серверу :"<< endl;" все остальные команды есть в С?
 

DarkKnight

Well-known member
01.08.2010
653
0
#12
C++:
using namespace std;
еще ;-) Все остальное есть...
Я допишу когда напишу как для *nixов переложить код, там несколько разногласий с виндой, небольших, таких например как
определение заголовочных файлов:
C++:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
WSADATA wsaData; //WSA заглушка
WORD WINSOCK_VERSION = MAKEWORD(2,2); //Версия сокетов

cout<< "....Загрузка WebServerа...."<< endl;
Sleep(1000); //Сделаем паузу так будет красивее

//Инициализация WSA
if (!WSAStartup(WINSOCK_VERSION,&wsaData))
cout<< "Инициализация WSA выполнена успешно"<< endl;
else{
cout<< "Error: Ошибка инициализации WSA"<< endl;
return -1;
}
Вот этой "срани" нету, она там просто не нужна ;-))
C++:
closesocket(clSocet); 
//Он будет вызываться просто
close(clSocet)
Ну вроде и все....

А пока cin и cout смело заменяй на
printf и scanf
 

BashOrgRu

Well-known member
15.03.2009
77
0
#13
:) спасибо)
А вот в ответ сервер должен на запрос, например, "GET /topic39269.html"(что-то типо такого) посылать код странички? Или например на метод head. Вобщем, как должен выглядить наш ответ? Можно не готовый код, а просто хотя бы пример нескольких команд в сервер и от сервера, естественно, без CGI.
 

BashOrgRu

Well-known member
15.03.2009
77
0
#14
А ip: 192.168.1.2 , по которому находится сервер, где указывал в коде?
 

DarkKnight

Well-known member
01.08.2010
653
0
#15
А ip: 192.168.1.2 , по которому находится сервер, где указывал в коде?
Это мой локальный Ip ;-)) Можно заместо него узать 127.0.0.* любой....
Его не надо указывать, отлавливаются все сокеты TCP/IP которые прилетают на 8888 порт ;-)))

З.Ы. С ответом я завтра допишу... И асинхронную обработку тоже завтра сделаю.... :) Яж написал это только начало, но в тоже время это и основная часть ;-)

Ну а если побаловаться хочешь, то делай так... Получаешь имя файла(вытаскиваешь его с запроса) GET /index.html...
Вытащил имя /index.html
Открываешь его для чтения, читаешь, и отправляешь шапку(которую я написал в буфер, кроме текста)+ весь файл...
И вот у тебя уже в браузере требуемая страница
 

BashOrgRu

Well-known member
15.03.2009
77
0
#16
Вот слегка доделал, теперь файлы выдаёт. Но когда надо запрашывает файл расширения png или gif, то выходит ошибка чтения, как я понимаю, это fgetc, надо заменить на fread, но fread почему-то не пашет)
Теперь вопросы:
text/plain - в какой случае Content-Type такой должен быть?
Есть ли какой-то способ на C посылать серверу ответы типа "HTTP/1.(какое-то число) (какое-то число) (какое-то слово)\nContent-Type: (какое-то слово)"?
Где можно метод HEAD отловить из запроса браузера?))

Код:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

struct word{
char *cont;
struct word *next;
};

char buffer[] = "HTTP/1.1 200 Ok\nContent-Type: text/html";

struct word *CreatList(char *str, int Count){
int i=0,is=-1,j,mark=0,k,p;
struct word *head,*Elem;

head=(struct word *)malloc(sizeof(struct word));
Elem=head;
while(i!=(Count+1)){
if ( str[i]==' ' || str[i]=='\n' || i==Count ){
Elem->cont=malloc((i-is)*sizeof(char));
k=0;
for (j=(is+1);j<i;j++){
*((Elem->cont)+k)=str[j];
k++;
}
*((Elem->cont)+k)='\0';
if (i!=Count)
Elem->next=(struct word *)malloc(sizeof(struct word));
else
Elem->next=NULL;
Elem=Elem->next;
is=i;
}
i++;
}
return (head);
}

char *OpRF(char *name){
char c;
FILE *fp;
int i=0;
for (i=0;i<strlen(name);i++) name[i]=name[i+1];
if (name[0]=='\0') name="index.htm\0";
if ((fp=fopen(name,"r"))==NULL){
printf("Ошибка чтения\n");
return NULL;
}
fseek(fp,0,SEEK_END);
int l;
l=ftell(fp);
char *str=malloc((l+1)*sizeof(char));
i=0;
close(fp);
fp=fopen(name,"r");
while((c=fgetc(fp))!=EOF){
str[i]=c;
i++;
}
str[i]='\0';
close(fp);
return str;
}

int main (void)
{
struct word *Sp;
char *str;
int i=0,ServerSocket = socket (AF_INET, SOCK_STREAM, 0); 
struct sockaddr_in ServerAddress;

ServerAddress.sin_addr.s_addr = INADDR_ANY;
ServerAddress.sin_port = htons(8888);
ServerAddress.sin_family = AF_INET;

if (bind(ServerSocket,(struct sockaddr *)&ServerAddress, sizeof(ServerAddress))<0)
{
printf( "Не могу забиндить \n");
return 0;
} else printf( "Bind is OK\n");

if (listen(ServerSocket,3) >=0)
printf("Прослушивание выполнено \n");
else return 0;
struct sockaddr_in clAddr;

int clAddrLen = sizeof(clAddr);
while (1)
{
int clSocet = accept(ServerSocket,(struct sockaddr *)&clAddr,&clAddrLen);
char Rv[1024];
memset(Rv,0,1024);
recv(clSocet,Rv,1024,0);
printf( "Запрос к серверу :\n");
printf("%s",Rv);
Sp=CreatList(Rv,strlen(Rv));
str=OpRF((Sp->next)->cont);
printf( "------------------------------------------------------------\n");
send(clSocet,buffer,sizeof(buffer),0);
if (str!=NULL)
send(clSocet,str,strlen(str),0);
printf("Ответ послан\n");
shutdown(clSocet,0);
close(clSocet);
}
return 0;
}
 

DarkKnight

Well-known member
01.08.2010
653
0
#17
Код:
HTTP/1.1 200 Ok
MIME-version: 1.1
Content-Type: image/gif (Ну смотря какой формат)
Content-Length: [в этом месте размер катинки в байта]
\n
[бинарный вывод файла]
Код:
Если картинка не найден то просто высылай
HTTP/1.1 404 File Not Found
]\n
Еще раз повторюсь что файл должен быть открыт для бинарного чтения
fp=fopen(name,"r+b");
 

BashOrgRu

Well-known member
15.03.2009
77
0
#18
Поле Last-modified, со статичиским ресурсом должно содержать время первого запроса к этому ресурсу?
 

DarkKnight

Well-known member
01.08.2010
653
0
#19
Неа... Время изменения запрашиваемого файла...
Смысл же в том, что бы из кэша подтянуть если он не изменился....
 

BashOrgRu

Well-known member
15.03.2009
77
0
#20
то есть надо stat юзать? Вприницпе, уже формирование ответа закончил))
А как сделать асинхронный поток сокетов?