在PHP中使用Sockets 从Usenet中获取文件


Posted in PHP onJanuary 10, 2008

作者:Armel Fauveau 
原文地址:http://www.phpbuilder.net/columns/armel20010427.php3
PHP能够打开远程或者本地服务器的sockets!这里是一个使用socket的简单的例子:连接到Usenet的新闻服务器,与服务器沟通,并从一个精确的新闻分组中下载一些文章。

使用PHP打开Socket
使用fsockopen()来打开一个Socket。这个函数在PHP3和PHP4中都存在。函数的原型如下:
<?php

intfsockopen 
    (string hostname, 
        int port [, 
        int errno [, 
        string errstr [, 
        double timeout]]])
?>
对于网络主机,它将建立一个TCP的Socket的连接到主机名的端口上。主机名可以是域名或者IP地址。对于UDP连接,你需要明确指出其协议:udp://hostname。对于unix主机,主机名将在socket的路径中使用,在这个例子中端口必须设置成0。可选项timeout可以用来设置连接超时的秒数。
关于fsockopen()的更多信息可以访问http://www.php.net/manual/function.fsockopen.php

网络新闻传输协议(NNTP)
访问一个usenet新闻服务器需要用到一个特别的协议,称作NNTP,即网络新闻传输协议标准。这个协议的详细资料在RFC977中,你可以在http://www.w3.org/Protocols/rfc977/rfc977.html中查看到。这个文档详细的描述了如何使用不同的命令来连接并且和NNTP服务器对话。

连接服务器
连接到NNTP服务器需要知道服务器的主机名(或者IP地址)和它将要监听的端口。另外建议你加上一个超时的时间,这样连接失败的时候就不会“冻结”程序。
<?php
$cfgServer    = "your.news.host";
$cfgPort    = 119;
$cfgTimeOut    = 10;
// open asocket
if(!$cfgTimeOut)
    // without timeout
    $usenet_handle = fsockopen($cfgServer, $cfgPort);
else
    // with timeout
    $usenet_handle = fsockopen($cfgServer, $cfgPort, &$errno, &$errstr, $cfgTimeOut);
if(!$usenet_handle) {
    echo"Connexionfailed\n";
    exit();
}    
else {
    echo"Connected\n";
    $tmp = fgets($usenet_handle, 1024);
}
?>

与服务器交互
现在我们已经连接上服务器了,而且能够通过先前打开的socket连接与服务器进行交互。让我们对服务器说“我们要从某一新闻分组中获取到最新的10篇文章”。RFC977定义了如何选择正确的新闻分组的命令,如下:
GROUPggg
必需的参数ggg是你将要选择的新闻分组的名字,比如net.news。使用list命令你可以获取到一组有效的新闻列表。成功选择响应会返回组中首尾两篇新闻的新闻号以及对存档新闻号估计。
比如

chrome:~$ telnetmy.news.host 119
Trying aa.bb.cc.dd...
Connected tomy.news.host.
Escape character is'^]'.
200 my.news.hostInterNetNews NNRP server INN 2.2.2 13-Dec-1999 ready (posting ok).
GROUP alt.test
211 232 222996 223235alt.test
quit
205 .
在接受到命令“GROUP alt.test”,新闻服务器返回了“211232 222996 223235 alt.test”。其中211是RFC标识码(简单的解释说命令已经成功的执行—查看RFC你可以获取更加详细的资料),返回信息说明其中有232篇文章,其中最旧的新闻的索引号是222996,而最新的新闻索引号是223235。现在让我们计算下:222996+232并不等于232235。这丢失的文章或者从这服务器移除出去了,或者被他的作者取消了(是的,这是可能的,也是很容易实现的),或者是删除了。
小心起见,在选择新闻分组之前,服务器可能需要认证,当然这是由服务器是否公开或者私有来决定的。一般是允许任何人获取新闻,但发表新闻需要通过认证。
<?php
//$cfgUser    = "xxxxxx";
//$cfgPasswd    = "yyyyyy";
$cfgNewsGroup    = "alt.php";
// identification required on private server
if($cfgUser) {
    fputs($usenet_handle, "AUTHINFO USER".$cfgUser."\n");
    $tmp = fgets($usenet_handle, 1024);
    fputs($usenet_handle, "AUTHINFO PASS ".$cfgPasswd."\n");
    $tmp = fgets($usenet_handle, 1024);
    // check error
    if($tmp != "281Ok\r\n") {
        echo "502Authentication error\n";
        exit();
    }    
}
// select newsgroup
fputs($usenet_handle, "GROUP ".$cfgNewsGroup."\n");
$tmp = fgets($usenet_handle, 1024);
if($tmp == "480 Authentication required for command\r\n") {
    echo "$tmp\n";
    exit();
}    
$info = split(" ", $tmp);
$first = $info[2];
$last = $info[3];

print "First : $first\n";
print "Last : $last\n";
?>

获取一些文章
现在我们已经有最新文章的A索引号,那就能很容易的获取最新的十篇文章。RFC977指出使用ARTICLE命令可以和文章的索引号或者消息的ID一起使用。为了小心起见,在这里,文章的索引号和消息ID是不同的,因为每个新闻服务器定义不同,所以在不同的新闻服务器上相同文章的索引号都会不一样的,但是消息ID好是唯一的(包含在文章的头部中)
<?php
$cfgLimit    = 10;
// upload last articles
$boucle=$last-$cfgLimit;
while ($boucle <= $last) {
    set_time_limit(0);
    fputs($usenet_handle, "ARTICLE$boucle\n");    
    $article="";
    $tmp = fgets($usenet_handle, 4096);
    if(substr($tmp,0,3) != "220") {
        echo "+----------------------+\n";
        echo "Error onarticle $boucle\n";
        echo "+----------------------+\n";
    }
    else {
        while($tmp!=".\r\n") {
            $tmp = fgets($usenet_handle, 4096);
            $article = $article.$tmp;
        }        
        echo "+----------------------+\n";
        echo "Article$boucle\n";
        echo "+----------------------+\n";
        echo "$article\n";
    }    
    $boucle++;
}
?>
我们仅仅从这个服务器的这个分组上获取了十条最新的新闻。你也可以使用HEAD命令来至获取文章的头部信息,或者使用BODY命令来获取新闻的正文。

关闭连接
使用fclose()函数你就可以结束与NNTP服务器之间的会话,当然你可以些一个新的文件,如下:
<?php
// close connexion
fclose($usenet_handle);
?>
更多关于fclose()的信息,请看:http://www.php.net/manual/function.fclose.php

结论
本文中,我们只说明了在确定的情况下如何打开、使用和关闭一个socket连接:连接上一个NNTP服务器然后从新闻分组中取回一些文章。使用POST命令在NNTP服务器上发表一篇文章并不复杂多少。
因此,下一步就是编写一个新闻客户端(并去掉一些Netscape),它需要能很容易的保存文章,并使用一些搜索引擎(比如htgid, http://www.htdig.org/)来索引这些文章,而且要有一个WEB应用程序能进行新闻分组下的关键字搜索。这里有一个例子,你可以访问http://www.phpindex.com/ng/去下载。

PHP 相关文章推荐
PHP个人网站架设连环讲(二)
Oct 09 PHP
PHP学习之整理字符串
Apr 17 PHP
php程序的国际化实现方法(利用gettext)
Aug 14 PHP
Thinkphp模板中使用自定义函数的方法
Sep 23 PHP
浅析php与数据库代码开发规范
Aug 08 PHP
PHP实现CSV文件的导入和导出类
Mar 24 PHP
jQuery+PHP发布的内容进行无刷新分页(Fckeditor)
Oct 22 PHP
php接口技术实例详解
Dec 07 PHP
Linux服务器下PHPMailer发送邮件失败的问题解决
Mar 04 PHP
Thinkphp结合ajaxFileUpload实现异步图片传输示例
Mar 13 PHP
tp5(thinkPHP5)框架实现多数据库查询的方法
Jan 10 PHP
深入理解PHP+Mysql分布式事务与解决方案
Dec 03 PHP
php扩展ZF――Validate扩展
Jan 10 #PHP
set_include_path在win和linux下的区别
Jan 10 #PHP
php模板之Phpbean的目录结构
Jan 10 #PHP
Phpbean路由转发的php代码
Jan 10 #PHP
php框架Phpbean说明
Jan 10 #PHP
深入解析php模板技术原理【一】
Jan 10 #PHP
php下MYSQL limit的优化
Jan 10 #PHP
You might like
php 数组的合并、拆分、区别取值函数集
2010/02/15 PHP
ezSQL PHP数据库操作类库
2010/05/16 PHP
PHP cdata 处理(详细介绍)
2013/07/05 PHP
Destoon模板制作简明教程
2014/06/20 PHP
PHP isset()与empty()的使用区别详解
2017/02/10 PHP
javascript 函数使用说明
2010/04/07 Javascript
Underscore.js 的模板功能介绍与应用
2012/12/24 Javascript
如何判断元素是否为HTMLElement元素
2013/12/06 Javascript
jQuery的end()方法使用详解
2015/07/15 Javascript
Js获取图片原始宽高的实现代码
2016/05/17 Javascript
JS 实现缓存算法的示例(FIFO/LRU)
2018/03/20 Javascript
Vue之mixin全局的用法详解
2018/08/22 Javascript
使用layui监听器监听select下拉框,事件绑定不成功的解决方法
2019/09/28 Javascript
js实现页面导航层级指示效果
2020/08/25 Javascript
jQuery实现穿梭框效果
2021/01/19 jQuery
[06:30]DOTA2英雄梦之声_第15期_死亡先知
2014/06/21 DOTA
python使用str &amp; repr转换字符串
2016/10/13 Python
python利用rsa库做公钥解密的方法教程
2017/12/10 Python
python遍历文件夹下所有excel文件
2018/01/03 Python
python+opencv识别图片中的圆形
2020/03/25 Python
Python如何基于rsa模块实现非对称加密与解密
2020/01/03 Python
Python vtk读取并显示dicom文件示例
2020/01/13 Python
numpy实现RNN原理实现
2021/03/02 Python
HTML5印章绘制电子签章图片(中文英文椭圆章、中文英文椭圆印章)
2019/06/03 HTML / CSS
德国户外商店:eXXpozed
2020/07/25 全球购物
培训演讲稿范文
2014/01/12 职场文书
机关单位人员学雷锋心得体会
2014/03/10 职场文书
民事授权委托书范文
2014/08/02 职场文书
2014年最新党员对照检查材料汇总
2014/09/15 职场文书
购房个人委托书范本
2014/10/11 职场文书
二审代理词范文
2015/05/25 职场文书
何时使用Map来代替普通的JS对象
2021/04/29 Javascript
解决Maven项目中 Invalid bound statement 无效的绑定问题
2021/06/15 Java/Android
Python 键盘事件详解
2021/11/11 Python
Mysql多层子查询示例代码(收藏夹案例)
2022/03/31 MySQL
《群青的幻想曲》京力秋树角色PV公开
2022/04/08 日漫