Есть задачка: на одном сервере хранится текстовом файле база данных (сервер А), на втором (сервер В) нужно организовать вычисления по данным той базы. Каким макаром можно сделать так, что бы сервер В сказал серверу А: "вот тебе входные данные" и в ответ получил "вот результат"?
В литературе нашел примеры организации tcp и udp серверов и клиентов, но там все заканчивается на самом интересном: вот мы и установили сокет! А как его теперь применить написано очень размыто :(Заранее благодарен хотя бы за то, что обратили внимание на мое сообщение :)
Отправка данных по TCP:send(сокет, ссылка_на_данные, размер_данных_в_байтах, флаги);
Прием данных по TCP:
recv(сокет, ссылка_на_память_для_данных, размер_принимаемых_данных_в_байтах, флаги);
Вместо флагов обычно используется "0". Рекомендуется обратить внимание еще и на функции poll и select. А так же почитать умную книжку "Программирование TCP/IP". Автор, кажись, некто Стивенсон. Точно не скажу - под рукой сейчас нет :)
Короче, man :
socket, fcntl, bind, listen, accept, connect, send, recv, sendto, recvfrom, poll, select, shutdown, close
Посылать и получать - send и recv или sendto и recvfrom. В манах они хорошо описаны, в книгах не хуже. Внимательно смотри на все что касается сигналов, это пригодится в будущем.Теперь определимся с терминами. Авторизация - это проверка полномочий (прав доступа). Судя по вопросу, до проблем авторизации тебе еще далеко, тебе надо реализовать протокол обмена данными.
Рассматривай сетевое соединение как трубу, через которую передаются данные. В один конец трубы запихали байт, из другой трубы он вылез. Конечно, пихать данные по одному байту крайне не эффективно, лучше посылать данные блоками. Тут подстерегает коварная ошибка: при использовании TCP если ты отправил блок, скажем, из 100 байт, это не значит что на другой стороне recv() получит сразу 100 байт. Он может получить, например, сначала 10, а потом остальные 90, а может сразу 200 (если захватит кусок от предыдущей или последующей порции данных).
Тебе надо изобрести протокол, по которому будут общаться клиент и сервер. Например, сервер B может послать строку "DATA BEGIN" с переводом строки на конце, затем послать данные в текстовом виде, так же разделенные символом перевода строки, потом "DATA END" как знак того, что данные окончились. Сервер A после приема всех данных может ответить, например, строчкой "RESULT: 47", передав таким способом результат. Протокол полностью зависит от твоих целей и задач.
Есть ошибка, которую любят совершать новички: НЕЛЬЗЯ передавать по сети структуры. Структуры могут по-разному выравниваться разными компиляторами, на разных компьютерах могут быть разные размеры слов и разный порядок слов в байте. Передавать любые бинарные данные по сети надо с большой остороностью.
Если все это кажется сложным, используй более высокоуровневые библиотеки, которые возьмут все заботы на себя. Например, какой-нибудь RPC или CORBA. Последняя не так сложна, как это может сначала показаться. Простые вещи в CORBA реализуются достаточно просто, но нет предела усложнению.
Спасибо за ответ.
> Судя по вопросу, до проблем авторизации тебе еще далеко, тебе
>надо реализовать протокол обмена данными.
Именно так. Я уже сделал авторизацию, но потом оказалось что база пользователей лежит на другом сервере. Теперь вот думаю как это реализовать.
>Тебе надо изобрести протокол, по которому будут общаться клиент и сервер. Например,
>сервер B может послать строку "DATA BEGIN" с переводом строки на
>конце, затем послать данные в текстовом виде, так же разделенные символом
>перевода строки, потом "DATA END" как знак того, что данные окончились.
> Сервер A после приема всех данных может ответить, например, строчкой
>"RESULT: 47", передав таким способом результат. Протокол полностью зависит от
>твоих целей и задач.
Мне то в принципе нужно что бы так: в одну сторону $login + $passwd, обратно "да-нет"+$users_alias+$email... и все, но похоже в реализации это выглядит гораздо сложнее :)>Если все это кажется сложным, используй более высокоуровневые библиотеки, которые возьмут все
>заботы на себя. Например, какой-нибудь RPC или CORBA. Последняя не
>так сложна, как это может сначала показаться. Простые вещи в CORBA
>реализуются достаточно просто, но нет предела усложнению.
Хм... нужно поглядеть что за Корба такая... может действительно с ее помощью что-то придумается
>>Если все это кажется сложным, используй более высокоуровневые библиотеки, которые возьмут все
>>заботы на себя. Например, какой-нибудь RPC или CORBA. Последняя не
>>так сложна, как это может сначала показаться. Простые вещи в CORBA
>>реализуются достаточно просто, но нет предела усложнению.
>Хм... нужно поглядеть что за Корба такая... может действительно с ее помощью
>что-то придумается
Поглядел... похоже это типа из Агромной пушки по мелкому воробью :)
>Именно так. Я уже сделал авторизацию, но потом оказалось что база пользователей
>лежит на другом сервере. Теперь вот думаю как это реализовать.Если нужно сделать правильно, то надо что-то вроде Kerberos. Популярно о принципах лежащих в его основе рассказывается здесь:
http://web.mit.edu/kerberos/www/dialogue.html
>Мне то в принципе нужно что бы так: в одну сторону $login
>+ $passwd, обратно "да-нет"+$users_alias+$email... и все, но похоже в реализации это
>выглядит гораздо сложнее :)Лущше $password не посылать. Логинящийся посылает $login, ему в ответ $random_string. Логинящийся посылает MD5($random_string . $password). Сервер проделывает то же самое, и если результат совпал то продолжает диалог иначе разрывает соединение.
>Лущше $password не посылать. Логинящийся посылает $login, ему в ответ $random_string.
> Логинящийся посылает MD5($random_string . $password). Сервер проделывает то же самое,
>и если результат совпал то продолжает диалог иначе разрывает соединение.
вот именно в этом и есть загвоздка - пока я не пойму КАК организовать вот это "передал строку - получил строку".
А насчет передавать пароль или его крипт - это уже детали. причем не шибко важные, ибо все это вертится во внутренней сети, и хаккеров пока не предвидится, юзеры MS Word с трудом осилили, им не до взлома :)
>>Лущше $password не посылать. Логинящийся посылает $login, ему в ответ $random_string.
>> Логинящийся посылает MD5($random_string . $password). Сервер проделывает то же самое,
>>и если результат совпал то продолжает диалог иначе разрывает соединение.
>вот именно в этом и есть загвоздка - пока я не пойму
>КАК организовать вот это "передал строку - получил строку".
>А насчет передавать пароль или его крипт - это уже детали. причем
>не шибко важные, ибо все это вертится во внутренней сети, и
>хаккеров пока не предвидится, юзеры MS Word с трудом осилили, им
>не до взлома :)
Не проще ли вместо текстовой БД пользовать нормальную (e.g. Postgres,MySQL). С ней не будет проблем соединится по IP-host,login,password
>Не проще ли вместо текстовой БД пользовать нормальную (e.g. Postgres,MySQL). С ней
>не будет проблем соединится по IP-host,login,password
Нет, не проще: база уже давно работает, переписывать базу - это жуткое дело, ибо она используется еще рядом других задач.
Эх, если б можно было MySQL базу заюзать, разве весь этот сыр-бор я поднимал? :(
Короче. Делается всё элементарно - ищется модуль для системной библиотеки с названием, типа nss-mysql (для постгреса такой есть), ставится в систему, строится на предмет сервера и таблиц и вообще забывается про то, как кто где авторизуется.Серьезный минус только один - нужно заставить PAM при создании пользователя шифровать пароль и засылать его на SQL-сервер. Ручной метод "втупую" - "adduser xxx; passwd xxx", заглядываем в /etc/shadow, берем оттуда пароль, сами запросом втыкаем его в нужную таблицу, "userdel xxx; rm -rf /home/xxx".
Плюсы:
a) в /etc/passwd, /etc/group и /etc/shadow никаких ссылок на пользователей нет - всё лежит в базе;
б) пароли от запрашивающей программы никуда не передаются - от сервера поступает пароль в зашифрованном виде в соответствии с системой, в которой его шифровали (в линухе в последнее время обычно MD5). То есть открытые пароли по сети вообще не бегают;
в) данные по соответствию uid/gid символьным именам пользователей и групп доступны любой программе. Если не использовать nss, то половина серверных программ вообще работать не будет. Например, почтовые сервисы;
г) имеется масса потенциальных возможностей, которых нет в стандартных средствах *nix.
Я пол-года назад почти дописал сетевую авторизации со своим протоколом (дабы отвязать клиента от конкретной SQL-базы вообще - к ней привязана только серверная часть) с использованием механизма NSS и базы PostgreSQL. Отлично работал. За основу клинетской части был взят тот самый nss-pgsql и капитально переписан.
Сейчас потихоньку переделываю всё это хозяйство - сервер перевел на потоки с порционным созданием в зависимости от нагрузки (аля NetWare), наворотил конфигурацию (в принципе, можно создавать группы в группах, чего нельзя делать стандартными средствами *nix), имеется возможность раздавать свои данные разным сервисам (например, зачем команде "ls -l" посылать пароль, если ей нужно только имя пользователя и группы узнать) на разных машинах, имеется возможность кеширования на стороне клиента и на стороне сервера (на случай частых запросов одного и того же; например, "ls -l ~/"). Есть некоторые мысли по поводу защиты от DoS-атак. Еще некоторые улучшения будут. Планирую в ближашие пару месяцев таки добить это дело и пустить в работу на офисе, если времени будет достаточно. А то совсем загрузили фигней всякой :)
>Серьезный минус только один - нужно заставить PAM при создании пользователя шифровать
>пароль и засылать его на SQL-сервер. Ручной метод "втупую" - "adduser
>xxx; passwd xxx", заглядываем в /etc/shadow, берем оттуда пароль, сами
Еще раз уточняю: _без_ SQL нужно, база хранится на втором сервере в текстовом формате! Вот я хотел написать на перле некую двойку: одна часть на веб-сервере из формы получает имя-пароль, обращается ко второй части (на другой сервер) и каким-то макаром (вопрос безопасности пока не рассматриваю) вторая часть сравнивает с текстовой базой. Собственно сам процесс авторизации - это отлажено для случая, если база юзеров лежит на одной машине с веб-сервером. Теперь нужно изобрести как это разнести на разные машины. Причем, средства ограничены - на веб-сервере провайдер никаких наворотов ставить не согласится (поэтому все прийдется делать обычным перловым скриптом), да и на втором сервере тоже ничего в принципе воротить нельзя, тоже максимум перловый скрипт, или может сишная программа небольшая.
Буду очень признателен если у кого-то найдется пример типа: две простенькие программки: (1) слушает непрерывно и в случает получения строки отвечает отправкой другой строки, (2) отправила строку и получила ответную. И все. Да, желательно все это на Perl. Или крайний случай: программа (1) - Си, (2) - перл
Заранее признателен, Константин
или thttpd?
да и на перле много демонов написано, webmin, например
>да и на перле много демонов написано, webmin, например
хм, webmin нужно поглядеть, ща займусь.
Хотя уже частично решил проблему, теперь другие грабли:
отправляю строку, сервер ее получает (могу ее в переменную загнать и обрабатывать), сервер отвечает что-то, клиентский скрипт получает, могу вывести на print, а вот в переменную - не могу, не вижу ее снаружи:#!/usr/bin/perl
use Socket;$port = 4000;
$them = '192.168.1.100';$SIG{'Int'} = 'dokill';
sub dokill {
kill 9, $child if $child;
}#Нужно отправить функции строку, в ответ получить другую.
$zapros = "test\n";
$otvet=autoriz($zapros);
print "Otvet = $otvet\n\n";
sub autoriz {chop($hostname = `hostname`);
($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyname($port,'tcp')
unless $port =~ /^\d+$/;print "Using port $port to connect to server on host $them...\n\n\n";
($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname);
($name, $aliases,$type,$len,$thataddr) = gethostbyname($them);
if (socket(S,AF_INET, SOCK_STREAM, $proto)) {
# print "Socket creation succeeded.\n";
}
else {
die $!;
}$sockaddr = 'S n a4 x8';
$this = pack($sockaddr, AF_INET, 0, $thisaddr);
$that = pack($sockaddr, AF_INET, $port, $thataddr);if (bind(S, $this)) {
# print "Bind succeeded.\n";
}
else {
die $!;
}# This is similar to the 'accept' in the server, except the server is
# waiting for a call and we're actively initiating the conversation.
# 'connect' connects our socket to the server's socket on the other end.if (connect(S, $that)) {
# print "Connect succeeded.\n";
}
else {
die $!;
}
# Force our socket to flush output immediately after a printselect(S);
$| = 1;select(STDOUT);
if ($child = fork) {
#Отправляю серверу строку - он ее получает
send (S, $_[0], 0);
# Sleep for 3 seconds then...
sleep 3;
} else {
# Вот тут планирую ответ сервера в переменную запихнуть
while (<S>) {
#сервер получил строку "test" и ответил "OK"
print "Server: $_";
# на экране вижу Server: OK
$tempper="$_\n\n";
}
}return "Repl: $tempper\n";
#А получаю в ответ из функции только "Repl:", т.е. $tempper = ""
}и где ж тут грабли то?
ошибка здесь:
$tempper="$_\n\n";
делай, например, так:
$tempper=$tempper."$_\n\n";
а то у тебя в цикле результат не накапливается
>ошибка здесь:
>$tempper="$_\n\n";
>делай, например, так:
>$tempper=$tempper."$_\n\n";
>а то у тебя в цикле результат не накапливается
беда не в том, что не накапливался, а в том, что он принципиально не возвращался из процедуры :)
но уже найден простой вариант#!/usr/bin/perl -w
# gethttp.pluse Socket;
use strict;@main::otvet=autoriz();
print @main::otvet;sub autoriz {
my $serveraddr="127.0.0.1";my $sock_name = GetSockName($serveraddr,4000)
or die "Couldn't convert $serveraddr into an Internet address: $!\n";socket(CONN,PF_INET,SOCK_STREAM,getprotobyname('tcp'));
connect(CONN,$sock_name)
or die "Couldn't' connect to $serveraddr: $!\n";select(CONN); $|=1; select(STDOUT);
print CONN "1\n\n";
my @body = <CONN>;
close(CONN);return @body;
}sub GetSockName{
my ($nm,$pt) = @_;
return undef unless defined($nm);
return undef unless defined($pt);
return undef unless $nm = gethostbyname($nm);
return sockaddr_in($pt,$nm);
}
> перловый скрипт, или может сишная программа небольшая.
>Буду очень признателен если у кого-то найдется пример типа: две простенькие программки:
>(1) слушает непрерывно и в случает получения строки отвечает отправкой другой строки,
>(2) отправила строку и получила ответную. И все.Попробую вырезать из своего демона...
use IO::Socket;
use strict;my $client = "";
my $bindaddr = "127.0.0.1";
my $serverport = 12345;while (1) {
get_input();
}
sub startsrv {
my $server=IO::Socket::INET->new(LocalAddr=>$bindaddr,
LocalPort=>$serverport,
Type=>SOCK_STREAM,
Reuse=>1,
Listen=>10) or die "Couldn't be a TCP server on port $serverport";
return $server;
}sub getinput {
while ($client = $server->accept()) {
die "Can't fork!: $@" if (!defined (my $child=fork()));
if ($child == 0) {
# find client ip address
my $haddr = getpeername($client);
my ($port,$iaddr) = unpack_sockaddr_in($haddr);
my $in_ip = inet_ntoa($iaddr);
# Send "wellcome banner" to CLIENT
$client->print("Wellcome to example TCP server (Your ip: <$in_ip>)\n");
my $cmd = $client->getline; # Recive incoming string from CLIENT
# Do something with recieved string
.....
# Send some datas to CLIENT
$client->print("....\n");
close $client;
exit 0;
} else {
close $client;
}
}
}читай perldoc IO:Socket;
для клиента все с точностью до наоборот. Ну и форкаться не надо, сомо собой... :-)
Насчет шифрования паролей:
RFC 2138 - Radius
Смотри сюда
http://www.opennet.me/openforum/vsluhforumID9/1913.html
Тут то что тебе нужно
слева оракл, справа pam (это тебе не надо)
а посередине сокет сервер и сокет клиент которые пароль шифрованный гоняют туда сюда. Реализация с ошибками, есть у меня без ошибок, уже исправил.
Попробуй эту, если понравится, скину новую версию
Ты выложи куда-нибуль которая без ошибок.
>Ты выложи куда-нибуль которая без ошибок.Что выложить без ошибок?
Да этоо я к chepil обращался.
Он нарисовал PAM для работы с ORACLE, были у него кой какие глюки, которые он и вылечил.
вот и говорю чтобы куда-нибудь выложил свое творчество.
http://chepil.surgut.ru