ecshop编辑完毕商品点击js判断表单提交完毕的时候提示fail to copy file: D:\web\ftp信息\web

Javascript Disabled Detected
You currently have javascript disabled. Several functions may not work. Please re-enable javascript to access full functionality.
Parse error: syntax error, unexpected $end in C:\xampp\htdocs\shop_sppc\includes\boxes\specials.php on line 56
Started by , Oct 20
Please log in to reply
13 replies to this topic
I have a brand new installation of oscommerce 2.2
and just 1 contribution: Separate Price per Customer.
All I did was copying the catalog from SPPC into the standard catalog.
But it gave me a lot of strange results. One is this:
Parse error: syntax error, unexpected $end in C:\xampp\htdocs\shop_sppc\includes\boxes\specials.php on line 56
I'm not a specialist - but I can't see what's wrong with this last line.
Any help???
Thanks!
You don't have a syntactical error. I'd say more of a logical error.It says:Parse error: syntax error, unexpected $endMaybe missing a &}&?Post the contents of the file and I'll try to help.
If I suggest you edit any file(s) make a backup first - I'm not perfect and neither are you.&Given enough impetus a parallelogramatically shaped projectile can egress a circular orifice.&- Me -Like this post? &Like& it again over there &
There is no line 56 in that file. It only has 53...
The stock file has 35 lines. Looks like he/she can't be bothered to post the code as suggested by germ.
The Coopco Underwear ShopIf you live to be 100 years of age, that means you have lived for 36,525 days. Don't waste another, there aren't many left.
It's the original file of the Separate price per c I didn't change anything:&?php/*
$Id: specials.php,v 1.31
22:21:03 hpdl Exp $
adapted for Separate Pricing Per Customer v4
osCommerce, Open Source E-Commerce Solutions
Copyright & 2003 osCommerce
Released under the GNU General Public License*/// BOF Separate Pricing Per Customer//
global variable (session): $sppc_customers_group_id -& local variable $customer_group_id
if (isset($_SESSION['sppc_customer_group_id']) && $_SESSION['sppc_customer_group_id'] != '0') {
$customer_group_id = $_SESSION['sppc_customer_group_id'];
$customer_group_id = '0';
if ($customer_group_id == '0')
$random_product = tep_random_select(&select p.products_id, pd.products_name, p.products_price, p.products_tax_class_id, p.products_image, s.specials_new_products_price from & . TABLE_PRODUCTS . & p, & . TABLE_PRODUCTS_DESCRIPTION . & pd, & . TABLE_SPECIALS . & s where p.products_status = '1' and p.products_id = s.products_id and pd.products_id = s.products_id and pd.language_id = '& . (int)$languages_id . &' and s.status = '1' and s.customers_group_id = '0' order by s.specials_date_added desc limit & . MAX_RANDOM_SELECT_SPECIALS);
} else { // $sppc_customer_group_id is in the session variables, so must be set
$random_product = tep_random_select(&select p.products_id, pd.products_name, IF(pg.customers_group_price IS NOT NULL,pg.customers_group_price, p.products_price) as products_price, p.products_tax_class_id, p.products_image, s.specials_new_products_price from & . TABLE_PRODUCTS . & p, & . TABLE_PRODUCTS_DESCRIPTION . & pd, & . TABLE_SPECIALS . & s LEFT JOIN & . TABLE_PRODUCTS_GROUPS . & pg using (products_id, customers_group_id) where p.products_status = '1' and p.products_id = s.products_id and pd.products_id = s.products_id and pd.language_id = '& . (int)$languages_id . &' and s.status = '1' and s.customers_group_id= '&.$customer_group_id.&' order by s.specials_date_added desc limit & . MAX_RANDOM_SELECT_SPECIALS);
if (tep_not_null($random_product)) {// EOF Separate Pricing Per Customer?&<!-- specials //-->
$info_box_contents = array();
$info_box_contents[] = array('text' =& BOX_HEADING_SPECIALS);
new infoBoxHeading($info_box_contents, false, false, tep_href_link(FILENAME_SPECIALS));
if ($random_product = tep_random_select(&select p.products_id, pd.products_name, p.products_price, p.products_tax_class_id, p.products_image, s.specials_new_products_price from & . TABLE_PRODUCTS . & p, & . TABLE_PRODUCTS_DESCRIPTION . & pd, & . TABLE_SPECIALS . & s where p.products_status = '1' and p.products_id = s.products_id and pd.products_id = s.products_id and pd.language_id = '& . $languages_id . &' and s.status = '1' order by s.specials_date_added desc limit & . MAX_RANDOM_SELECT_SPECIALS)) {
new SpecialsBoxHeading($info_box_contents, false, false, tep_href_link(FILENAME_SPECIALS));
$info_box_contents = array();
$info_box_contents[] = array('align' =& 'center',
'text' =& '&a href=&' . tep_href_link(FILENAME_PRODUCT_INFO, 'products_id=' . $random_product[&products_id&]) . '&&' . tep_image(DIR_WS_IMAGES . $random_product['products_image'], $random_product['products_name'], SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT) . '&/a&&br&&a href=&' . tep_href_link(FILENAME_PRODUCT_INFO, 'products_id=' . $random_product['products_id']) . '&&' . $random_product['products_name'] . '&/a&&br&&s&' . $currencies-&display_price($random_product['products_price'], tep_get_tax_rate($random_product['products_tax_class_id'])) . '&/s&&br&&span class=&productSpecialPrice&&' . $currencies-&display_price($random_product['specials_new_products_price'], tep_get_tax_rate($random_product['products_tax_class_id'])) . '&/span&');
new infoBox($info_box_contents);?&
&/tr&<!-- specials_eof //-->&?php
Yes, and it only has 53 lines, there is no line 56. Make sure your FTP client is in text/ascii mode when uploading the php files. Perhaps it is corrupted so upload it again.
Well... It has 6 {, but only 5 }, so there's your &Parse error: syntax error, unexpected $end&...Sometimes it's not too easy to tell where the omitted one belongs....
At the end of the file, change this:<?php
?>to:<?php
If I suggest you edit any file(s) make a backup first - I'm not perfect and neither are you.&Given enough impetus a parallelogramatically shaped projectile can egress a circular orifice.&- Me -Like this post? &Like& it again over there &
You are right, but since he said this was an original file from the package I compared the file now and found two additional lines that are not in the file I added to the package. Checked it again by downloading and comparing it and indeed. These two lines after new infoBoxHeading($info_box_contents, false, false, tep_href_link(FILENAME_SPECIALS)); are not in the original SPPC file:if ($random_product = tep_random_select("select p.products_id, pd.products_name, p.products_price, p.products_tax_class_id, p.products_image, s.specials_new_products_price from " . TABLE_PRODUCTS . " p, " . TABLE_PRODUCTS_DESCRIPTION . " pd, " . TABLE_SPECIALS . " s where p.products_status = '1' and p.products_id = s.products_id and pd.products_id = s.products_id and pd.language_id = '" . $languages_id . "' and s.status = '1' order by s.specials_date_added desc limit " . MAX_RANDOM_SELECT_SPECIALS)) {
new SpecialsBoxHeading($info_box_contents, false, false, tep_href_link(FILENAME_SPECIALS));
That would explan the extra {
Edited by germ, 21 October 2007 - 12:46.
If I suggest you edit any file(s) make a backup first - I'm not perfect and neither are you.&Given enough impetus a parallelogramatically shaped projectile can egress a circular orifice.&- Me -Like this post? &Like& it again over there &
T but the result is just this error:Fatal error: Class 'SpecialsBoxHeading' not found in C:\xampp\htdocs\shop_sppc\includes\boxes\specials.php on line 42and just to be complete: in the admin menu &catalog& I get this result:Fatal error: Call to undefined function tep_hide_session_id() in C:\xampp\htdocs\shop_sppc\admin\categories.php on line 843Maybe is the easiest way if someone tells me where I can download the complete contribution without any changes like added lines.Thanks
Jan knows infinitely more than I on this subject (he made the latest contribution on this), so I'll gracefully bow out....
If I suggest you edit any file(s) make a backup first - I'm not perfect and neither are you.&Given enough impetus a parallelogramatically shaped projectile can egress a circular orifice.&- Me -Like this post? &Like& it again over there &
You would need to remove those extra two lines I mentioned.and just to be complete: in the admin menu &catalog& I get this result:Fatal error: Call to undefined function tep_hide_session_id() in C:\xampp\htdocs\shop_sppc\admin\categories.php on line 843Maybe is the easiest way if someone tells me where I can download the complete contribution without any changes like added lines.As far as I'm aware the only place to download SPPC is from the . Where did you find this &unauthorized& version?As a side note, the &pre-installed& files in the latest version of SPPC are for MS 2.2 RC1. Using those files on older osCommerce versions will undoubtedly give errors in certain places.
thanks to everybody - I'll try
I found this package here:
Version from august, 27.
There is no version dated August 27, only August 24 and 29 (the last one is the one for RC1).
Reply to quoted posts&&&&& &&&
Username or email:
Forum Password
Remember me
This is not recommended for shared computersFileZilla FTP Server安装设置教程
随着宽带网的普及,自己架设FTP服务器进行文件交流的用户越来越多。一提到FTP服务器软件,一般用户首先想到的是大名鼎鼎的Serv-U。使用主流FTP
Server存在一个问题:一旦那个软件有什么安全漏洞被公布,由于它的知名度,那么这个安全漏洞也将人尽皆知,这样服务器的安全风险将很大。其实除了这些主流软件之外,还有很多相对不太出名的FTP
Server,使用这些比较冷门的FTP Server软件,上述风险相对比较小一些。这里介绍的FileZilla
Server是一款免费的开放源代码的软件,具有一般FTP服务器的绝大多数功能。软件支持用户分组;支持流量限制,可以定制按时段的流量限制;支持MODE Z
FTP协议;支持远程配置管理……
安装和设置也比较简单。
首先到官方网站
http://sourceforge.net/project/showfiles.php?group_id=21558
下载最新版本,然后双击安装。出现许可协议,选择“I
Agree”同意协议继续……
在这一屏,中间的端口号是管理员登录配置服务器的连接端口号,默认为14147。这个设置还关系到远程登录配置。可以根据自己的情况修改,当然也可以在安装完成之后再修改。
点击Close结束安装,之后弹出连接服务器的窗口。FileZilla
Server支持远程配置,如果是从远程进行配置的话,这里的地址、端口号和口令要跟远程服务器上面的对应起来。
本例是从本地登录,密码正确无误之后就进入服务器设置页面。
首先要进行服务器全局参数设置:点“Edit”菜单,选“Settings”
settings(常规设置):
Listen on Port:监听端口,其实就是FTP服务器的连接端口。
Max.Number of
users:允许最大并发连接客户端的数量。
Threads:处理线程。也就是CPU优先级别。数值调得越大优先级越高,一般默认即可。
下面的是超时设置,自上至下分别为:连接超时、传输空闲超时、登入超时。单位为秒。
message页面设置客户端登录成功以后显示的Welcome信息。建议不要用软件默认的,因为任何软件都不能保证没有什么漏洞,如果在这里暴露软件名称的话,一旦这个FTP
Server软件有什么安全漏洞,别有用心的人知道了服务器软件的名称就可能针对性地发起攻击。所以建议这里设置的信息不要包含任何服务器资料。
Filter(IP过滤器)页面,设置IP过滤规则,在上面栏目中的IP是被禁止的,下面的是允许的。
Passive mode
settings(被动传输模式设置):这个页面要重点关注。如果服务器本身直接拥有公网IP,可以选软件默认的“Default”。
倘若服务器是在局域网里面,在一个网关后面,那么就要选择第二项“Use
the follwoing
IP”,并且在下面的输入栏填写公网的IP地址;否则,客户端用PASV被动模式可能无法连接FTP服务器。因为服务器是在内网中,在客户端使用PASV模式连接服务器的时候,服务器收到连接请求之后需要把自身的IP地址告诉客户端,由于服务器在内网中,它侦测到的IP地址是内网的(如192.168.0.5),它把这个IP地址交给客户端,客户端自然无法连接。在这里设置了指定的IP地址后,服务器就会把这个公网合法的IP地址提交给客户端,这样才能正常建立连接。
如果服务器是动态IP的,那么可以选择下面的“Retrieve
external IP address
from”,利用FileZilla官方网站免费提供的IP查询页面获取当时的公网合法IP,然后服务器把这个公网合法IP地址提交给客户端。当然静态IP也可以用这个,只不过没有必要。
这个设置页面对服务器位于内网的情况非常重要。有些FTP服务器端没有这个设置项目,客户端就只能用Port主动模式连接。当然有些客户端软件针对这个问题有专门的设置,如FlashFXP的站点设置中只要选中“被动模式使用站点IP”就可以了。
对于在局域网中的服务器,如果服务器没有置于DMZ区,那么强烈建议选中下面的“Use
custom port
range”定义PASV端口范围。由于PASV模式中,是服务器随机打开端口,然后把打开的端口号告诉客户端,让客户端连接打开的端口。但是因为服务器处于网关后面,如果网关那里没有做对应的端口映射,客户端从外网就无法连接服务器打开的端口,导致PASV模式连接失败。在这里限定服务器打开的端口范围,然后到连接外网的网关那里,对服务器的这些端口做端口映射(虚拟服务)。这需要服务器和Internet网关设备配合设置,这样外网的客户端才能用PASV模式连接进来。
settings(安全设置):这里的两个选项关系到能否FXP。软件默认状态“Block incoming server-to-server
transfers”和“Block outgoing server-to-server
transfers”两项都是选中的,前面那项是禁止连入的服务器对传,后面是禁止传出的服务器对传。也就是说默认状态不允许FXP,如果需要使用FXP,那么就把这两个项目取消选择。注意FXP传输除了跟这个页面的设置有关,还跟IP过滤器有关。
Admin Interface
setting(管理员界面设定):这个就是登录配置服务器界面的一些参数。端口号的设置在安装的时候也出现过。下面两栏可以定义允许远程登录配置的网络界面和IP地址。在最下面更改管理员口令。
Logging(日志):设定是否启用日志记录功能以及日志文件大小和文件名。
Speed Limits(速度限制):这个是全局参数,默认状态不限速。可以选中“Constant Speed Limit
of”并填写限速数值来实现速度限制,下载(传出)和上传(传入)可以分别设置。还可以根据时段自定义限速规则——“Use Speed Limit
Rules”,比如这台服务器或者网络连接除了做FTP服务器之外还有别的用途,需要根据时间调度,不能让FTP传输挤占所有网络带宽影响其它的网络服务;就可以通过这里设置。
Filetransfer compression(文件传输压缩设置):MODE Z
FTP协议是一种实时压缩的传输协议。在这种模式下,发送方的数据在发出之前先进行压缩,再送到网络链路中传输,接收方将收到数据实时解包,在本地还原重组成原文件。这种模式可以大幅度减少网络中的数据流量,提升传输效率(速度)。当然对于已经压缩过的文件,就几乎没有效果了。要使用这种传输模式,需要服务器端和客户端都支持MODE
勾选“Enable MODE Z support”就可以启用本服务器的MODE Z支持功能,这样,只要客户端也支持MODE
Z就可以获得它带来的性能提升。“Minimum allowed compression level”和“Maximum allowed compression
level”分别设置最小压缩率和最大压缩率。最下面可以输入不启用MODE
Z功能的目标IP。
完成这些设置以后,点击“OK”按钮保存设置并退出服务器全局设置页面返回主界面。
下面要进行的是用户组(Group
Settings)设置。在主界面点击第五个按钮或者由“Edit”——“Groups”菜单进入。
组设置是为了便于用户归类管理,相同权限的用户归属到同一个组里面,这样就不用重复多次设置每个用户的权限等参数,简化配置和管理工作。点击右边的“Add”按钮创建新组。
组创建完成以后,点“Shared
folders”进入目录权限设置页面。点击中间区域的“Add”按钮添加目录。默认状态添加的第一个目录即为该组用户登录之后看到的主目录(Home
Directory),主目录前面有个粗体的“H”标识。目录列表右侧分别是对该目录的操作权限设置,上面是文件权限设置,下面是目录权限设置。如果要改变主目录,只要在列表中选中需要设置为主目录的那个,然后点击“Set
as home dir”按钮即可。
设置好主目录之后,再点击“Add”按钮把其余的目录依次设置进来就可以了。不过这里得注意,如果仅仅把别的目录添加进去,那么你用客户端连接之后,会发现除了主目录和它的子目录之外,别的目录都看不见。这是怎么回事?这里要说明一个概念——虚拟路径。所谓虚拟路径,就是在客户端看到的目录结构。由于一个用户只能有一个主目录,别的目录如果不映射成虚拟目录的话,客户端将看不到它。所以只有把除了主目录之外的其它目录,虚拟成主目录的子目录,这样在客户端才能看到。
比如本例,主目录是D:\Downloads,如果不做虚拟路径设置,那么客户端登录进来只能看到主目录里面的内容,还有一个E:\FTPRoot目录下面的东西看不到。如何设置虚拟路径?鼠标右键点击列表中的“E:\FTPRoot”目录,在弹出的菜单里面选“Edit
aliases”编辑别名;现在要把E:\FTPRoot目录作为客户端主目录下的FTPRoot目录,那么就在弹出的窗口里面输入“D:\Downloads\FTPRoot”并点击“OK”按钮确定。注意拼写规则,路径的前面部分必须是主目录的绝对路径。这样设置之后,在客户端就可以看到一个“FTPRoot”目录,这个目录其实就是E:\FTPRoot目录。
组设置中的“Speed
Limits”和“IP
Filter”跟全局设置里面的速度限制和IP过滤器设置方法是一样的,请参照前面的内容。只不过这个是仅仅针对这个组的用户生效。而全局设置是对所有的用户都生效。
设置完毕之后点击“OK”按钮回到主界面。
最后一项就是设置用户(Users)。点击主界面第四个按钮或者由“Edit”——“Users”菜单进入。
点击右边的“Add”按钮创建用户,在“Password”域输入用户口令。然后从“Group
membership”栏选择该用户所属的组(Group),这样该用户将继承该组的所有属性/权限,不用再单独一一设置这些参数了。这也是设置组体现的方便性,在用户比较多的时候使用组来分类会使得管理工作更加方便、高效。完毕之后点“OK”按钮保存设置并返回主界面。
当然,也可以设置一个不属于任何组的用户,这样的话,就得单独定制该用户的权限。对于少量特殊用户,可以用这种方式设置。
至此,FileZilla
Server的基本设置就完成并可以运行了。
对于初次使用FTP Server软件的用户,本文也可以作为入门参考。其实所有的FTP
Server软件安装设置的基本原理都是类似的,掌握了一个之后,别的可以举一反三融会贯通。
姓名:赵荣涛
生日:1984年
住址:山东济南
工作:大众网-山东省互联网传媒集团
职位:技术经理/架构师
地址:济南泺源大街大众传媒大厦
时间:2006.7——至今perlipc - Perl interprocess communication (signals, fifos, pipes, safe subprocesses, sockets, and semaphores)
perlipc - Perl interprocess communication (signals, fifos, pipes, safe
subprocesses, sockets, and semaphores)
IPC facilities of Perl are built out of the good old Unix signals, named pipes, pipe opens, the Berkeley socket routines, and SysV
IPC calls. Each is used in slightly different situations.
Perl uses a simple signal handling model: the %SIG hash contains names or references of user-installed signal handlers. These handlers will be called with an argument which is the name of the signal that triggered it.
A signal may be generated intentionally from a particular keyboard sequence like control-C or control-Z, sent to you from another process, or triggered automatically by the kernel when special events transpire, like a child process exiting, your process running out of stack space, or hitting file size limit.
For example, to trap an interrupt signal, set up a handler like this. Do as little as you possibly notice how all we do is set a global variable and then raise an exception. That's because on most systems, libraries are not re- particularly, memory allocation and
I/O routines are not. That means that doing nearly
anything in your handler could in theory trigger a memory fault and subsequent core
sub catch_zap {
my $signame =
$shucks++;
die &Somebody sent me a SIG$signame&;
$SIG{INT} = 'catch_zap';
# could fail in modules
$SIG{INT} = \&catch_
# best strategy
The names of the signals are the ones listed out by kill -l on your system, or you can retrieve them from the Config module. Set up an
@signame list indexed by number to get the name and a
%signo table indexed by name to get the number:
defined $Config{sig_name} || die &No sigs?&;
foreach $name (split(' ', $Config{sig_name})) {
$signo{$name} = $i;
$signame[$i] = $
So to check whether signal 17 and
SIGALRM were the same, do just this:
print &signal #17 = $signame[17]\n&;
if ($signo{ALRM}) {
print &SIGALRM is $signo{ALRM}\n&;
You may also choose to assign the strings 'IGNORE' or 'DEFAULT' as the handler, in which case Perl will try to discard the signal or do the default thing. Some signals can be neither trapped nor ignored, such as the
STOP (but not the
TSTP) signals. One strategy for temporarily ignoring signals is to use a local() statement, which will be automatically restored once your block is exited. (Remember that local() values are ``inherited'' by functions called from within that block.)
sub precious {
local $SIG{INT} = 'IGNORE';
sub more_functions {
# interrupts still ignored, for now...
Sending a signal to a negative process
ID means that you send the signal to the entire Unix
process-group. This code send a hang-up signal to all processes in the
current process group except for the current process itself:
local $SIG{HUP} = 'IGNORE';
kill HUP =& -$$;
# snazzy writing of: kill('HUP', -$$)
Another interesting signal to send is signal number zero. This doesn't actually affect another process, but instead checks whether it's alive or has changed its
unless (kill 0 =& $kid_pid) {
warn &something wicked happened to $kid_pid&;
You might also want to employ anonymous functions for simple signal
$SIG{INT} = sub { die &\nOutta here!\n& };
But that will be problematic for the more complicated handlers that need to reinstall themselves. Because Perl's signal mechanism is currently based on the signal(3) function from the
C library, you may sometimes be so misfortunate as to run on systems where that function is ``broken'', that is, it behaves in the old unreliable SysV way rather than the newer, more reasonable
POSIX fashion. So you'll see defensive people writing signal handlers like this:
sub REAPER {
$waitedpid =
# loathe sysV: it makes us not only reinstate
# the handler, but place it after the wait
$SIG{CHLD} = \&REAPER;
$SIG{CHLD} = \&REAPER;
# now do something that forks...
or even the more elaborate:
use POSIX &:sys_wait_h&;
sub REAPER {
while ($child = waitpid(-1,WNOHANG)) {
$Kid_Status{$child} = $?;
$SIG{CHLD} = \&REAPER;
# still loathe sysV
$SIG{CHLD} = \&REAPER;
# do something that forks...
Signal handling is also used for timeouts in Unix, While safely protected
block, you set a signal handler to trap alarm signals and then schedule to
have one delivered to you in some number of seconds. Then try your blocking
operation, clearing the alarm when it's done but not before you've exited
block. If it goes off, you'll use die() to jump out of the
block, much as you might using longjmp() or
throw() in other languages.
Here's an example:
local $SIG{ALRM} = sub { die &alarm clock restart& };
flock(FH, 2);
# blocking write lock
if ($@ and $@ !~ /alarm clock restart/) { die }
For more complex signal handling, you might see the standard
POSIX module. Lamentably, this is almost entirely
undocumented, but the t/lib/posix.t file from the Perl source distribution has some examples in it.
A named pipe (often referred to as a
FIFO) is an old Unix
IPC mechanism for processes communicating on the same machine. It works just like a regular, connected anonymous pipes, except that the processes rendezvous using a filename and don't have to be related.
To create a named pipe, use the Unix command mknod(1) or on
some systems, mkfifo(1). These may not be in your normal path.
# system return val is backwards, so && not ||
$ENV{PATH} .= &:/etc:/usr/etc&;
system('mknod',
$path, 'p')
&& system('mkfifo', $path) )
die &mk{nod,fifo} $
A fifo is convenient when you want to connect a
process to an unrelated one. When you open a fifo, the program will block
until there's something on the other end.
For example, let's say you'd like to have your .signature file be a named pipe that has a Perl program on the other end. Now every
time any program (like a mailer, news reader, finger program, etc.) tries
to read from that file, the reading program will block and your program
will supply the new signature. We'll use the pipe-checking file test -p
to find out whether anyone (or anything) has accidentally removed our fifo.
$FIFO = '.signature';
$ENV{PATH} .= &:/etc:/usr/games&;
while (1) {
unless (-p $FIFO) {
unlink $FIFO;
system('mknod', $FIFO, 'p')
&& die &can't mknod $FIFO: $!&;
# next line blocks until there's a reader
open (FIFO, && $FIFO&) || die &can't write $FIFO: $!&;
print FIFO &John Smith (smith\@host.org)\n&, `fortune -s`;
close FIFO;
# to avoid dup signals
Perl's basic open() statement can also be used for
unidirectional interprocess communication by either appending or prepending
a pipe symbol to the second argument to open(). Here's how to
start something up in a child process you intend to write to:
open(SPOOLER, &| cat -v | lpr -h 2&/dev/null&)
|| die &can't fork: $!&;
local $SIG{PIPE} = sub { die &spooler pipe broke& };
print SPOOLER &stuff\n&;
close SPOOLER || die &bad spool: $! $?&;
And here's how to start up a child process you intend to read from:
open(STATUS, &netstat -an 2&&1 |&)
|| die &can't fork: $!&;
while (&STATUS&) {
next if /^(tcp|udp)/;
close STATUS || die &bad netstat: $! $?&;
If one can be sure that a particular program is a Perl script that is expecting filenames in
@ARGV, the clever programmer can write something like this:
$ program f1 &cmd1|& - f2 &cmd2|& f3 & tmpfile
and irrespective of which shell it's called from, the Perl program will
read from the file f1, the process cmd1, standard input (tmpfile
in this case), the f2 file, the cmd2 command, and finally the f3
file. Pretty nifty, eh?
You might notice that you could use backticks for much the same effect as
opening a pipe for reading:
print grep { !/^(tcp|udp)/ } `netstat -an 2&&1`;
die &bad netstat& if $?;
While this is true on the surface, it's much more efficient to process the
file one line or record at a time because then you don't have to read the
whole thing into memory at once. It also gives you finer control of the
whole process, letting you to kill off the child process early if you'd
Be careful to check both the open() and the
close() return values. If you're writing to a pipe, you should also trap
SIGPIPE. Otherwise, think of what happens when you
start up a pipe to a command that doesn't exist: the open()
will in all likelihood succeed (it only reflects the fork()'s
success), but then your output will fail--spectacularly. Perl can't know
whether the command worked because your command is actually running in a
separate process whose exec() might have failed. Therefore,
while readers of bogus commands return just a quick end of file, writers to
bogus command will trigger a signal they'd better be prepared to handle.
open(FH, &|bogus&);
print FH &bang\n&;
Both the main process and the child process share the same
STDOUT and
STDERR filehandles. If both processes try to access them at once, strange things can happen. You may want to close or reopen the filehandles for the child. You can get around this by opening your pipe with open(), but on some systems this means that the child process cannot outlive the parent.
You can run a command in the background with:
system(&cmd &&);
The command's
STDOUT and
STDERR (and possibly
STDIN, depending on your shell) will be the same as the parent's. You won't need to catch
SIGCHLD because of the double-fork taking place (see below for more details).
In some cases (starting server processes, for instance) you'll want to
complete dissociate the child process from the parent. The following
process is reported to work on most Unixish systems. Non-Unix users should
check their Your_OS::Process module for other solutions.
Open /dev/tty and use the
TIOCNOTTY ioctl on it. See tty(4)
for details.
Change directory to /
STDOUT, and
STDERR so they're not connected to the old tty.
Background yourself like this:
Another interesting approach to
IPC is making your single program go multiprocess and
communicate between (or even amongst) yourselves. The open()
function will accept a file argument of either "-|" or "|-"
to do a very interesting thing: it forks a child connected to the filehandle you've opened. The child is running the same program as the parent. This is useful for safely opening a file when running under an assumed
GID, for example. If you open a pipe
to minus, you can write to the filehandle you opened and your kid will find it in his
STDIN. If you open a pipe
from minus, you can read from the filehandle you opened whatever your kid writes to his
my $sleep_count = 0;
$pid = open(KID_TO_WRITE, &|-&);
unless (defined $pid) {
warn &cannot fork: $!&;
die &bailing out& if $sleep_count++ & 6;
} until defined $
if ($pid) {
print KID_TO_WRITE @some_
close(KID_TO_WRITE) || warn &kid exited $?&;
($EUID, $EGID) = ($UID, $GID); # suid progs only
open (FILE, && /safe/file&)
|| die &can't open /safe/file: $!&;
while (&STDIN&) {
print FILE; # child's STDIN is parent's KID
# don't forget this
Another common use for this construct is when you need to execute something
without the shell's interference. With system(), it's
straightforward, but you can't use a pipe open or backticks safely. That's
because there's no way to stop the shell from getting its hands on your
arguments. Instead, use lower-level control to call exec()
Here's a safe backtick or pipe open for read:
# add error processing as above
$pid = open(KID_TO_READ, &-|&);
if ($pid) {
while (&KID_TO_READ&) {
# do something interesting
close(KID_TO_READ) || warn &kid exited $?&;
($EUID, $EGID) = ($UID, $GID); # suid only
exec($program, @options, @args)
|| die &can't exec program: $!&;
# NOTREACHED
And here's a safe pipe open for writing:
# add error processing as above
$pid = open(KID_TO_WRITE, &|-&);
$SIG{ALRM} = sub { die &whoops, $program pipe broke& };
if ($pid) {
for (@data) {
print KID_TO_WRITE;
close(KID_TO_WRITE) || warn &kid exited $?&;
($EUID, $EGID) = ($UID, $GID);
exec($program, @options, @args)
|| die &can't exec program: $!&;
# NOTREACHED
Note that these operations are full Unix forks, which means they may not be
correctly implemented on alien systems. Additionally, these are not true
multithreading. If you'd like to learn more about threading, see the
modules file mentioned below in the
ALSO section.
While this works reasonably well for unidirectional communication, what
about bidirectional communication? The obvious thing you'd like to do
doesn't actually work:
open(PROG_FOR_READING_AND_WRITING, &| some program |&)
and if you forget to use the -w flag, then you'll miss out entirely on the diagnostic message:
Can't do bidirectional pipe at -e line 1.
If you really want to, you can use the standard open2() library function to catch both ends. There's also an open3() for tridirectional
I/O so you can also catch your child's
STDERR, but doing so would then require an awkward select() loop and wouldn't allow you to use normal Perl input operations.
If you look at its source, you'll see that open2() uses low-level primitives like Unix pipe() and exec() to create all the connections. While it might have been slightly more efficient by using socketpair(), it would have then been even less portable than it already is. The open2() and open3() functions are unlikely to work anywhere except on a Unix system or some other one purporting to be
POSIX compliant.
Here's an example of using open2():
use IPC::Open2;
$pid = open2( \*Reader, \*Writer, &cat -u -n& );
Writer-&autoflush(); # default here, actually
print Writer &stuff\n&;
$got = &Reader&;
The problem with this is that Unix buffering is really going to ruin your
day. Even though your Writer filehandle is auto-flushed, and the process on the other end will get your
data in a timely manner, you can't usually do anything to force it to give
it back to you in a similarly quick fashion. In this case, we could,
because we gave cat a -u flag to make it unbuffered. But very few Unix commands are designed to
operate over pipes, so this seldom works unless you yourself wrote the
program on the other end of the double-ended pipe.
A solution to this is the nonstandard Comm.pl library. It uses pseudo-ttys to make your program behave more reasonably:
require 'Comm.pl';
$ph = open_proc('cat -n');
for (1..10) {
print $ph &a line\n&;
print &got back &, scalar &$ph&;
This way you don't have to have control over the source code of the program
you're using. The Comm library also has expect() and interact()
functions. Find the library (and we hope its successor IPC::Chat) at your nearest
CPAN archive as detailed in the
ALSO section below.
While not limited to Unix-derived operating systems (e.g., WinSock on PCs provides socket support, as do some
VMS libraries), you may not have sockets on your system, in which case this section probably isn't going to do you much good. With sockets, you can do both virtual circuits (i.e.,
TCP streams) and datagrams (i.e.,
UDP packets). You may be able to do even more depending on your system.
The Perl function calls for dealing with sockets have the same names as the corresponding system calls in
C, but their arguments tend to differ for two reasons: first, Perl filehandles work differently than
C file descriptors. Second, Perl already knows the length of its strings, so you don't need to pass that information.
One of the major problems with old socket code in Perl was that it used
hard-coded values for some of the constants, which severely hurt
portability. If you ever see code that does anything like explicitly
setting $AF_INET = 2, you know you're in for big trouble: An immeasurably superior approach is
to use the Socket module, which more reliably grants access to various constants and
functions you'll need.
If you're not writing a server/client for an existing protocol like
SMTP, you should give some thought to how your server will know when the client has finished talking, and vice-versa. Most protocols are based on one-line messages and responses (so one party knows the other has finished when a ``\n'' is received) or multi-line messages and responses that end with a period on an empty line (``\n.\n'' terminates a message/response).
Use Internet-domain sockets when you want to do client-server communication
that might extend to machines outside of your own system.
Here's a sample
TCP client using Internet-domain sockets:
#!/usr/bin/perl -w
require 5.002;
my ($remote,$port, $iaddr, $paddr, $proto, $line);
= shift || 'localhost';
= shift || 2345;
# random port
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die &No port& unless $
= inet_aton($remote)
|| die &no host: $remote&;
= sockaddr_in($port, $iaddr);
= getprotobyname('tcp');
socket(SOCK, PF_INET, SOCK_STREAM, $proto)
|| die &socket: $!&;
connect(SOCK, $paddr)
|| die &connect: $!&;
while (defined($line = &SOCK&)) {
close (SOCK)
|| die &close: $!&;
And here's a corresponding server to go along with it. We'll leave the address as
INADDR_ANY so that the kernel can choose the appropriate interface on multihomed hosts. If you want sit on a particular interface (like the external side of a gateway or firewall machine), you should fill this in with your real address instead.
#!/usr/bin/perl -Tw
require 5.002;
BEGIN { $ENV{PATH} = '/usr/ucb:/bin' }
sub logmsg { print &$0 $$: @_ at &, scalar localtime, &\n& }
my $port = shift || 2345;
my $proto = getprotobyname('tcp');
$port = $1 if $port =~ /(\d+)/; # untaint port number
socket(Server, PF_INET, SOCK_STREAM, $proto)
|| die &socket: $!&;
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR,
pack(&l&, 1))
|| die &setsockopt: $!&;
bind(Server, sockaddr_in($port, INADDR_ANY))
|| die &bind: $!&;
listen(Server,SOMAXCONN)
|| die &listen: $!&;
logmsg &server started on port $port&;
$SIG{CHLD} = \&REAPER;
for ( ; $paddr = accept(Client,Server); close Client) {
my($port,$iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
logmsg &connection from $name [&,
inet_ntoa($iaddr), &]
at port $port&;
print Client &Hello there, $name, it's now &,
scalar localtime, &\n&;
And here's a multithreaded version. It's multithreaded in that like most
typical servers, it spawns (forks) a slave server to handle the client
request so that the master server can quickly go back to service a new
#!/usr/bin/perl -Tw
require 5.002;
BEGIN { $ENV{PATH} = '/usr/ucb:/bin' }
# forward declaration
sub logmsg { print &$0 $$: @_ at &, scalar localtime, &\n& }
my $port = shift || 2345;
my $proto = getprotobyname('tcp');
$port = $1 if $port =~ /(\d+)/; # untaint port number
socket(Server, PF_INET, SOCK_STREAM, $proto)
|| die &socket: $!&;
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR,
pack(&l&, 1))
|| die &setsockopt: $!&;
bind(Server, sockaddr_in($port, INADDR_ANY))
|| die &bind: $!&;
listen(Server,SOMAXCONN)
|| die &listen: $!&;
logmsg &server started on port $port&;
my $waitedpid = 0;
sub REAPER {
$waitedpid =
$SIG{CHLD} = \&REAPER;
# loathe sysV
logmsg &reaped $waitedpid& . ($? ? & with exit $?& : '');
$SIG{CHLD} = \&REAPER;
for ( $waitedpid = 0;
($paddr = accept(Client,Server)) || $
$waitedpid = 0, close Client)
next if $waitedpid and not $
my($port,$iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
logmsg &connection from $name [&,
inet_ntoa($iaddr), &]
at port $port&;
spawn sub {
print &Hello there, $name, it's now &, scalar localtime, &\n&;
exec '/usr/games/fortune'
or confess &can't exec fortune: $!&;
sub spawn {
my $coderef =
unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
confess &usage: spawn CODEREF&;
if (!defined($pid = fork)) {
logmsg &cannot fork: $!&;
} elsif ($pid) {
logmsg &begat $pid&;
# I'm the parent
# else I'm the child -- go spawn
open(STDIN,
&&&Client&)
|| die &can't dup client to stdin&;
open(STDOUT, &&&Client&)
|| die &can't dup client to stdout&;
## open(STDERR, &&&STDOUT&) || die &can't dup stdout to stderr&;
exit &$coderef();
This server takes the trouble to clone off a child version via
fork() for each incoming request. That way it can handle many
requests at once, which you might not always want. Even if you don't
fork(), the listen() will allow that many pending
connections. Forking servers have to be particularly careful about cleaning
up their dead children (called ``zombies'' in Unix parlance), because
otherwise you'll quickly fill up your process table.
We suggest that you use the -T flag to use taint checking (see ) even if we aren't running setuid or setgid. This is always a good idea for servers and other programs run on behalf of someone else (like
CGI scripts), because it lessens the chances that people from the outside will be able to compromise your system.
Let's look at another
TCP client. This one connects to the
TCP ``time'' service on a number of different machines and shows how far their clocks differ from the system on which it's being run:
#!/usr/bin/perl
require 5.002;
my $SECS_of_70_YEARS = ;
sub ctime { scalar localtime(shift) }
my $iaddr = gethostbyname('localhost');
my $proto = getprotobyname('tcp');
my $port = getservbyname('time', 'tcp');
my $paddr = sockaddr_in(0, $iaddr);
my($host);
printf &%-24s %8s %s\n&,
&localhost&, 0, ctime(time());
foreach $host (@ARGV) {
printf &%-24s &, $
my $hisiaddr = inet_aton($host)
|| die &unknown host&;
my $hispaddr = sockaddr_in($port, $hisiaddr);
socket(SOCKET, PF_INET, SOCK_STREAM, $proto)
|| die &socket: $!&;
connect(SOCKET, $hispaddr)
|| die &bind: $!&;
my $rtime = '
read(SOCKET, $rtime, 4);
close(SOCKET);
my $histime = unpack(&N&, $rtime) - $SECS_of_70_YEARS ;
printf &%8d %s\n&, $histime - time, ctime($histime);
That's fine for Internet-domain clients and servers, but what about local
communications? While you can use the same setup, sometimes you don't want
to. Unix-domain sockets are local to the current host, and are often used
internally to implement pipes. Unlike Internet domain sockets, Unix domain
sockets can show up in the file system with an ls(1) listing.
$ ls -l /dev/log
srw-rw-rw-
0 Oct 31 07:23 /dev/log
You can test for these with Perl's -S file test:
unless ( -S '/dev/log' ) {
die &something's wicked with the print system&;
Here's a sample Unix-domain client:
#!/usr/bin/perl -w
require 5.002;
my ($rendezvous, $line);
$rendezvous = shift || '/tmp/catsock';
socket(SOCK, PF_UNIX, SOCK_STREAM, 0)
|| die &socket: $!&;
connect(SOCK, sockaddr_un($rendezvous))
|| die &connect: $!&;
while (defined($line = &SOCK&)) {
And here's a corresponding server.
#!/usr/bin/perl -Tw
require 5.002;
BEGIN { $ENV{PATH} = '/usr/ucb:/bin' }
my $NAME = '/tmp/catsock';
my $uaddr = sockaddr_un($NAME);
my $proto = getprotobyname('tcp');
socket(Server,PF_UNIX,SOCK_STREAM,0)
|| die &socket: $!&;
unlink($NAME);
(Server, $uaddr)
|| die &bind: $!&;
listen(Server,SOMAXCONN)
|| die &listen: $!&;
logmsg &server started on $NAME&;
$SIG{CHLD} = \&REAPER;
for ( $waitedpid = 0;
accept(Client,Server) || $
$waitedpid = 0, close Client)
logmsg &connection on $NAME&;
spawn sub {
print &Hello there, it's now &, scalar localtime, &\n&;
exec '/usr/games/fortune' or die &can't exec fortune: $!&;
As you see, it's remarkably similar to the Internet domain
TCP server, so much so, in fact, that we've omitted
several duplicate functions--spawn(), logmsg(),
ctime(), and REAPER()--which are exactly the same
as in the other server.
So why would you ever want to use a Unix domain socket instead of a simpler
named pipe? Because a named pipe doesn't give you sessions. You can't tell
one process's data from another's. With socket programming, you get a
separate session for each client: that's why accept() takes
two arguments.
For example, let's say that you have a long running database server daemon that you want folks from the World Wide Web to be able to access, but only if they go through a
CGI interface. You'd have a small, simple
CGI program that does whatever checks and logging you feel like, and then acts as a Unix-domain client and connects to your private server.
For those preferring a higher-level interface to socket programming, the IO::Socket module provides an object-oriented approach. IO::Socket is included as part of the standard Perl distribution as of the 5.004 release. If you're running an earlier version of Perl, just fetch IO::Socket from
CPAN, where you'll also find find modules providing easy interfaces to the following systems:
FTP, Ident
(RFC 931),
NIS and NISPlus,
NNTP, Ping,
SNMP, SSLeay, Telnet, and Time--just to name a few.
Here's a client that creates a
TCP connection to the ``daytime'' service at port 13
of the host name ``localhost'' and prints out everything that the server
there cares to provide.
#!/usr/bin/perl -w
$remote = IO::Socket::INET-&new(
PeerAddr =& &localhost&,
PeerPort =& &daytime(13)&,
or die &cannot connect to daytime port at localhost&;
while ( &$remote& ) { print }
When you run this program, you should get something back that looks like
Wed May 14 08:40:46 MDT 1997
Here are what those parameters to the new constructor mean:
This is which protocol to use. In this case, the socket handle returned will be connected to a
TCP socket, because we want a stream-oriented connection, that is, one that acts pretty much like a plain old file. Not all sockets are this of this type. For example, the
UDP protocol can be used to make a datagram socket, used for message-passing.
This is the name or Internet address of the remote host the server is
running on. We could have specified a longer name like "", or an address like "204.148.40.9". For demonstration purposes, we've used the special hostname "localhost", which should always mean the current machine you're running on. The
corresponding Internet address for localhost is "127.1", if you'd rather use that.
This is the service name or port number we'd like to connect to. We could
have gotten away with using just "daytime" on systems with a well-configured system services file,[FOOTNOTE: The
system services file is in /etc/services under Unix] but just in case, we've specified the port number (13) in
parentheses. Using just the number would also have worked, but constant
numbers make careful programmers nervous.
Notice how the return value from the new constructor is used as a filehandle in the while loop? That's what's called an indirect filehandle, a scalar variable
containing a filehandle. You can use it the same way you would a normal
filehandle. For example, you can read one line from it this way:
$line = &$handle&;
all remaining lines from is this way:
@lines = &$handle&;
and send a line of data to it this way:
print $handle &some data\n&;
Here's a simple client that takes a remote host to fetch a document from,
and then a list of documents to get from that host. This is a more
interesting client than the previous one because it first sends something
to the server before fetching the server's response.
#!/usr/bin/perl -w
unless (@ARGV & 1) { die &usage: $0 host document ...& }
$host = shift(@ARGV);
foreach $document ( @ARGV ) {
$remote = IO::Socket::INET-&new( Proto
=& &http(80)&,
unless ($remote) { die &cannot connect to http daemon on $host& }
$remote-&autoflush(1);
print $remote &GET $document HTTP/1.0\n\n&;
while ( &$remote& ) { print }
The web server handing the ``http'' service, which is assumed to be at its
standard port, number 80. If your the web server you're trying to connect
to is at a different port (like 1080 or 8080), you should specify as the
named-parameter pair, PeerPort =& 8080. The autoflush
method is used on the socket because otherwise the system would buffer up
the output we sent it. (If you're on a Mac, you'll also need to change
in your code that sends data over the network to be a "\015\012" instead.)
Connecting to the server is only the first part of the process: once you have the connection, you have to use the server's language. Each server on the network has its own little command language that it expects as input. The string that we send to the server starting with
``GET'' is in
HTTP syntax. In this case, we simply request each specified document. Yes, we really are making a new connection for each document, even though it's the same host. That's the way you always used to have to speak
HTTP. Recent versions of web browsers may request that the remote server leave the connection open a little while, but the server doesn't have to honor such a request.
Here's an example of running that program, which we'll call webget:
shell_prompt$ webget
/guanaco.html
HTTP/1.1 404 File Not Found
Date: Thu, 08 May :32 GMT
Server: Apache/1.2b6
Connection: close
Content-type: text/html
&HEAD&&TITLE&404 File Not Found&/TITLE&&/HEAD&
&BODY&&H1&File Not Found&/H1&
The requested URL /guanaco.html was not found on this server.&P&
Ok, so that's not very interesting, because it didn't find that particular
document. But a long response wouldn't have fit on this page.
For a more fully-featured version of this program, you should look to the lwp-request program included with the
LWP modules from
Well, that's all fine if you want to send one command and get one answer,
but what about setting up something fully interactive, somewhat like the
way telnet works? That way you can type a line, get the answer, type a line, get the
answer, etc.
This client is more complicated than the two we've done so far, but if
you're on a system that supports the powerful
call, the solution isn't that rough. Once you've made the connection to
whatever service you'd like to chat with, call
to clone your process. Each of these two identical process has a very
simple job to do: the parent copies everything from the socket to standard
output, while the child simultaneously copies everything from standard
input to the socket. To accomplish the same thing using just one process
would be much
harder, because it's easier to code two processes to do one thing than it
is to code one process to do two things. (This keep-it-simple principle is
one of the cornerstones of the Unix philosophy, and good software
engineering as well, which is probably why it's spread to other systems as
Here's the code:
#!/usr/bin/perl -w
my ($host, $port, $kidpid, $handle, $line);
unless (@ARGV == 2) { die &usage: $0 host port& }
($host, $port) = @ARGV;
# create a tcp connection to the specified host and port
$handle = IO::Socket::INET-&new(Proto
or die &can't connect to port $port on $host: $!&;
$handle-&autoflush(1);
# so output gets there right away
print STDERR &[Connected to $host:$port]\n&;
# split the program into two processes, identical twins
die &can't fork: $!& unless defined($kidpid = fork());
# the if{} block runs only in the parent process
if ($kidpid) {
# copy the socket to standard output
while (defined ($line = &$handle&)) {
print STDOUT $
kill(&TERM&, $kidpid);
# send SIGTERM to child
# the else{} block runs only in the child process
# copy standard input to the socket
while (defined ($line = &STDIN&)) {
print $handle $
function in the parent's if block is there to send a signal to our child process (current running in
the else block) as soon as the remote server has closed its end of the connection.
at the end of the parent's block is there to eliminate the child process as
soon as the server we connect to closes its end.
If the remote server sends data a byte at time, and you need that data
immediately without waiting for a newline (which might not happen), you may
wish to replace the while loop in the parent with the following:
while (sysread($handle, $byte, 1) == 1) {
print STDOUT $
Making a system call for each byte you want to read is not very efficient
(to put it mildly) but is the simplest to explain and works reasonably
Setting up server is little bit more involved than running a client. The
model is that the server creates a special kind of socket that does nothing
but listen on a particular port for incoming connections. It does this by
calling the IO::Socket::INET-&new() method with slightly different arguments than the client did.
This is which protocol to use. Like our clients, we'll still specify "tcp" here.
We specify a local port in the
argument, which we didn't do for the client. This is service name or port
number for which you want to be the server. (Under Unix, ports under 1024
are restricted to the superuser.) In our sample, we'll use port 9000, but
you can use any port that's not currently in use on your system. If you try
to use one already in used, you'll get an ``Address already in use''
message. Under Unix, the netstat -a command will show which services current have servers.
parameter is set to the maximum number of pending connections we can accept until we turn away incoming clients. Think of it as a call-waiting queue for your telephone. The low-level Socket module has a special symbol for the system maximum, which is
SOMAXCONN.
parameter is needed so that we restart our server manually without waiting
a few minutes to allow system buffers to clear out.
Once the generic server socket has been created using the parameters listed
above, the server then waits for a new client to connect to it. The server
blocks in the
method, which eventually an bidirectional connection to the remote client.
(Make sure to autoflush this handle to circumvent buffering.)
To add to user-friendliness, our server prompts the user for commands. Most
servers don't do this. Because of the prompt without a newline, you'll have
to use the
variant of the interactive client above.
This server accepts one of five different commands, sending output back to
the client. Note that unlike most network servers, this one only handles
one incoming client at a time. Multithreaded servers are covered in Chapter
6 of the Camel or in the perlipc(1) manpage.
Here's the code. We'll
#!/usr/bin/perl -w
# for OO version of gethostbyaddr
$PORT = 9000;
# pick something not in use
$server = IO::Socket::INET-&new( Proto
LocalPort =& $PORT,
=& SOMAXCONN,
die &can't setup server& unless $
print &[Server $0 accepting clients]\n&;
while ($client = $server-&accept()) {
$client-&autoflush(1);
print $client &Welcome to $0; type help for command list.\n&;
$hostinfo = gethostbyaddr($client-&peeraddr);
printf &[Connect from %s]\n&, $hostinfo-&name || $client-&
print $client &Command? &;
while ( &$client&) {
next unless /\S/;
# blank line
(/quit|exit/i)
elsif (/date|time/i)
{ printf $client &%s\n&,
elsif (/who/i )
$client `who 2&&1`;
elsif (/cookie/i )
$client `/usr/games/fortune 2&&1`; }
elsif (/motd/i )
$client `cat /etc/motd 2&&1`;
print $client &Commands: quit date who cookie motd\n&;
} continue {
print $client &Command? &;
Another kind of client-server setup is one that uses not connections, but messages.
UDP communications involve much lower overhead but also provide less reliability, as there are no promises that messages will arrive at all, let alone in order and unmangled. Still,
UDP offers some advantages over
TCP, including being able to ``broadcast'' or ``multicast'' to a whole bunch of destination hosts at once (usually on your local subnet). If you find yourself overly concerned about reliability and start building checks into your message system, then you probably should use just
TCP to start with.
UDP program similar to the sample Internet
TCP client given earlier. However, instead of checking one host at a time, the
UDP version will check many of them asynchronously by simulating a multicast and then using select() to do a timed-out wait for
I/O. To do something similar with
TCP, you'd have to use a different socket handle for each host.
#!/usr/bin/perl -w
require 5.002;
use Sys::H
my ( $count, $hisiaddr, $hispaddr, $histime,
$host, $iaddr, $paddr, $port, $proto,
$rin, $rout, $rtime, $SECS_of_70_YEARS);
$SECS_of_70_YEARS
$iaddr = gethostbyname(hostname());
$proto = getprotobyname('udp');
$port = getservbyname('time', 'udp');
$paddr = sockaddr_in(0, $iaddr); # 0 means let kernel pick
socket(SOCKET, PF_INET, SOCK_DGRAM, $proto)
|| die &socket: $!&;
bind(SOCKET, $paddr)
|| die &bind: $!&;
printf &%-12s %8s %s\n&,
&localhost&, 0, s
$count = 0;
for $host (@ARGV) {
$hisiaddr = inet_aton($host)
|| die &unknown host&;
$hispaddr = sockaddr_in($port, $hisiaddr);
defined(send(SOCKET, 0, 0, $hispaddr))
|| die &send $host: $!&;
$rin = '';
vec($rin, fileno(SOCKET), 1) = 1;
# timeout after 10.0 seconds
while ($count && select($rout = $rin, undef, undef, 10.0)) {
$rtime = '';
($hispaddr = recv(SOCKET, $rtime, 4, 0))
|| die &recv: $!&;
($port, $hisiaddr) = sockaddr_in($hispaddr);
$host = gethostbyaddr($hisiaddr, AF_INET);
$histime = unpack(&N&, $rtime) - $SECS_of_70_YEARS ;
printf &%-12s &, $
printf &%8d %s\n&, $histime - time, scalar localtime($histime);
While System
IPC isn't so widely used as sockets, it still has some interesting uses. You can't, however, effectively use SysV
IPC or Berkeley mmap() to have shared memory so as to share a variable amongst several processes. That's because Perl would reallocate your string when you weren't wanting it to.
Here's a small example showing shared memory usage.
$IPC_PRIVATE = 0;
$IPC_RMID = 0;
$size = 2000;
$key = shmget($IPC_PRIVATE, $size , 0777 );
die unless defined $
$message = &Message #1&;
shmwrite($key, $message, 0, 60 ) || die &$!&;
shmread($key,$buff,0,60) || die &$!&;
print $buff,&\n&;
print &deleting $key\n&;
shmctl($key ,$IPC_RMID, 0) || die &$!&;
Here's an example of a semaphore:
$IPC_KEY = 1234;
$IPC_RMID = 0;
$IPC_CREATE = 0001000;
$key = semget($IPC_KEY, $nsems , 0666 | $IPC_CREATE );
die if !defined($key);
print &$key\n&;
Put this code in a separate file to be run in more than one process. Call
the file take:
# create a semaphore
$IPC_KEY = 1234;
$key = semget($IPC_KEY,
die if !defined($key);
$semnum = 0;
$semflag = 0;
# 'take' semaphore
# wait for semaphore to be zero
$semop = 0;
$opstring1 = pack(&sss&, $semnum, $semop, $semflag);
# Increment the semaphore count
$semop = 1;
$opstring2 = pack(&sss&, $semnum, $semop,
$semflag);
$opstring = $opstring1 . $opstring2;
semop($key,$opstring) || die &$!&;
Put this code in a separate file to be run in more than one process. Call
this file give:
# 'give' the semaphore
# run this in the original process and you will see
# that the second process continues
$IPC_KEY = 1234;
$key = semget($IPC_KEY, 0, 0);
die if !defined($key);
$semnum = 0;
$semflag = 0;
# Decrement the semaphore count
$semop = -1;
$opstring = pack(&sss&, $semnum, $semop, $semflag);
semop($key,$opstring) || die &$!&;
IPC code above was written long ago, and it's
definitely clunky looking. It should at the very least be made to use strict
and require "sys/ipc.ph". Better yet, check out the IPC::SysV modules on
If you are running under version 5.000 (dubious) or 5.001, you can still
use most of the examples in this document. You may have to remove the
use strict and some of the my() statements for 5.000, and for both you'll
have to load in version 1.2 or older of the Socket.pm module, which is included in perl5.002.
Most of these routines quietly but politely return
when they fail instead of causing your program to die right then and there
due to an uncaught exception. (Actually, some of the new Socket conversion functions croak() on bad arguments.) It is
therefore essential that you should check the return values of these
functions. Always begin your socket programs this way for optimal success,
and don't forget to add
-T taint checking flag to the pound-bang line for servers:
#!/usr/bin/perl -w
require 5.002;
All these routines create system-specific portability problems. As noted elsewhere, Perl is at the mercy of your
C libraries for much of its system behaviour. It's probably safest to assume broken SysV semantics for signals and to stick with simple
UDP e.g., don't try to pass open file descriptors over a local
UDP datagram socket if you want your code to stand a chance of being portable.
Because few vendors provide
C libraries that are safely re-entrant, the prudent
programmer will do little else within a handler beyond setting a numeric
variable or, if locked into a slow (restarting) system
call, using die() to raise an exception and
longjmp(3) out. In fact, even these may in some cases cause a
core dump. It's probably best to avoid signals except where they are
absolutely inevitable. This perilous problems will be addressed in a future
release of Perl.
Tom Christiansen, with occasional vestiges of Larry Wall's original version
and suggestions from the Perl Porters.
There's a lot more to networking than this, but this should get you
For intrepid programmers, the classic textbook Unix Network Programming
by Richard Stevens (published by Addison-Wesley). Note that most books on networking address networking from the perspective of a
C translation to Perl is left as an exercise for the reader.
The IO::Socket(3) manpage describes the object library, and the
Socket(3) manpage describes the low-level interface to
sockets. Besides the obvious functions in , you should also check out the modules file at your nearest
CPAN site. (See
or best yet, the Perl
FAQ for a description of what
CPAN is and where to get it.)
Section 5 of the modules file is devoted to ``Networking, Device Control (modems), and Interprocess Communication'', and contains numerous unbundled modules numerous networking modules, Chat and Expect operations,
CGI programming,
NNTP, Proxy, Ptty,
SMTP, Telnet, Threads, and ToolTalk--just to name a few.

我要回帖

更多关于 ecshop 在线提交表单 的文章

 

随机推荐