Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Perl: Общие вопросы > проблема записи хэндлов в глоабальный хэш ? |
Автор: zerg13new 25.3.2010, 13:42 |
народ, ОЧЕНЬ нужна помощь, так как я уже устал и не понимаю совсем ничего пишу в linux на perl и цель программы такова: создать сервер, кот принимал бы соединения от клиентов и мог посылать любому выбранном на сервере клиенту сообщение. пишу под консоль, итак код той части что не получается: # my server 2 # 2010.03.05 3 4 #!/usr/bin/perl -w 5 6 use strict; 7 use IO::Socket; 8 #use Thread; 9 use threads; 10 use threads::shared; 11 #use POSIX 'WNOHANG'; # for SIGNAL 12 use constant BUFSIZE => 1024; 13 14 15 my %clients :shared; 16 my( $addr, $port ); 17 18 19 $addr = &get_ip(); # get IP from interface 20 21 # get port 22 print "enter port: "; 23 while( <STDIN> ) { 24 chomp($_); 25 $port = $_; 26 if ( /[0-9]{4,5}/ && $_ > 1024 ) { last; } 27 print "either wrong value or port is deny, enter new value: "; 28 } 29 30 my $server = IO::Socket::INET -> new( LocalAddr => $addr, 31 LocalPort => $port, 32 Listen => 255, 33 Type => SOCK_STREAM, 34 Proto => 'tcp', 35 Reuse => 1 ); 36 die "can't create, bind or listen: $!\n" unless $server ; 37 print "socket OK\n bind OK\n listen OK\n Server started at $addr:$port\n"; 38 warn "Server ready. Waiting for connections... \n"; 39 40 threads -> create( \&to_client ) || die "$! \n"; # STDIN 41 42 # start threads to treat querys 43 while( my $client = $server -> accept ) { 44 my $client_ip = $client -> peerhost(); 45 my $client_port = $client -> peerport(); 46 my $tmp = "$client_ip:$client_port"; 47 48 %clients = { %clients , "$client_ip:$client_port" => $client } ; # add new socket to %client ИМЕННО ЗДЕСЬ # здесь никакого кода, просто пустота 56 syswrite( $clients{ "$client_ip:$client_port" }, "hi, how are you??") || die ":$! _ $@"; 57 #foreach my $q ( keys %clients ) { print "debug0: $q\n"; } 58 print "got a connection from: $client_ip:$client_port $client\n"; 59 $client -> autoflush; 60 61 threads -> create( \&from_client, $client, $client_ip, $client_port ) || die "$! \n"; #STDOUT 62 63 } 64 exit 0; 65 В итоге я получу ошибку "Invalid value for shared scalar at server.pl line 48, <STDIN> line 1." Хотелось бы прочитать совет спецов, показывающий наглядно как положить мне хэндл сокета $client в расшареный хэш %clients и как потом его оттуда правильно извлечь, чтобы он считался хэндлом. Если не сложно, то не отказался бы и от обьяснений где и почем же я балбес. Я честно пробывал множество всевозможных ссылок, разыменований, читал документацию типо http://perldoc.perl.org/threads/shared.html но ничего толкового не получилось. |
Автор: Pfailed 25.3.2010, 14:08 | ||||
Это что-то странное. Попробуйте проще:
|
Автор: zerg13new 25.3.2010, 15:48 |
пробывал и так, всё равно будет ошибка при выполнении программы. Вообще как я заметил, ошибка появляется как только хэш %clients становится глобальным. То есть я делал массив хэндлов и запихивал в них хэндлы push_ом без проблем, но как только обьявлял массив shared, то тут же появлялась эта ошибка |
Автор: DurRandir 25.3.2010, 15:57 |
>use threads; >use threads::shared; О нет, нет, нет |
Автор: zerg13new 25.3.2010, 16:33 | ||
как насчёт обьяснить чего не нравится или есть ли какие предложения дельные ?? рассмотрю с удовольствием, а то я уже ![]() |
Автор: mvsgt 25.3.2010, 16:38 | ||
"нет нет нет" работает нормально, только очень тормозит если злоупотреблять. Почему бы не сделать простой форкающий сервер - модулей для *daemon* много. Добавлено через 3 минуты и 30 секунд
это непонятно что. и, кажется, с shared хэшами нельзя так рабоать вообще - только поэлементно. в инструкции про это что-то есть. |
Автор: mvsgt 25.3.2010, 17:08 |
никак нельзя, я думаю. дескрипторы так не распространяются. А вот под windows как раз может и получиться - там форки тредами эмулируются. Но всё равно это муторно и ненадёжно. Надо ещё раз задачу обдумать. Может вообще нужно что-то типа comet. |
Автор: DurRandir 25.3.2010, 18:17 |
Основная проблема с потоками - вам _кажется_, что всё очевидно. А это не так. Вот вы помянули некие блокировки (для форка) - а для потоков они вам, видимо, и не нужны? Ну, флаг в руки. Для эффективного использования сетевого I/O - посмотрите на AnyEvent/EV - последние версии вполне собираются под strawberry perl. |
Автор: zerg13new 25.3.2010, 18:37 |
DurRandir, пока что я исхожу из того, что: 1. я могу передать в поток хэндл сокета как аргумент (вначале я просто передавал аргументом потоку именно хэндл $client и поток с ним нормально работал, следовательно в поток передать хэндл можно и он будет действительный и с ним работает ) 2. я могу создать расшаренный для потоков хэш (я указывал хэндл для хэша как "$client" и он его спокойно записывал, и поток читал из хэша, но хэндлом уже не считал(вдать изменяет контекст из-за кавычек), а потому преполагаю пункт 1) ТО наверное можно как-то совместить: создав что-то глоабльное для всех потоков, откуда потоки смогут спокойно читать, к примеру попытать хэш. И только из-за этих 2х пунктов я верю, что я просто чего-то недопонимаю и потому никак не могу записать нормально хэндл в хэш, и жду от вас победы над моей проблемой ![]() |
Автор: mvsgt 25.3.2010, 18:41 |
в линуксовом перле потоки вообще есть? |
Автор: ginnie 25.3.2010, 18:47 |
zerg13new, быстрым поиском нашел http://perlmonks.org/?node_id=395513 на PerlMonks о доступе к сокетам из потоков. Посмотрите, может найдете что-нибудь полезное. Сам я подобными задачами не занимался, так что конкретно ничем помочь не могу. |
Автор: zerg13new 25.3.2010, 19:20 |
mvsgt, безусловно, как и в виндовой версии strawberry perl ginnie, спасибо, прочёл, попробуем обмозговать.. А вообще этот блог читается на одном дыхании как художественная книжка, здорово и эмоционально написано )) кстати вот попробывал ещё такой вариант: $clients{ "$client_ip:$client_port" } = *client; # add new socket to %client print ref($client) . "++ $client\n"; 49(номер строки) syswrite( *{$clients{"$client_ip:$client_port"}}{IO}, "hi, how are you??") || die ":$! _ $@"; вывод: IO::Socket::INET++ IO::Socket::INET=GLOB(0x8a399d8) Can't use string ("*main::client") as a symbol ref while "strict refs" in use at server.pl line 49, <STDIN> line 1. |
Автор: gcc 25.3.2010, 19:53 | ||||
может хэш сделать:
http://perldoc.perl.org/perlref.html#Making-References
|
Автор: zerg13new 25.3.2010, 20:42 | ||
gcc,
спасибо, сделал уже так: $clients{ "$client_ip:$client_port" } = *client{IO}; # add new socket to %client да вот только не знаю как же извлечь то хэндл (не тот либо неопределно значние либо ещё что-нибудь) для syswrite( (вот уж как здесь не изголяюсь с обзыванием), "hi, how are you??") || die ":$! _ $@"; есть идеи как излечь из хэша ?? а то он не хочет... |
Автор: gcc 25.3.2010, 20:47 | ||
может надо посмотреть... дайте вывод |
Автор: ginnie 25.3.2010, 20:49 |
zerg13new, *client{IO} у Вас undef потому, что для сокета Вы используете лексическую переменную, а указанная конструкция работает только с динамическими (т.е. глобальными переменными). |
Автор: ramus 25.3.2010, 21:45 |
Вы не читали книгу "Разработка сетевых программ на PERL" Линкольн Д.Штайн ? В этой книге подробно рассматриваются все аспекты написания сервера (включая неблокирующийся ввод-вывод и демонизацию). Очень рекомендую. Почерпнете очень многое. Она у меня есть как в бумажном виде так и в электронном. Весит 20МБ. Могу выслать куда нибудь... |
Автор: zerg13new 26.3.2010, 11:27 |
пока кода ввода в маси и вывода выглядит так $clients{ "$client_ip:$client_port" } = *client{IO}; # add new socket to %client syswrite( $clients{"$client_ip:$client_port"}, "hi, how are you??") || die ":$! _ $@"; gcc, Can't use an undefined value as a symbol reference at server.pl line 50, <STDIN> line 1. HASH(0x8a923f8)Perl exited with active threads: ginnie, я на сайтах читал, что так получают. примеры были таковы $scalarref = *s{SCALAR}; # Ссылка на скалярную переменную $arrayref = *s{ARRAY}; # Ссылка на массив скаляров $hashref = *s{HASH}; # Ссылка на ассоциативный массив $coderef = *s{CODE}; # Ссылка на подпрограмму $ioref = *s{IO}; # Ссылка на дескриптор файла, сокета $globref = *s{GLOB}; # Ссылка на все запись не спорю что я могу неправильно потом их вытаскивать, но я уже как только не пробую ![]() ramus, вот с неё то и начинал, причём примеры те кот он приводит с глоабыльными переменными уже не работают,потому как с версии 5.6 или более поздней глобальные переменные не разделяются просто так, для этого нужны спец модули... вот потому пришлось отказаться от этой книги, хотя как основу я её прочёл одну из первых для сетевого взаимодействия так же я прочёл/пролистал: Ellie Quigley - Perl by Example Lincoln D Stein - network programming with Perl Холзнер - Perl Специальный справочник 2001 |
Автор: ginnie 26.3.2010, 12:54 | ||||||||
zerg13new, поясню свою мысль:
правильно работает для глобальной переменной $s
У Вас в коде используется лексическая переменная $client
а здесь уже используется глобальная переменная $client (угадайте, что записано в таблице символов для client в файловом дескрипторе - undef)
Теперь я понятно объяснил (сомневаюсь :о)? |
Автор: ginnie 26.3.2010, 13:27 | ||
По поводу конкретных советов: наиболее верным мне видится вариант из первого сообщения, похоже проблема в том, что неявный файловый дескриптор $client неразделяемый (not shared). Можно попробовать сделать его разделяемым при помощи share($client). Сработает такой вариант или нет - не знаю, особенно учитывая что в описании ни слова о indirect filehandles
Других вариантов с разделением между потоками набора неявных файловых дескрипторов я пока придумать не смог. |
Автор: DurRandir 26.3.2010, 13:53 |
А, мне тут пришла в голову идея. Раз уже вам очень-очень хочется это сделать на потоках, то передавайте в поток fileno($handle), а в принимающем потоке воссоздавайте объект через new_from_fd (метод из IO::Handle). Но если это под win - то надо проверять работоспособность. |
Автор: zerg13new 26.3.2010, 17:13 |
ginnie, спасибо, всё ясно о чём вы ![]() DurRandir, почитаем о реализации такой идеи и если ничего не выйдет с более вышестоящим советом, то обязательно попробуем, спасибо |
Автор: zerg13new 3.4.2010, 17:06 |
Проблема решена, но лично у меня только в Linux (debian ) на perl v 5.10.0: Первоисточник, где я общался по поводу проблемы, как использовать массив хэндлов между потоками в сервере по обмена сообщениями из консоли здесь: http://perlmonks.org/index.pl?node_id=830907 В кратце суть решения проблемы такова оказалась: ## В ОСНОВНОМ потоке вот так записываем хэндлы в хэш while( my $client = $server -> accept ) { ## $clients{ "$client_ip:$client_port" } = fileno( $client ); ## здесь кладём в хэш хэндл сокета (точнее некий его заменитель, число) ## } В ДОЧЕРНЕМ потоке, которому надо работать с этими хэндлами из этого хэша: my $client; my $fileno = $clients{$clnt_ip_port}; open $client, "+<&$fileno" || die "$!\n ВСЁ народ, всем ОГРОМНОЕ спасибо за то что живо отреагировали на мои проблемы и дали дельные советы. Вы мне помогли очень-очень. Именно за такие сообщества я и обожаю инет ![]() |