Сервер как его составить

Время на прочтение
2 мин

Количество просмотров 43K

Народ, это моя первая статья, так что задолбите меня критикой, дабы повысить качество следующих статей. :)

Вступление

Ну, начнем как и везде с определений, берите тетрадь и ручку сейчас начнется нудятина. Чтобы мы cмогли написать свой сервер, нужно для начала понимать как он вообще работает, ловите определение:

Сервер – это программное обеспечение, которое ожидает запросов клиентов и обслуживает или обрабатывает их соответственно.

Если объяснять это своими словами, представьте фургон с хот-догами(сервер), проголодавшись, вы(клиент) подходите и говорите повару, что вы хотите заказать(запрос), после чего повар обрабатывает, что вы ему сказали и начинает готовить, в конечном итоге вы получаете свой хот-дог(результат) и сытый радуетесь жизни. Для наглядности посмотри схему.


Околопрактика

Для написания сервера мы будем использовать Python и модуль Socket.

Socket позволяет нам общаться с сервером с помощью сокетов. Весь код я постараюсь пояснять, дабы ты мой дорогой читатель все понял. В конце статьи будет готовый код.

Создайте два файла в одной директории:

  1. socket_server.py

  2. socket_client.py


Практика

Пишем код для серверной части, так что открывайте файл socket_server.py.

Начнем с импорта модуля и создания TCP-сокета:

import socket

Далее весь код будет с комментариями:

s.bind(('localhost', 3030)) # Привязываем серверный сокет к localhost и 3030 порту.
s.listen(1) # Начинаем прослушивать входящие соединения
conn, addr = s.accept() # Метод который принимает входящее соединение.

Добавим вечный цикл, который будет считывать данные с клиентской части, и отправлять их обратно.

while True: # Создаем вечный цикл.
	data = conn.recv(1024) # Получаем данные из сокета.
	if not data:
		break
	conn.sendall(data) # Отправляем данные в сокет.
	print(data.decode('utf-8')) # Выводим информацию на печать.
conn.close()

Переходим к клиентской части, весь код теперь пишем в файле socket_client.py.

Начало у клиентской части такое-же как и у серверной.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Далее подключимся к нашему серверу и отправим сообщение «Hello. Habr!».

s.connect(('localhost', 3030)) # Подключаемся к нашему серверу.
s.sendall('Hello, Habr!'.encode('utf-8')) # Отправляем фразу.
data = s.recv(1024) #Получаем данные из сокета.
s.close()

Результат:

Слева сервер, справа клиент

Слева сервер, справа клиент

Заключение

Вот мы с вами и написали свой первый сервер, рад был стараться для вас, ниже будет готовый код.

socket_server.py:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(('localhost', 3030)) # Привязываем серверный сокет к localhost и 3030 порту.
s.listen(1) # Начинаем прослушивать входящие соединения.
conn, addr = s.accept() # Метод который принимает входящее соединение.

while True:
	data = conn.recv(1024) # Получаем данные из сокета.
	if not data:
		break
	conn.sendall(data) # Отправляем данные в сокет.
	print(data.decode('utf-8'))
conn.close()

socket_client.py:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect(('localhost', 3030)) # Подключаемся к нашему серверу.
s.sendall('Hello, Habr!'.encode('utf-8')) # Отправляем фразу.
data = s.recv(1024) #Получаем данные из сокета.
s.close()

В наше время не все люди, которые связаны с хостингом/серверами и компьютерами хотят платить средства за аренду. Действительно, зачем платить деньги за аренду когда можно собрать все свое? Хоть данная претензия звучит достаточно логично все же мы с вами разберем почему этого делать не стоит. А для тех кто все же решится на постройку своего сервера мы предоставляем полезную информацию.

В данной статье мы с вами рассмотрим следующие вопросы: Как собрать свой сервер? Какие комплектующие понадобятся? На что стоит обратить внимание? Почему хостинг лучше домашнего сервера?

Как собрать свой сервер?

Для тех кто готов потратить свои нервы и достаточно крупную сумму денег ниже предоставляется гайд. В данном гайде вы сможете найти ответы на все вышеперечисленные вопросы. Начнем с самого главного а именно

На что стоит обратить внимание при сборке сервера?

Домашний сервер является дорогим удовольствием, которое требует постоянного контроля, а также особенного помещения. В помещение где вы желаете расположить сервер должна быть отличная вентиляция т.к. он будет сильно греться.

Также необходимо обладать бесперебойным питанием, а в этом вам поможет Источник бесперебойного питания.

В случае если ваш сервер вы собираетесь расположить в квартире вам потребуется шумоподавление в комнате где он будет расположен, т.к. из-за кулеров охлаждения он будет шуметь достаточно сильно.

Обязательно удостоверьтесь что не будет проблем описанных выше иначе у вас могут возникнуть проблемы с соседями. Из личного опыта могу сказать что из-за шума от кулеров соседи могут прийти к вам с просьбой отключить сие оборудование.

Какие комплектующие нужны для сборки сервера?

Для сборки сервера вам потребуются те же комплектующие что и для сборки ПК, но есть явные отличия в их стоимости.

  • Материнская плата
    Материнская плата должна соответствовать вашим требованиям. Например, если вашему проекту необходима дополнительная мощность то стоит купить материнскую плату с поддержкой двух процессоров. Все зависит от ваших потребностей и того что вы собираетесь разместить на вашем сервере.

Примером материнской платы могу указать следующие:
MZ71-CE0 (rev. 3.x)
MZ72-HB2 (rev. 3.0)
Asus P11C-M/4L

  • Процессор
    Так как это сервер то мы берем процессоры с минимум 8 ядрами. Данные виды процессоров можно считать самыми доступными и удобными для работы. Опять же вы можете купить два процессора для своего сервера. Однако если у вас есть необходимость в двух процессорах то лучше устанавливайте процессоры с количеством ядер более 32.

Примеры:
Intel Xeon E-2174G
Intel Xeon W-1390
AMD EPYC 7443P

  • Оперативная память
    ОЗУ важный элемент в работе сервера и компьютера. Как правило чем больше ОЗУ тем быстрее будет обрабатываться информация. Поэтому следует устанавливать на сервер минимум 16 ГБ оперативной памяти. И опять же чем более требовательный ваш проект тем больше памяти понадобится. Есть некоторые ограничения, которые могут быть наложены самой ОС которую вы используете.

Примеры:
Samsung RDIMM 32GB 2Rx4 DDR4
Black Diamond Memory 16GB
Micron 32GB DDR4 3200

  • Видеокарта (Опционально)
    Для некоторого рода серверов обязательным атрибутом является видеокарта. С её помощью можно производить обработку графической информации, например, обрабатывать видео. Если вдруг у вас игровой сервер то вам потребуется данный атрибут для быстродействия и более плавной обработки информации.

Примеры:
AMD Radeon Pro WX 9100
NVIDIA RTX A4000
PNY Quadro RTX 4000

  • HDD SSD
    Вы не сможете разместить свой проект на сервере если там не будет памяти. Для того чтобы ваш проект смог выдать на полную мощность свои показатели вам потребуется большой объем. Представим что у вас интернет магазин. Для того чтобы вы смогли сохранить всю базу клиентов вам понадобится минимум 1 ТБ HDD, а лучше всего если у вас в связке будет SSD. SSD будет отвечать за скорость обработки информации, а в это время HDD будет хранилищем всей информации.

Например
Seagate Exos 16TB Enterprise
Toshiba 16TB Enterprise

  • Охлаждение
    Есть два типа охлаждения вам потребуется установка кулеров для охлаждения процессора и сервера. Этот вид охлаждения очень шумный, но действует отлично.
  • Питание и ИБП
    Для работы сервера на постоянной основе вам потребуется постоянный источник питания. Как правило в домах свет не отключают неожиданно, но лучше перестраховаться и купить себе источник бесперебойного питания. Именно ИБП позволит вам сохранить данные в случае отключения света, а также не беспокоиться о перепаде напряжения.
  • Корпус
    Естественно для сервера потребуется корпус, который позволит вместить туда все необходимое. Данный корпус должен обладать хорошим притоком воздуха, а также быть небольшим чтобы было легко разместить его дома.

В данный список не были включены кабели и прочее т.к. это разумеется что они должны быть в наличии.

Почему хостинг лучше домашнего сервера?

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

  • Дороговизна комплектующих
    Каждая деталь в сервере, как правило, стоит колоссальных средств. Может быть вам и не нужен мощный сервер и все на что вы потратитесь (По максимуму) будет 8 ГБ оперативной памяти, процессор с 4 ядрами и 100 ГБ SSD. В любом случае в наше время это у вас требует минимум 200 долларов.
  • Энергия
    Сервер в отличие от компьютера работает круглосуточно. Из-за этого вы должны быть готовы к тому что придется платить кругленькую сумму за электроэнергию. Также электричество должно поступать бесперебойно иначе ваш сервер может работать нестабильно.
  • Шум
    Так как сервер это мощная штука то ему потребуется отличное охлаждение. Вам придется потратиться на топовое охлаждение чтобы не потерять эффективность работы сервера. Именно поэтому будьте готовы к тому что у вас больше не будет тихо в доме.
  • Внештатные ситуации
    Хоть комплектующие у серверов как правило очень долго могут работать, но и у них есть предел. Таким образом ваш сервер может выйти из строя в любой момент, например, из-за перепада напряжения.

Сверху я перечислил лишь 4 пункта, но их достаточно для того чтобы понять что домашний сервер это очень дорогое удовольствие. Именно поэтому многие люди пользуются услугами хостинг провайдеров. Например, у нас вы можете дешево арендовать VPS сервер и настроить с каким кол-вом ГБ, Ядер и объемом памяти он должен быть. Со всеми предложениями вы можете ознакомиться здесь

Вывод

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

Чтобы вы могли спать спокойно пока ваш сервер работает без перебоев рекомендую вам ознакомиться с нашими ценами.

Содержание:

  • Конфигурирование устройства
  • Какие задачи решает сервер
  • Сборка домашнего сервера
  • Реализация
  • Оборудование
  • Порядок монтажа сервера
  • Материнская плата

  • Установка процессора

  • Охлаждение

  • Оперативная память

  • Жесткие диски

  • Видеокарты

  • Контроллер

  • Укладка проводов в корпусе

  • Коммутаторы

  • Резервное питание

  • Первый запуск и установка ПО 
  • Тестирование сервера

Довольно часто компании сталкиваются с тем, что ИТ-мощностей для выполнения серьезных задач не хватает, необходимо расширение. В каталоге серверов можно выбрать подходящее оборудование. Менее затратным вариантом станет самостоятельная сборка сервера, но лучше обратиться к специалистам. Такой подход позволит развернуть уникальную систему под конкретные бизнес-процессы. Если нет возможности проконсультироваться со специалистами, вот несколько советов по сборке сервера.

Конфигурирование устройства

Перед тем как выбрать сервер, нужно выяснить численность сотрудников и масштаб организации. Чтобы устройство впоследствии работало бесперебойно, важно его правильно настроить. Конфигурирование программного обеспечения повышает безопасность и производительность оборудования. Но нужно ответственно подойти к этому процессу. Если опыта недостаточно, лучше обратиться к IT-инженерам ittelo.ru, которые отлично разбираются в этом деле. Они гарантируют безопасность и стабильность работы оборудования.

Какие задачи решает сервер

Аппаратные машины имеют огромный спектр применения. Они выполняют разные операции:

  1. Работают с базой данных. Для компоновки устройства используют высокопроизводительные комплектующие.
  2. Хранят файлы. При сборке такого сервера учитывают объем дискового пространства.
  3. Вычисления. Конфигурация оборудования для такой задачи не слишком сложная. Рекомендуется применять мощные графические ускорители. 
  4. Виртуализацию. В этом случае необходимы многоядерные процессоры, а также быстродействующие компоненты.
  5. Видеонаблюдение. Для сборки понадобится несколько планок RAM, мощный процессор и дисковая память большой емкости.

Сборка домашнего сервера

Справиться с этой работой смогут пользователи, которые разбираются в аппаратном обеспечении компьютера. Ведь компоновка сервера выполняется так же, как и ПК.

При сборке аппаратного устройства придется учесть множество параметров:

  1. Требования к функциям. Для одних задач понадобятся максимально производительные машины, а для других хватит и умеренных решений.
  2. Массив информации, который нужно обрабатывать. Чем он крупнее, тем больше объема оперативной памяти требуется.
  3. Нагрузку на сервер. Оборудование должно работать бесперебойно даже при мощных нагрузках, а не только в нормальном режиме.
  4. Количество текущих и потенциальных пользователей. Чем их больше, тем выше будет нагрузка на оборудование.

Еще стоит помнить, что серверу придется обрабатывать данные большого количества клиентов, поэтому он должен быть производительней и мощнее обычного ПК.

Реализация

Сначала надо выбрать железо, которое будет размещено в корпусе. Помните, что домашний сервер должен:

  • тихо работать;
  • иметь небольшие размеры;
  • быть достаточно мощным для обслуживания сети;
  • выделять как можно меньше тепла.

Кроме того, для сборки мощного компьютера понадобится специальный корпус (шасси) со встроенным блоком питания. Первым делом нужно разобраться с его размерами. К примеру, величина форм-фактора 1U в высоту составляет 43,7 мм. Это стандарт в инфраструктуре. Устройство получится компактным, но менее производительным, поскольку не хватит места для установки мощных процессоров, видеокарт или системы охлаждения. Распространенным вариантом является шасси 2U. Есть и другие размеры — 3U, 4U, 5U и даже 10U.

Оборудование

Покупать для сборки сервера желательно комплектующие от одного производителя. Помимо корпуса с блоком питания, понадобятся еще следующие элементы:

  • материнская плата;
  • производительные процессоры;
  • ОЗУ;
  • жесткие диски;
  • RAID-контроллер;
  • видеокарты.

Сборочные работы можно выполнить самостоятельно или доверить IT-инженеру.

35.jpg

Порядок монтажа сервера

Расположение компонентов зависит от архитектуры аппаратного устройства и высоты корпуса. Главное — не нарушать технику безопасности при размещении элементов компьютера: мелкие устанавливайте в верхней части, а тяжелые и большие — в нижней. Тем самым удастся уменьшить нагрузку на комплектующие и загрязнение небольших деталей.

Материнская плата

Это основной компонент сервера. Необходимо выбирать материнскую плату, к которой разрешено подключать дополнительные процессоры, оперативную память и накопители.

Важно! Устанавливать этот компонент компьютера без заземляющего браслета и диэлектрических перчаток нельзя. Статика может легко повредить электронные детали. Вдобавок запрещено размещать материнскую плату без специальной пластиковой прослойки. Если контакты платформы будут соприкасаться с металлическим корпусом, то короткого замыкания не избежать.

Установленную на место материнскую плату надо зафиксировать крепежными болтами. Делать это нужно осторожно, без усилий, чтобы не повредить хрупкий текстолит. Ускорить процесс можно с помощью аккумуляторных отверток. Затем к «материнке» подсоединяют кабели питания и вентиляторы охлаждающей системы.

Установка процессора

Для сборки сервера рекомендуется выбирать процессоры, которые способны быстро обрабатывать данные. При выборе этих устройств нужно обращать внимание на количество ядер. Обычно применяют версии, имеющие 12, 24 либо 32 ядра. Но для домашнего сервера можно использовать CPU с 4, 6, 8 или 10 ядрами.

Процессор устанавливают на материнскую плату строго по приложенной к нему инструкции. Это ответственный момент, требующий максимальной концентрации внимания.

Охлаждение

Следующий шаг — размещение радиатора охлаждения. Чаще всего для домашних серверов применяют пассивные устройства. Но перед их установкой между площадкой процессора и подошвой радиатора наносят термопасту.

Обратите внимание! Чтобы собранный компьютер работал стабильно, он должен находиться в помещении, температура в котором не превышает 20‒22°С, а влажность — 50%.

Оперативная память

Далее переходим к размещению ОЗУ. Необходимо использовать оперативную память достаточного объема, чтобы сервер работал без замедлений. Выбирать лучше модели с коррекцией ошибок и не меньше 8‒16 Гб.

Порядок установки модулей RAM зависит от производителя материнской платы. Оперативная память имеет несколько технических ограничений, которые не стоит игнорировать:

  1. Не рекомендуется вставлять нечетное количество планок. Это может привести к снижению скорости работы сервера.
  2. Распределять память лучше поканально, чтобы задействовать все возможные управляющие режимы
  3. В компьютер желательно устанавливать ОЗУ с идентичным значением частоты, напряжения и задержки, которое поддерживает материнская плата.

Перед размещением модулей памяти нужно убедиться, что в слотах отсутствуют посторонние частицы. Если потребуется, то разъемы нужно очистить сжатым воздухом.

Жесткие диски

При их выборе учитывают в основном скорость обработки информации, а также объем для хранения данных. Поэтому требуется установка производительных дисковых накопителей с интерфейсом SAS либо SATA: они обеспечивают максимальную пропускную способность.

Жесткие диски достаточно зафиксировать в штатных салазках и установить в отдельный слот, который специально выделен в корпусе под накопители.

Видеокарты

Серверы, как правило, не обрабатывают графику. Большинство из них работает без мониторов или в консольном режиме. Тратиться на мощную видеокарту необязательно, достаточно остановить свой выбор на интегрированном либо дискретном варианте.

Подключить видеокарту предстоит в разъем PCIe на материнской плате. Когда золотые контакты графического процессора коснутся слота шины, надо осторожно его вставить на место до появления щелчка. После этого закрепите видеоадаптер винтами.

Контроллер

Еще к слоту PCIe подключают RAID-контроллер. Устройство целесообразно использовать для мощных аппаратов с множеством накопителей: он объединяет жесткие диски в массивы RAID. Есть разные виды таких контроллеров, каждый из которых имеет свои плюсы. К примеру, RAID уровня 0 повышает производительность серверного оборудования, которое каждую секунду обрабатывает данные и запросы пользователей. Если нужно обеспечить безопасность информации, то стоит выбирать RAID1. Контроллер этого уровня зазеркаливает файлы на все жесткие диски. В итоге при выходе из строя одного накопителя другие продолжат работать без потери данных.

Укладка проводов в корпусе

Воздух через мощную машину должен проходить свободно, чтобы все комплектующие хорошо охлаждались. Если кабели будут просто болтаться и препятствовать прохождению воздушных масс, то отвод тепла ухудшится. Это приведет к повышенному расходу электричества, так как нагрузка на охлаждающую систему увеличится. Излишки проводов нужно зафиксировать к точкам крепления стяжками либо многоразовыми липучками.

Коммутаторы

Свитч понадобится, чтобы объединить несколько аппаратных устройств в локальную сеть для обмена информацией. Коммутатор позволит получить сверхбыструю внутреннюю связь. Для домашнего сервера подойдет устройство с количеством портов на борту от 5 до 20. Внутренняя пропускная способность свитча не должна быть меньше внешней, иначе при нагрузках работа сервера замедлится.

Резервное питание

Резкое отключение электроэнергии приводит к потере данных. Защитить серверное оборудование от скачков и пропадания напряжения в сети поможет ИБП (источник бесперебойного питания). Это устройство обеспечит оптимальное напряжение для стабильного функционирования сервера, а в аварийной ситуации при полном отсутствии электропитания сможет поддержать его работоспособность в течение некоторого времени.

Выбрать подходящий ИБП несложно: достаточно сложить паспортную мощность блоков питания сервера и других подключенных устройств. К полученному результату останется лишь добавить 30%.

Первый запуск и установка ПО 

Сначала надо подсоединить к собранному оборудованию монитор. После этого можно включить устройства. Если появится изображение, значит, не зря старались, вам удалось самостоятельно собрать сервис. Тем не менее, стоит еще раз проверить:

  • идет ли питание ко всем комплектующим;
  • правильно ли подключены планки оперативной памяти;
  • видит ли устройство все установленные элементы.

При необходимости нужно срочно заменить дефектное «железо».

Выбор программного обеспечения для мощного компьютера зависит от возложенных на него задач. Для компоновки домашнего сервера подойдет операционная система Windows Server. Но перед установкой ОС не забудьте разбить жесткий диск на два тома. Одна часть должна быть больше, а другая — меньше. Настройку оборудования после инсталляции ОС лучше доверить ИТ-инженерам.

Тестирование сервера

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

Во время тестирования ОЗУ запускается процесс чтения и записи данных в разной последовательности. При обнаружении ошибок нужно найти сбойный модуль RAM и заменить его на другой (желательно аналогичный).

В процессе тестирования жесткого диска программа имитирует мощную нагрузку на сервер в течение длительного времени. После этого проверяется S.M.A.R.T. всех накопителей. Не должно быть ненулевых значений ни в одном параметре, в противном случае придется заменить диск.

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

Как создается сервер

Многие задаются вопросом «Как создать сервер?», но для того чтобы на него ответить, необходимо понять, что скрывается под термином «сервер». Сервер — это аппаратно-программная вычислительная система, которая может предоставлять различные сервисы своим «клиентам» (клиент – это любой пользователь ПК, подсоединяющийся к серверу и пользующийся его услугами).

Как создается сервер

Инструкция

Аппаратная часть сервера (аппаратное обеспечение) — это по сути обычный компьютер повышенной мощности и с возможностью свободного расширение, т.е. увеличения мощности посредством установки новых модулей.

Программная часть сервера (серверное программное обеспечение) — эта та часть сервера, которая выполняет основной функционал. Она обычно состоит специальных операционных (их еще называют серверными, т.к. они ориентированы только на определенные задачи: высокая работоспособность, надежная система отказоустойчивости, отсутствие лишних модулей системы вроде поддержки игр и т.д.). Примером подобной системы может служить Windows Server 2003 x64. Вторая часть — это серверные программы, такие как прокси-сервер, http-сервер (например, Apache), сервер баз данных (например Oracle) и т.д.

Для разработчиков, а также для начинающих администраторов существуют различные сборки серверов, которые можно скачать и запустить без глубоких познаний в администрировании и программировании, просто выполнив установку. Одной из самых популярных программ подобного типа является Xampp.

Данный программный пакет является кроссплатформенной (т.е. он будет работать как с Windows, так и с Unix/Linux/Solaris) сборкой веб-сервера, который включает в себя два основных сервера Apache (http-сервер для обслуживания запросов) и MySql (сервер популяной базы данных), интерпретатор языка программирования php (без данного компонента сервера не будут работать php-скрипты), язык программирования Perl, серверы отправки и получения электронной почты — POP3/SMTP, а также несколько полезных утилит для управления сервером — phpMyAdmin(система управления базами данных) и fpt-клиент FileZilla.

Последним этапом создания сервера является создание (покупка) постоянного ip-адреса, ведущего к вашему серверу. Покупку ip-адреса и доменного имени можно сделать у одной из компаний, предоставляющих услуги по аренде хостинга (хостинг — это арендуемый сервер с установленной операционной системой и веб-сервером) и доменов (например, agava.ru).

Видео по теме

Войти на сайт

или

Забыли пароль?
Еще не зарегистрированы?

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.


Введение

Интернет играет в нашей жизни большую роль. Мы берем почту, узнаем свежую информацию, отлавливаем своих знакомых через ICQ… Но что ассоциируется со словом Интернет у большинства людей в первую очередь? Сайты. Многие при слове «Интернет» вспоминают свои любимые сайты, а некоторые (не слишком продвинутые) ставят знак равенства между WWW и Интернетом, хотя в последнем есть много другого интересного: email, IRC, p2p-сети, MUD’ы и так далее. Но World Wide Web играет доминирующую роль.

В основе WWW лежит протокол HyperText Transfer Protocol. Надо сказать, что HTTP может использоваться не только для передачи сайтов, но и для передачи всего чтобы то ни было. С помощью протокола HTTP мы можем скачать, например, недавно вышедший фильм или свежие mp3 :). В p2p-сетях HTTP-протокол применяется именно в этих целях.

В данном пособии мы рассмотрим, как написать простой веб-сервер. Я предполагаю, что вы знакомы с основами программирования winsock и умеете создавать сокеты и коннектиться к чему-нибудь :).

Основы HyperText Transfer Protocol

Идея HTTP довольно проста. Клиент шлет запрос серверу, тот рассматривает его и шлет соответствующий ответ. Ответом может быть запрошенный файл, сообщение о том, что такого файла на сервере нет или что-то еще. Примерная структура запроса следующая:

<метод> <запрашиваемый_ресурс> HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[..заголовочных полей может быть много..]<n>
<заголовочное_поле>: <значение><n>
<n>

<метод> — вид запроса. Основных два: GET и POST. Друг от друга они отличаются, главным образом, способом передачи дополнительной информации, отсылающейся вместе с запросом. В этом туториале мы рассмотрим только метод GET — функционально он похож на POST, но несколько проще. О методе POST я расскажу во второй части данного туториала, если, конечно, таковая вообще появится на свет :).

<n> — это два байта 0Dh, 0Ah, несомненно, хорошо знакомые всем ассемблерщикам :).

Таким образом, с методом мы определились. На данный момент запрос, который мы (в качестве клиента) должны будем послать серверу выглядит так:

GET <запрашиваемый_ресурс> HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[...]
<заголовочное_поле>: <значение><n>
<n>

Теперь нам нужно задать <запрашиваемый_ресурс>. Возьмем типичную ссылка на одном из лучших сайтов по программированию в Рунете WASM.RU (немного рекламы не помешает 🙂 ):

http://www.wasm.ru/article.php?article=1016002

Здесь «http://» указывает на то, что используется протокол HTTP, «www.wasm.ru» говорит о том, что необходимо подсоединиться к сайту www.wasm.ru, а все, что идет после знака вопроса — это дополнительные параметры страницы. Последние используются не всегда и не на всех сайтах.

Предположим, что мы подсоединились к www.wasm.ru и хотим получить article.php с необходимыми параметрами. Очевидно, что раз мы уже подсоединились к данному серверу и знаем, что необходимо использовать протокол HTTP, то пересылать «http://www.wasm.ru» не нужно, а значит, мы пошлем только «/article.php?article=1016002». Теперь наш запрос к серверу выглядит так:

GET /article.php?article=1016002 HTTP/1.1<n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[...]
<заголовочное_поле>: <значение><n>
<n>

Теперь осталось разобраться с заголовочными полями. С их помощью клиент передает дополнительную информацию о себе или характере запроса. Например, довольно часто используемым заголовочным полем является ‘User-Agent’. Опера, скажем, шлет следующее:

User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 98) Opera 6.02 [en]

Другим еще более важным полем является ‘Host’. В нем задается имя веб-сервера, с которого мы хотим получить документ. «Как же так», — можете сказать вы, — «Ведь мы же уже указывали имя веб-сервера, когда создавали сокет и коннектились к нему!» Да, это так. Когда мы коннектились к серверу, мы вызвали функцию gethostbyname, которой передали имя веб-сервера, а она возвратила нам адрес структуры, содержащей адрес сервера. Дело в том, что одному IP-адресу может соответствовать несколько имен сайтов, поэтому когда веб-сервер получает запрос, он должен знать к какому сайту обращается клиент (один веб-сервер может обслуживать тысячи различных сайтов, которые физически будут расположены на одной машине с одним адресом). Для этого мы пишем в ‘Host’ адрес сайта, к которому обращаемся:

Host: www.wasm.ru

Вот как теперь выглядит наш запрос:

GET /article.php?article=1016002 HTTP/1.1<n>
User-Agent: ManualSender/1.0 :)<n>
Host: www.wasm.ru<n>
<n>

Надо заметить, что хотя эти и другие заголовочные поля являются очень желательными, но, строго говоря, они не являются обязательными, то есть их может и не быть. Также я хочу обратить ваше внимание на то, что за последним заголовочным файлом следуют _два_ <n>, а не один. Это важно.

Теперь взглянем на все вышеизложенное с точки зрения веб-сервера (ведь мы же собирались писать веб-сервер, помните? 🙂 ). Он ждет, пока к нему не поступит запрос, обрабатывает его и посылает ответ, который выглядит примерно так:

HTTP/1.1 <код_ответа> <сообщение><n>
<заголовочное_поле>: <значение><n>
<заголовочное_поле>: <значение><n>
[..заголовочных полей может быть много..]<n>
<заголовочное_поле>: <значение><n>
<n>
<тело_документа>

Получив запрос, сервер должен выдать клиенту код ответа (это трехзначное число), а также сопутствующее ему сообщение. Например, если запрошенный документ был найден, первая строка ответа будет примерно следующей:

HTTP/1.1 200 Ok

Если коды ответов жестко заданы стандартом (200 означает, что запрос был успешно обработан и будет возвращен соответствующий документ/информация), то <сообщение> зависит только от вашей фантазии (конечно, по смыслу оно должно совпадать с <кодом_ответа>. Например, вместо «HTTP/1.1 200 Ok» мы можем послать «HTTP/1.1 200 You want it, you’ll get it» :).

В ответе сервера также могут быть заголовочные поля. Они содержат дополнительные сведения об ответе и данных, идущих вместе с ним. Далее я приведу важнейшие (для нас).

Content-Type — этот заголовок задает тип отдаваемых данных. Главнейшие типы заданы в стандарте, и от того, что вы напишите в данном поле, зависит поведение клиента при приеме данных. Например, если вы посылаете браузеру пользователя html-файл, то необходимо указать тип данных ‘text/html’, иначе браузер может отобразить файл неправильно, либо вообще не будет его отображать, а предложит пользователю его скачать.

Content-Length — здесь указывается длина данных (не включая сам ответ с заголовочными данными) в байтах.

Исходя из вышесказанного, ответ сервера будет выглядеть примерно так:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
Content-Length: <длина_документа><n>
<n>
<тело_документа>

Если мы хотим отослать простой html-файл, то можем сократить ответ до следующего:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
<n>
<тело_документа>

В результате получаем следующее. Клиент шлет запрос на получение документа, лежащего, например, в корне сервера:

GET / HTTP/1.1<n>
User-Agent: ManualSender/1.0 :)<n>
Host: www.someoneserver.com<n>
<n>

Сервер получает этот запрос, обрабатывает и выдает корневую страницу:

HTTP/1.1 200 Ok<n>
Content-Type: text/html<n>
<n>
<html>
<head><title>Добро пожаловать на HTTP-сервер!</title></head>
<body>Вы находитесь на нашем http-сервере</body>
</html>

Код приложения

;---------------------------------------------------------------------
; http.asm
;---------------------------------------------------------------------

format PE console

entry start

; Подключаемые файлы

include '....includekernel.inc'
include '....includeuser.inc'
include '....includemacrostdcall.inc'
include '....includemacroimport.inc'
include 'winsock.inc'
include 'macros.inc'

; Используемые значения

WM_SOCKET = WM_USER+100
INBUF_LEN     = 100000

; Секции программы

section '.data' data readable writeable

include 'strings.inc'

  hInstance dd ?
  http_class dd ?
  hwnd dd ?
  msg dd ?
  sock dd ?
  rv dd ?
  wc WNDCLASS
  wsadata WSADATA
  saddr SOCKADDR_TCP
  iaddr SOCKADDR_TCP
  buf rb INBUF_LEN

section '.code' code executable readable

include 'http_window.asm'

start:

      invoke GetModuleHandle, 0
      mov [hInstance], eax

      ; Инициализируем сокеты
      invoke WSAStartup, 101h, wsadata
      test eax, eax
      jnz error.wsanotinit

      ; Создаем окно
      jmp make_http_window

      ; Цикл обработки сообщений
   .msg_loop:
        invoke GetMessage,msg,NULL,0,0
        test eax,eax
        jz .exit
        invoke TranslateMessage,msg
        invoke DispatchMessage,msg
        jmp .msg_loop

   .exit:
      ; Очищаем сокеты
      invoke WSACleanup
      invoke ExitProcess, 0

error:

   .recv_error:

      ccall [printf], _recv_error
      jmp start.exit

   .connection_was_closed:

      ccall [printf], _connection_was_closed
      jmp start.exit

   .wsanotinit:

      ccall [printf], _wsanotinit, eax, wsadata.size
      jmp start.exit

   .bind_error:

      ccall [printf], _bind_error
      jmp start.exit

   .listen_error:

      ccall [printf], _listen_error
      jmp start.exit

   .cant_createsocket:

      invoke WSAGetLastError
      ccall [printf], _cant_createsocket, eax
      jmp start.exit

   .cant_resolve:

      ccall [printf], _fmtstr, _cant_resolve
      jmp start.exit

   .select_error:
      invoke WSAGetLastError
      cinv printf, _select_error, eax
      jmp start.exit

   .accept_error:
      invoke WSAGetLastError
      cinv printf, _accept_error, eax
      jmp start.exit


section '.idata' import data readable writeable

  library kernel32, 'kernel32.dll', 
          winsock, 'ws2_32.dll', 
          msvcrt, 'msvcrt.dll', 
          user32, 'user32.dll'

  kernel32:
  import ExitProcess, 'ExitProcess', 
         GetModuleHandle, 'GetModuleHandleA', 
         GetLastError, 'GetLastError', 
         WriteFile, 'WriteFile', 
         GetStdHandle, 'GetStdHandle', 
         RtlZeroMemory, 'RtlZeroMemory', 
         lstrlen, 'lstrlen', 
         lstrcmp, 'lstrcmp'

  user32:
  import RegisterClass, 'RegisterClassA', 
         DefWindowProc, 'DefWindowProcA', 
         CreateWindowEx, 'CreateWindowExA', 
         DestroyWindow, 'DestroyWindow', 
         GetMessage, 'GetMessageA', 
         TranslateMessage, 'TranslateMessage', 
         DispatchMessage, 'DispatchMessageA', 
         MessageBox, 'MessageBoxA', 
         ShowWindow, 'ShowWindow'

  winsock:
  import WSAStartup, 'WSAStartup', 
         WSACleanup, 'WSACleanup', 
         socket, 'socket', 
         gethostbyname, 'gethostbyname', 
         connect, 'connect', 
         WSAGetLastError, 'WSAGetLastError', 
         recv, 'recv', 
         send, 'send', 
         htons, 'htons', 
         bind, 'bind', 
         listen, 'listen', 
         WSAAsyncSelect, 'WSAAsyncSelect', 
         accept, 'accept', 
         closesocket, 'closesocket'

  msvcrt:
  import printf, 'printf'

;---------------------------------------------------------------------
; http.asm
;---------------------------------------------------------------------
; Создание скрытого окна, которое будет получать сообщения от сокета
make_http_window:

     ; Регистрируем класс окна
     mov eax, [hInstance]
     mov [wc.hInstance], eax
     mov [wc.hIcon], 0
     mov [wc.hCursor], 0
     mov [wc.style], 0
     mov [wc.lpfnWndProc], http_window_proc
     mov [wc.cbClsExtra], 0
     mov [wc.cbWndExtra], 0
     mov [wc.hbrBackground], COLOR_BTNFACE+1
     mov [wc.lpszMenuName], 0
     mov [wc.lpszClassName], _http_window_class
     invoke  RegisterClass, wc
     mov [http_class], eax
     test eax, eax
     jnz .create_http_window
     cinv printf, _fmtstr, _class_not_registered
     jmp start.exit
  .create_http_window:
     ; Создаем окно
     invoke GetModuleHandle, 0
     invoke CreateWindowEx, NULL, [http_class], _http_window_name, 
                            WS_SYSMENU, 100, 100, 100, 100, NULL, NULL, 
                            [hInstance], 0
     mov [hwnd], eax
     test eax, eax
     jnz start.msg_loop
     cinv printf, _fmtstr, _window_not_created
     br
     invoke GetLastError
     cinv printf, _fmtnum, eax

     jmp start.exit

proc http_window_proc, hWnd, wmsg, wparam, lparam
     enter
     push ebx esi edi
     cmp [wmsg], WM_CREATE
     je .wmcreate
     cmp [wmsg], WM_DESTROY
     je .wmdestroy
     cmp [wmsg], WM_SOCKET
     je .wmsocket

  .defwndproc:
     invoke DefWindowProc, [hWnd], [wmsg], [wparam], [lparam]
     jmp .finish

  .wmcreate:

     cinv printf, _start_sockets
     ; Создаем сокет
     invoke socket, AF_INET, SOCK_STREAM, 0
     cmp eax, -1
     je error.cant_createsocket
     mov [sock], eax
     ; Указываем Windows, чтобы она извещала нас о входящих соединениях
     invoke WSAAsyncSelect, [sock], [hWnd], WM_SOCKET, FD_ACCEPT
     cmp eax, -1
     je error.select_error

     ; Получаем адрес localhost
     invoke gethostbyname, _localhost
     test eax, eax
     jz error.cant_resolve
     mov eax, [eax+12]
     mov eax, [eax]
     mov eax, [eax]

     ; Подготавливаем saddr
     mov [saddr.sin_addr], eax
     mov [saddr.sin_family], AF_INET
     invoke htons, 80
     mov [saddr.sin_port], ax
     ; Биндим сокет к локальному адресу
     invoke bind, [sock], saddr, saddr.size
     test eax, eax
     jnz error.bind_error

     ; Начинаем слушать порт
     invoke listen, [sock], 10
     test eax, eax
     jnz error.listen_error

     jmp .done

  .wmdestroy:
     jmp .done

  ; Сообщение от WSAAsyncSelect
  .wmsocket:
     mov eax, [lparam]
     and eax, 0FFFFh
     cmp eax, FD_ACCEPT
     je .fd_accept
     cmp eax, FD_READ
     je .fd_read
     cmp eax, FD_CLOSE
     je .fd_close
     jmp .done

  .fd_accept:
     invoke accept, [wparam], iaddr, 0
     cmp eax, -1
     je error.accept_error
     invoke WSAAsyncSelect, eax, [hwnd], WM_SOCKET, FD_READ + FD_CLOSE
     jmp .done

  .fd_read:
     ; Обнуляем буфер
     invoke RtlZeroMemory, buf, INBUF_LEN
     ; Читаем данные из сокета
     invoke recv, [wparam], buf, INBUF_LEN, 0
     push eax
     invoke GetStdHandle, -11
     pop edx
     invoke WriteFile, eax, buf, edx, rv, 0
     invoke send, [wparam], index, index_size, 0
     invoke closesocket, [wparam]
     jmp .done

  .fd_close:
     invoke closesocket, [wparam]
     jmp .done

  .done:
     xor eax, eax

  .finish:
     pop ebx esi edi
     return
;---------------------------------------------------------------------
; strings.inc
;---------------------------------------------------------------------
_http_window_class db 'http_server_window_class', 0
_http_window_name db 'Noname', 0
_fmtstr db '%s', 0
_fmtnum db '%d', 0
_br db 0Dh, 0Ah, 0
_window_not_created db 'Window is not created', 0
_class_not_registered db 'Class is not registered', 0
_localhost db 'localhost', 0
_cant_resolve db "Can't resolve host", 0
_cant_createsocket db "Can't create socket: %d", 0
_bind_error db 'Error binding to host', 0
_listen_error db 'Error starting listening', 0
_wsanotinit db 'WSA not initialized: %d, %d', 0
_connection_was_closed db 'Connection was closed', 0
_recv_error db 'Receiving error', 0
_select_error db 'WSAAsyncSelect error %d', 0
_accept_error db 'Accept error %d', 0
_start_sockets db 'Starting sockets', 0Dh, 0Ah, 0
_shutdown_sockets db 'Shutdowning sockets', 0
_inbound_connection db 'Inbound connection', 0
_method_get db 'GET', 0

index db 'HTTP/1.1 200 Ok', 0Dh, 0Ah
      db 'Content-type: text/html', 0Dh, 0Ah
      db 0Dh, 0Ah
      db '<html>', 0Dh, 0Ah
      db '<head>', 0Dh, 0Ah
      db '<title>Welcome to HTTP Server!</title>', 0Dh, 0Ah
      db '</head>', 0Dh, 0Ah
      db '<body>', 0Dh, 0Ah
      db '<h2>HTTP Server Online</h2>', 0Dh, 0Ah
      db 'Best http server in the world!', 0Dh, 0Ah
      db '</body>', 0Dh, 0Ah
      db '</html>'
index_size = $-index

Анализ кода

«Веб-сервер», чей исходный код был приведен выше, очень примитивен. Он умеет только принимать запрос и, не проверяя его на правильность, выдавать только приветственную html-страницу.

В http.asm все, я надеюсь, достаточно понятно. Мы инициализируем сокеты, создаем окно (для чего, я поясню позже), входим в цикл обработки сообщений, а перед тем, как завершить работу приложения, вызываем WSACleanup.

Самое интересное находится в http_window.asm. При обработке сообщения WM_CREATE мы создаем сокет:

     ; Создаем сокет
     invoke socket, AF_INET, SOCK_STREAM, 0
     cmp eax, -1
     je error.cant_createsocket
     mov [sock], eax

А потом вызываем следующую функцию:

     ; Указываем Windows, чтобы она извещала нас о входящих соединениях
     invoke WSAAsyncSelect, [sock], [hWnd], WM_SOCKET, FD_ACCEPT

Вот что об этой функции говорит Platform SDK:

[ начало описания функции WSAAsynctSelect ]

WSAAsyncSelect

Функция WSAAsyncSelect указывает Windows посылать сообщения о событиях, касающихся определенного сокета.

int WSAAsyncSelect(
  SOCKET <>,           
  HWND hWnd <>,          
  unsigned int wMsg <>,  
  long lEvent <>

);

Параметры:

s — Дескриптор сокета, о событиях, связанных с которым, будет сообщаться.

hWnd — Хэндл окна, которому будут посылаться эти сообщения.

wMsg — Сообщение, которое будет посылаться.

lEvent — Битовая маска, в которой задаются интересующие события.

Возвращаемые значения

Если вызов функции WSAAsyncSelect прошел успешно, возвращаемое значение будет равно нулю. В противном случае будет возвращено SOCKET_ERROR, а код ошибки можно будет получить, вызвав WSAGetLastError.

[ конец описания ]

Учтите, что после того, как WSAAsyncSelect отошлет вам сообщение о конкретном событии, связанном с сокетом, то пока вы не предпримите определенных действий, нового сообщения о таком же событии вы не получите. Например, если вы получили сообщение FD_ACCEPT (кто-то пытается законнектиться к вам), то сообщения о другой попытки коннекта вы не получите до тех пор, пока не вызовите функцию accept.

Мы задаем WM_SOCKET, определенное в http.asm, в качестве сообщение, которое будет присылаться Windows, когда произойдет интересующее нас сообщение. Необходимая информация будет находиться в wParam (дескриптор сокета, с которым связано событие) и в lParam (в нижнем слове — код события).

Теперь, когда кто-нибудь попытаемся приконнектиться к сокету, наше окно получит соответствующее уведомление от операционной системы. Впрочем, сначала нужно ассоциировать созданный сокет с определенным адресом и портом, к которым и должны будут коннектиться посетители веб-сервера.

     ; Получаем адрес localhost
     invoke gethostbyname, _localhost
     test eax, eax
     jz error.cant_resolve
     mov eax, [eax+12]
     mov eax, [eax]
     mov eax, [eax]

     ; Подготавливаем saddr
     mov [saddr.sin_addr], eax
     mov [saddr.sin_family], AF_INET
     invoke htons, 80
     mov [saddr.sin_port], ax

Веб-сервер будет «висеть» на localhost’е (т.е. на локальной машине) на 80-ом порту, который является стандартным HTTP-портом. Если в адресе сайта прямо не указан порт, то браузер будет обращаться к 80-ому порту.

     ; Начинаем слушать порт
     invoke listen, [sock], 10
     test eax, eax
     jnz error.listen_error

Собственно, в данных строчках и содержится ответ на то, как сделать из приложения сервер (не обязательно web). Это делает функция listen.

[ начало описания функции listen ]

listen

Функция listen устанавливает сокет в состояние, в котором он слушает порт на предмет входящих соединений.

int listen(
  SOCKET <>,    
  int backlog <>

);

Параметры

s — Дескриптор сокета

backlog — Максимальное количество входящих соединений.

Возвращаемые значения

Если во время вызова не произошло никакой ошибки, listen возвратит ноль. В противном случае будет возвращено значение SOCKET_ERROR, а код ошибки можно будет получить с помощью функции WSAGetLastError.

[ конец описания ]

  ; Сообщение от WSAAsyncSelect
  .wmsocket:
     mov eax, [lparam]
     and eax, 0FFFFh
     cmp eax, FD_ACCEPT
     je .fd_accept
     cmp eax, FD_READ
     je .fd_read
     cmp eax, FD_CLOSE
     je .fd_close
     jmp .done

Было получено сообщение WM_SOCKET. Это значит, что произошло какое-то интересующее нас событие, связанное со слушающим сокетом.

  .fd_accept:
     invoke accept, [wparam], iaddr, 0
     cmp eax, -1
     je error.accept_error

Кто-то пытается подсоединиться к нашему веб-серверу. Вызываем функцию accept, чтобы разрешить входящее соединение.

[ начало описания функции accept ]

accept

Функция accept разрешает входящее соединение.

SOCKET accept(
  SOCKET s,
  struct sockaddr FAR *addr,
  int FAR *addrlen

);

Параметры

s — Дескриптор сокета, который ранее был помещен в состояние прослушивания с помощью функции listen. Фактическое соединение осуществляется с помощью сокета, который возвращается accept’ом.

addr — Необязательный указатель на буфер, который получит адрес того, кто пытается подсоединиться к серверу.

addrlen — Необязательный указатель на двойное слово, которое содержит длину addr.

Возвращаемые значения

Если не произошло никакой ошибки, accept возвратит дескриптор нового сокета, через который и будет происходить соединение.

В противном случае будет возвращен INVALID_SOCKET, а код ошибки можно будет получить с помощью функции WSAGetLastError.

Переменная, на которую указывает addrlen, вначале содержит объем, занятый структурой, на которую указывает addr. По возвращении она будет содержать длину возвращенного адреса в байтах.

[ конец описания ]

     invoke WSAAsyncSelect, eax, [hwnd], WM_SOCKET, FD_READ + FD_CLOSE
     jmp .done

Соединение разрешено, и мы вызываем функцию WSAAsyncSelect, чтобы получить соответствующее уведомление, когда можно будет читать из сокета или он будет закрыт.

  .fd_read:
     ; Обнуляем буфер
     invoke RtlZeroMemory, buf, INBUF_LEN
     ; Читаем данные из сокета
     invoke recv, [wparam], buf, INBUF_LEN, 0
     push eax
     invoke GetStdHandle, -11
     pop edx
     invoke WriteFile, eax, buf, edx, rv, 0
     invoke send, [wparam], index, index_size, 0
     invoke closesocket, [wparam]
     jmp .done

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

  .fd_close:
     invoke closesocket, [wparam]
     jmp .done

Если сокет был закрыт клиентом, то мы его тоже закрываем со своей стороны.

Дополнительная литература

Для получения подробной информации о протоколе HTTP я рекомендую вам обратиться к RFC 2068.

Заключение

Надеюсь, вы почерпнули из этого туториала какую-нибудь полезную информацию. Напоследок мне хотелось бы сказать, что хотя составлять конкуренцию таким грандам как Apache и IIS без веских на то оснований, возможно, и не стоит, тем не менее, собственный маленький веб-сервер может быть очень полезен. Мне, например, предложили встроить в него механизм самораспространения, «чтобы он сам приходил к людям на дом» и устанавливался «через упрощенную процедуру инсталляции» ака Outlook. Другим, менее чреватым в плане возможных последствий для автора, вариантом может быть создание утилиты удаленного (не обязательно скрытого) администрирования, причем в качестве клиента будет выступать браузер, что весьма удобно, так как отпадет надобность в написании сопутствующей серверу клиентской программы. Возможно, вы найдете еще какое-нибудь применение для http-сервера. Все в ваших руках!

(c) Aquila / Hi-Tech, 2002

[C] Aquila / WASM.RU

Источник: wasm.ru /05.08.2002/


Поделиться в соц сетях

Понравилась статья? Поделить с друзьями:
  • Как составить исковое заявление в суд по трудовым спорам
  • Как найти 16 ричную систему
  • Триколор сила 100 качество 0 как исправить
  • Как найти траст банк
  • Как исправить ошибку в приложении гугл плей маркет