我们用到最多的模拟POST请求几乎都是使用php curl来实现了,没考虑到PHP socket也可以实现,今天看到朋友写了一文章,下面我来给大家分享一下PHP socket模拟POST请求实例。
以前模拟post请求俺都用PHP curl扩展实现来着,没想过PHP socket也可以实现。最近翻了下相关资料才发现原来没有那么高深,只是以前一直没有完全理解post的原理和本质而已,其实就是发送给目的程序一个标志为post的协议串如下:
POST /目的程序url HTTP/1.1
Accept: 接收信息格式
Referer: url来路
Accept-Language: 接收语言
Content-Type: application/x-www-form-urlencoded
Cookie: 网站cookie,不用俺过多解释,对吧?
User-Agent: 用户代理,操作系统及版本、CPU 类型、浏览器及版本等信息
Host: 要发送到的主机地址
Content-Length: 发送数据的长度
Pragma: 本地是否存在缓存
Cache-Control: 是否需要网页缓存
Connection: 连接状态
username=fengdingbo&password=111cn.net //post发送的数据
我想大家对表单的post方法提交数据应该是最熟悉不过了,例如我们想把用户名和密码发送给某个页面的时候,填写好相应的input框,点击提交按钮,最后把这个表单发送到action程序的就是以上数据。知道了这一点我想就不难了
这时候我们只需要用php的socket打开一个端口,例如80端口,把以上信息利用这个端口发送给目的程序就行了。
我们如何在一个端口上建立一个socket通道呢?
在PHP中是如此简单呢!
官方给的原型:
resource fsockopen ( string $hostname [, int $port = -1 [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") ]]]] )
下边是人类的理解:
fsockopen(主机名称,端口号,错误号的&变量,错误提示的&变量,超时时间)
主机名称就是你需要发送数据的目的地;
端口号就是这个目的程序会在哪个端口等着你的数据;
错误号的&变量,这个是如果建立socket不成功的时候返回的错误编号;
错误提示的&变量,是错误的时候返回的错误提示信息;
超时时间,就是post数据之后如果对方没有回应信息,等待的最长时间。
如果不出意外(你正确的设置fsockopen()函数的参数)的话,一个socket通道现在已经打开了,我们下一步需要做的就是,通过这个打开的通道把post请求协议发给目的程序,这时候可以使用fwrite或者fputs函数中的任意一个,把post的请求格式发给fsockopen()打开的资源句柄,这时候一个伟大的socket模拟的post请求就诞生了。
代码如下 |
复制代码 |
<?php
/**
* SOCKET扩展函数
* @copyright (c) 2013
* @author Qiufeng <fengdingbo@gmail.com>
* @link http://www.111cn.net
* @version 1.0
*/
/**
* Post Request
*
* @param string $url
* @param array $data
* @param string $referer
* @return array
*/
if ( ! function_exists('socket_post'))
{
function socket_post($url, $data, $referer='')
{
if( ! is_array($data))
{
return;
}
$data = http_build_query($data);
$url = parse_url($url);
if ( ! isset($url['scheme']) || $url['scheme'] != 'http')
{
die('Error: Only HTTP request are supported !');
}
$host = $url['host'];
$path = isset($url['path']) ? $url['path'] : '/';
// open a socket connection on port 80 - timeout: 30 sec
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if ($fp)
{
// send the request headers:
$length = strlen($data);
$POST = <<<HEADER
POST {$path} HTTP/1.1
Accept: text/plain, text/html
Referer: {$referer}
Accept-Language: zh-CN,zh;q=0.8
Content-Type: application/x-www-form-urlencodem
Cookie: token=value; pub_cookietime=2592000; pub_sauth1=value; pub_sauth2=value
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17
Host: {$host}
Content-Length: {$length}
Pragma: no-cache
Cache-Control: no-cache
Connection: closern
{$data}
HEADER;
fwrite($fp, $POST);
$result = '';
while(!feof($fp))
{
// receive the results of the request
$result .= fread($fp, 512);
}
}
else
{
return array(
'status' => 'error',
'error' => "$errstr ($errno)"
);
}
// close the socket connection:
fclose($fp);
// split the result header from the content
$result = explode("rnrn", $result, 2);
// return as structured array:
return array(
'status' => 'ok',
'header' => isset($result[0]) ? $result[0] : '',
'content' => isset($result[1]) ? $result[1] : ''
);
}
}
print_r(socket_post('http://www.111cn.net/', array('name='=>'qiufeng','password'=>md5('www.111cn.net'))));
/* e.g: socket_post('http://www.111cn.net', array('name='=>'qiufeng','password'=>md5('111cn.net'))); */
/* End of file socket_helper.php */
|
实际上,当socket通道打开时,我们传的COOKIE是正确的话,(截图运行的php代码来自上边,运行后返回的网页出现了我的用户名,说明对方网站已经承认我已经登录了)咱就可以干N多事情,比如刷帖,刷回复等,你们懂的,对吧?
好了上面还不够有说服力我们来看一个php socket实现图片上传
这个代码有两点要注意
一是他是http的post 请求;
二是表单上传协议,
下的请求 串适合任何语言.
代码如下 |
复制代码 |
<?php
$remote_server = "111cn.net";
$boundary = "---------------------".substr(md5(rand(0,32000)),0,10);
// Build the header
$header = "POST /api.php?action=twupload HTTP/1.0rn";
$header .= "Host: {$remote_server}rn";
$header .= "Content-type: multipart/form-data, boundary=$boundaryrn";
/*
// attach post vars
foreach($_POST AS $index => $value){
$data .="--$boundaryrn";
$data .= "Content-Disposition: form-data; name="".$index.""rn";
$data .= "rn".$value."rn";
$data .="--$boundaryrn";
}
*/
$file_name = "aaa.jpg";
$content_type = "image/jpg";
$data = '';
// and attach the file
$data .= "--$boundaryrn";
$content_file = file_get_contents('aaa.jpg');
$data .="Content-Disposition: form-data; name="userfile"; filename="$file_name"rn";
$data .= "Content-Type: $content_typernrn";
$data .= "".$content_file."rn";
$data .="--$boundary--rn";
$header .= "Content-length: " . strlen($data) . "rnrn";
// Open the connection
$fp = fsockopen($remote_server, 80);
// then just
fputs($fp, $header.$data);
// reader
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
|
在php中如果是简单的数组合并有很多的方法,但是我今天要求是根据数组ID然后重新合并数组,是要根据分类来操作。
最简单的数组合并我们只要使用array_merge即可
array_merge()将两个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面。返回作为结果的数组。
数组键名为数字键名时,要合并的两个数组中有同名数字KEY的时候,使用array_merge()不会覆盖掉原来的值,而使用“+”合并数组则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(注意:不是覆盖而是保留最先出现的那个值)。例子:
代码如下 |
复制代码 |
$array1 = array(1=>'0');
$array2 = array(1=> "data");
$result1 = $array2 + $array1;/*结果为$array2的值*/
print_r($result);
$result = $array1 + $array2 ;/*结果为$array1的值*/
print_r($result);
$result3 = array_merge($array2,$array1);/*结果为$array2和$array1的值,键名被重新分配*/
print_r($result3);
$result4 = array_merge($array1,$array2);/*结果为$array1和$array2的值,键名被重新分配*/
print_r($result4);
输出结果为:
Array ( [1] => data )
Array ( [1] => 0 )
Array (
[0] => data
[1] => 0
)
Array
(
[0] => 0
[1] => data
)
|
2.当相同数组键名为字符时,“+”运算符与键名为数字时一样,但array_merge()此时会覆盖掉前面相同键名的值。
例子:
代码如下 |
复制代码 |
$array1 = array('asd'=>'0');
$array2 = array('asd' => "data");
$result1 = $array2 + $array1;/*结果为$array2的值*/
print_r($result);
$result = $array1 + $array2 ;/*结果为$array1的值*/
print_r($result);
$result3 = array_merge($array2,$array1);/*结果为$array1*/
print_r($result3);
$result4 = array_merge($array1,$array2);/*结果为$array2*/
print_r($result4);
输出结果为:
Array ( [asd] => data )
Array ( [asd] => 0 )
Array ( [asd] => 0 )
Array ( [asd] => data )
|
讲了这么多费话,下面才是我要给各位朋友介绍的
例。
根据分类字段进行数组重组
代码如下 |
复制代码 |
<?php
//需要重组的数组
$arrar=array();
$array[]=array('ItemID' => 110126866896,'CategoryID'=>111);
$array[]=array('ItemID' => 120126866896,'CategoryID'=>112);
$array[]=array('ItemID' => 130126866896,'CategoryID'=>113);
$array[]=array('ItemID' => 140126866896,'CategoryID'=>114);
$array[]=array('ItemID' => 150126866896,'CategoryID'=>115);
$array[]=array('ItemID' => 160126866896,'CategoryID'=>116);
$array[]=array('ItemID' => 170126866896,'CategoryID'=>117);
$array[]=array('ItemID' => 118126866896,'CategoryID'=>111);
$array[]=array('ItemID' => 121126866896,'CategoryID'=>112);
$array[]=array('ItemID' => 132126866896,'CategoryID'=>113);
$array[]=array('ItemID' => 143126866896,'CategoryID'=>114);
$array[]=array('ItemID' => 154126866896,'CategoryID'=>115);
$array[]=array('ItemID' => 165126866896,'CategoryID'=>116);
$array[]=array('ItemID' => 176126866896,'CategoryID'=>117);
//数组根据分类进行重组
$newArray=array();
foreach($array as $val){
$newArray[$val['CategoryID']][]=$val;
}
//删除原始数组释放空间
$array=null;
unset($array);
print_r($newArray);
?>
|
今天在做一个php验证码程序时发现生成出来的图片不显示,开始以为是php gd库未打开,查用phpinfo查了是可以打开的啊,下面小编来给大家介绍此问题解决办法。
清除了bom,代码也是顶行开始写的,gd库也是开启的,从这里来看估计不是gd库的问题了,可能出在程序那句代码上。
生成验证码的代码:
代码如下 |
复制代码 |
<?php
/*
* 验证码产生程序
*/
$letter = '';
//获取随机数字
for ($i=0; $i<2; $i++) {
$letter .= chr(mt_rand(48,57));
}
//获取随机字母
for ($i=0; $i<2; $i++) {
$letter .= chr(mt_rand(65,90));
}
//重构字符顺序
$strs = str_split($letter);
shuffle ($strs);
$rndstring = "";
while (list ( , $str) = each ($strs)) {
$rndstring .= $str;
}
//如果支持GD,则绘图
if(function_exists("imagecreate"))
{
//向浏览器写入cookie
setcookie("zjs_ckstr", md5( strtolower($rndstring) ), time()+300,'/');//验证码有效期5分钟
$rndcodelen = strlen($rndstring);
//图片大小
$im = imagecreate(100,30);
//$im = imagecreatefromgif("code.gif");
//字体
$font_type = dirname(dirname(__FILE__))."/data/font/AvantGardeBookBT.ttf";
//背景颜色
$backcolor = imagecolorallocate($im,255,255,255);
//字体色
//不支持 imagettftext
$fontColor = ImageColorAllocate($im, 0,0,0);
//支持 imagettftext
$fontColor2 = ImageColorAllocate($im, 0,0,0);
//阴影
$fontColor1 = ImageColorAllocate($im, 255,255,25);
//添加背景杂点
$pixColor = imagecolorallocate($im, 199, 199, 199);//杂点颜色
for($j=0; $j<1000; $j++){
imagesetpixel($im, rand(0,100), rand(0,30), $pixColor);
}
//添加背景线
for($j=0; $j<=3; $j++){
//背景线颜色
$lineColor1 = ImageColorAllocate($im, rand(0, 255),rand(0, 255),rand(0, 255));
//背景线方向大小
imageline($im,rand(0,40),rand(3,25),rand(40,88),rand(3,25),$lineColor1);
}
$strposs = array();
//文字
for($i=0;$i<$rndcodelen;$i++){
if(function_exists("imagettftext")){
$strposs[$i][0] = $i*16+17;//x轴
$strposs[$i][1] = mt_rand(20,23);//y轴
imagettftext($im, 5, 5, $strposs[$i][0]+1, $strposs[$i][1]+1, $fontColor1, $font_type, $rndstring[$i]);
} else{
imagestring($im, 5, $i*16+7, mt_rand(2, 4), $rndstring[$i], $fontColor);
}
}
//文字
for($i=0;$i<$rndcodelen;$i++){
if(function_exists("imagettftext")){
imagettftext($im, 16,5, $strposs[$i][0]-1, $strposs[$i][1]-1, $fontColor2, $font_type, $rndstring[$i]);
}
}
header("Pragma:no-cachern");
header("Cache-Control:no-cachern");
header("Expires:0rn");
//输出特定类型的图片格式,优先级为 gif -> jpg
if(function_exists("imagegif")){
header("content-type:image/gifrn");
imagegif($im);
}else{
header("content-type:image/jpegrn");
imagejpeg($im);
}
ImageDestroy($im);
}
?>
|
感觉是不是没有问题了,后来百度发现一高人说关键是加入了ob_clean,了这个让我想了原因。
解决办法
ob_clean(); //关键代码,防止出现'图像因其本身有错无法显示'的问题。
加到 header 输出之前
代码如下 |
复制代码 |
header('Content-Type: image/png');
|
在php中如果我要用substr()截取字符串全英文的没问题,如果包括有中文或英文就会悲剧了,但大家也
别切我们可以使用其它办法来解决。
php截取中文字符串出现乱码,这是最近发现的事情,先前我曾经写过一篇关于自动生成meta信息的文章
,那篇关于利用php截取文章前多少字作为description方法,但是出现了IE6无法加载CSS的现象,这里
做一个补充。
首先要明确这么一个问题,之所以会出现IE6偶尔无法加载CSS的现象,是因为文件出现了乱码,导致后
面的加载CSS的link无法被IE6正确解析。因此就看到了一个纯HTML页面,没有CSS,赤裸裸!
明确了问题,剩下的问题就好解决了,就是防止乱码,既然万戈所提供的函数出现了乱码,就重新去找
了一个php函数来解决这个乱码的问题。
substr()函数可以分割文字,但要分割的文字如果包括中文字符往往会遇到问题。
mb_substr()这个函数的用法与substr()相似,只是在最后要加入多一个参数,以设定字符串的编码。
通过这里大几就应该理解我改进万戈方法的原因了~~
下面再介绍几个更高级处理办法
例1
代码如下 |
复制代码 |
function func_chgtitle($str,$len) { //$length我们允许字符串显示的最大长度
$tmpstr = "";
$strlen = $len;
for($i = 0; $i < $strlen; $i++) {
if(ord(substr($str, $i, 1)) > 0xa0) {
$tmpstr .= substr($str, $i, 2);
$i++;
} else
$tmpstr .= substr($str, $i, 1);
}
return $tmpstr;
}
|
例2
字符串编码为UTF-8的,一个中文字符占三个字节:
public static function chinesesubstr($str, $start, $len) { // $str指字符串,$start指字符串的
起始位置,$len指字符串长度
$strlen = $start + $len; // 用$strlen存储字符串的总长度,即从字符串的起始位置到字符
串的总长度
代码如下 |
复制代码 |
for($i = $start; $i < $strlen;) {
if (ord ( substr ( $str, $i, 1 ) ) > 0xa0) { // 如果字符串中首个字节的ASCII序数
值大于0xa0,则表示汉字
$tmpstr .= substr ( $str, $i, 3 ); // 每次取出三位字符赋给变量$tmpstr,即等
于一个汉字
$i=$i+3; // 变量自加3
} else{
$tmpstr .= substr ( $str, $i, 1 ); // 如果不是汉字,则每次取出一位字符赋给
变量$tmpstr
$i++;
}
}
return $tmpstr; // 返回字符串
}
|
在php中生成随机数据我们可以使用rand,mt_rand都可以生成指定范围内随机数据了,下面小编来给各位同学介绍一下方法。
调用mt_rand()这个方法可以生成随机数字,参数是范围的最小值和最大值,函数会返回最小值和最大值之间的一个随机数字。
要生成真正的随机数,对于计算来说不是一件容易的事。
php中两种方法可以生成随机数,一个经典的函数叫rand(),另一个更出色的函数是mt_rand()。
例1
代码如下 |
复制代码 |
$random =rand(0,1000);
或者
<?php
$rand = mt_rand(1, 100);
echo $rand;
?>
|
例2
代码如下 |
复制代码 |
srand((double)microtime()*1000000);
$random =rand(0,1000);
|
例3
代码如下 |
复制代码 |
/**
*获取一定范围内的多个随机数字
*/
function yang_numberRand($begin = 0, $end = 20, $limit = 5){
$rand_array = range($begin, $end);
shuffle($rand_array); //调用现成的数组随机排列函数
return array_slice($rand_array, 0, $limit); //截取前$limit个
}
|