From: Шевелёв Денис <alma_tv_denis@mail.ru.>
Newsgroups: email
Date: Mon, 24 Mar 2008 14:31:37 +0000 (UTC)
Subject: Пишем "ДЕМОНА" своими руками
Итак целью нашей с Вами задачи встал вопрос написания сетевого демона
для управления чем либо на Linux из под Windows с различных мест.
В каждой реализации Linux всегда присутствует библиотека GNU libc.
Мы не будем использовать объектно-ориентированное-программирование (ООП),
а возьмем стандартный язык разработки под Linux - это Cи.
Статья рассчитана на пользователей только начинающих изучать язык Си.
Краткая справка:
Разработка системы GNU началась 27 сентября 1983 года,
когда Ричард Столлмэн опубликовал объявление о проекте в
группах новостей net.unix-wizards и net.usoft.
5 января 1984 года Столлмэн уволился из Массачуссетского технологического
института с целью посвятить своё время написанию свободной операционной системы,
а также для того, чтобы институт не мог претендовать на какие-либо права
на исходный код системы. Первой программой GNU стал текстовый редактор Emacs.
В настоящее время система GNU/Linux, более широко известная как просто Linux,
достаточно распространена (особенно на рынке серверов) и является вполне
завершённой. Она состоит из большого количества программ проекта GNU (в первую
очередь системных утилит и GNU toolchain), ядра Linux - части системы,
отвечающей за выполнение других программ, включающей драйверы устройств и т. п.,
- и множества других свободных программ.
И так преступим к написанию сетевого ЭХО сервера с признаками "демонизма".
Файл назовем daemon.c
cd / --переходим в корневой каталог
mkdir myprogonlinux --создаем папку где будем писать демона
cd myprogonlinux
touch daemon.c --создаем файл
vi daemon.c
и так файл daemon.c
#include <stdio.h> //--список объявлений и используемых нами готовых библиотек Cи
#include <string.h> //--подробно о каждой библиотеке можно узнать в man
#include <sys/stat.h> //--например man stdio и.т.д
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <resolv.h>
#include <errno.h>
Любая программа на Си (если это не вспомогательный модуль где описываются выполняемые
универсальные функции)начинается с главной процедуры или функции main
int Daemon(void) // --это объявление функции для нашего демона то есть реализация ЭХО сервера
int main(int argc, char* argv[]) //--это наша главная процедура с которой начинается программа
{
pid_t parpid;
if((parpid=fork())<0) //--здесь мы пытаемся создать дочерний процесс главного процесса (масло масляное в прямом смысле)
{ //--точную копию исполняемой программы
printf("ncan't fork"); //--если нам по какойлибо причине это сделать не удается выходим с ошибкой.
exit(1); //--здесь, кто не совсем понял нужно обратится к man fork
}
else if (parpid!=0) //--если дочерний процесс уже существует
exit(0); //--генерируем немедленный выход из программы(зачем нам еще одна копия программы)
setsid(); //--перевод нашего дочернего процесса в новую сесию
Daemon(); //--ну а это вызов нашего демона с нужным нам кодом (код будет приведен далее)
return 0;
}
Я думаю многие после данного объяснения ничего не поняли.
Поясняю на пальцах
Любая программа на Си начинает исполнятся с главной процедуры main.
При запуске программы создается процесс (на время работы программы).
Мы просто создаем дочернюю копию этого процесса и переводим его в новую сессию,
что бы он не блокировал текущую вашу сессию.
для лучшего понимания (не поленитесь) перепишем выше приведенный код и сделаем два режима
работы "демон","не демон"
#define PORT_CON 6823 //--определяем порт который будет прослушивать наш эхо сервер
struct sockaddr_in client_name;//--структура sockaddr_in клиентской машины (параметры ее нам неизвестны. Мы не знаем какая машина к нам будет подключаться)
int size = sizeof(client_name);//--размер структуры (тоже пока неизвестен)
int client_socket_fd; //--идентификатор клиентского сокета
//--вобще теория сокетов более подробно будет рассмотрена в следующей статье
char sockbuff[1024]; //--наш буфер обмена информацией с клиентом
time_t now;
struct tm *ptr;
char tbuf[80];
int Daemon(void)
char* getTime();
char * getTime() //--функция определения времени в нужном формате
{
char *ret;
ret=(char*)malloc(100);
bzero(ret,100);
time(&now);
ptr = localtime(&now);
strftime(tbuf,80,"%Y-%B-%e %H:%M:%S",ptr);
ret=tbuf;
return (ret);
}
int main(int argc, char* argv[])
{
pid_t parpid;
if (argc < 2)
{
printf("Usage ./daemon -d for daemon or ./daemon -i for interactiven");
exit(1);
}
if (strcmp(argv[1],"-i")==0)
Daemon();
else if (strcmp(argv[1],"-d")==0)
{
if((parpid=fork())<0)
{
printf("ncan't fork");
exit(1);
}
else if (parpid!=0)
exit(0);
setsid();
Daemon();
}
else
{
printf("Usage ./daemon -d for daemon or ./daemon -i for interactiven");
exit(1);
}
return 0;
}
int Daemon(void)
{
FILE *logfile; //--лог для подключившихся клиентов причем для каждого будет свой
int socket_fd,nbytes; //--объявляем идентификатор сокета нашего сервера
char host[20];
char *namlog;
void sig_child(int);//--объявление функции ожидания завершения дочернего процесса
pid_t pid;
struct sockaddr_in name;//--структура sockaddr_in для нашего сервера на сей раз ее придется заполнить
namlog=(char*)malloc(25);
socket_fd=socket(PF_INET,SOCK_STREAM,0); //--создаем сокет сервера в данном случае TCP, если бы мы использовали флаг SOCK_DTGRAM то получили бы сокет UDP
name.sin_family=AF_INET; //--говорим что сокет принадлежит к семейству интернет
name.sin_addr.s_addr=INADDR_ANY; //--наш серверный сокет принимает запросы от любых машин с любым IP-адресом
name.sin_port=htons(PORT_CON); //--и прослушивает порт 6823
if(bind(socket_fd, &name, sizeof(name))==-1) //--функция bind спрашивает у операционной системы,может ли программа завладеть портом с указанным номером
{
perror("bind"); //--ну если не может,то выдается предупреждение
exit(0); //--соответственно выход из программы
}
listen(socket_fd,20); //--перевод сокета сервера в режим прослушивания с очередью в 20 позиций
for(;;) //--бесконечный цикл (как так?... спросите ВЫ. У нас же сервер,который должен постоянно принимать и обслуживать запросы и работать пока его не погасят насильно)
{
signal(SIGCHLD,sig_child); //--если клиент уже поработал и отключился ждем завершение его дочернего процесса
client_socket_fd = accept (socket_fd,&client_name,&size); //--подключение нашего клиента
if (client_socket_fd>0) //--если подключение прошло успешно
{
if ((pid=fork())==0) //--то мы создаем копию нашего сервера для работы с другим клиентом(то есть один сеанс уде занят дублируем свободный процесс)
{
inet_ntop(AF_INET,&client_name.sin_addr,host,sizeof(host));--в переменную host заносим IP-клиента
bzero(namlog,25);
strcpy(namlog,host);
strncat(namlog,"