zoukankan      html  css  js  c++  java
  • HTTP 笔记与总结(5)socket 编程:使用 HTTP 协议模拟登录并发帖

    在 VeryCD 上注册两个帐号,发送和接收站内信,观察 POST 请求时发送的参数(h****2 发送给 d***2)。(最好用 FireFox 的 FireBug 工具,发送站内信之前选中 “保持” 以保证站内信发送完毕页面跳转后还能查看到之前发送的 POST 请求时的参数。找到 http://home.verycd.com/cp.php?ac=pm&op=send&touid=0&pmid=0,选中 “POST”,查看参数如下:

    formhash 1cf47360
    message test
    pmsubmit true
    pmsubmit_btn 发送
    refer http://home.verycd.com/space.php?do=pm&filter=privatepm
    username d***2

    ),如下图所示:

    新建文件 msg.php,用来模拟 POST 请求。首先测试 POST 主体信息的拼接:

    <?php
    require './http.class.php';
    
    $http = new Http('http://home.verycd.com/cp.php?ac=pm&op=send&touid=0&pmid=0');
    $body = array(
    		'formhash'=>'1cf47360',
    		'message'=>	'test',
    		'pmsubmit'=>'true',
    		'pmsubmit_btn'=>'发送',
    		'refer'=>'http://home.verycd.com/space.php?do=pm&filter=privatepm',
    		'username'=>'d***2'
    	);
    
    file_put_contents('./res.html', $http->post($body));
    

      

    同时修改 http.class.php,修改 setLine() 方法,在请求行的请求地址处加上参数 $this->url['query']:

    <?php
    /*
    	PHP + socket 编程
    	@发送 HTTP 请求
    	@模拟下载
    	@实现注册、登录、批量发帖
    */
    
    //http 请求类的接口
    interface Proto{
    	//连接 url 
    	function conn($url);
    
    	//发送 GET 请求
    	function get();
    
    	//发送 POST 请求
    	function post();
    
    	//关闭连接
    	function close();
    }
    
    class Http implements Proto{
    
    	//换行符
    	const CRLF = "
    ";
    
    	//fsocket 的错误号与错误描述
    	protected $errno = -1;
    	protected $errstr = '';
    
    	//响应内容
    	protected $response = '';
    
    	protected $url = null;
    	protected $version = 'HTTP/1.1';
    	protected $fh = null;
    
    	protected $line = array();
    	protected $header = array();
    	protected $body = array();
    
    	public function __construct($url){
    		$this->conn($url);
    		$this->setHeader('Host:' . $this->url['host']);
    	}
    
    	//写请求行
    	protected function setLine($method){
    		$this->line[0] = $method . ' ' . $this->url['path'] . '?' . $this->url['query'] . '  ' . $this->version;
    	}
    
    	//写头信息
    	protected function setHeader($headerline){
    		$this->header[] = $headerline;
    	} 
    
    	//写主体信息
    	protected function setBody($body){
    		//构造 body 的字符串
    		$this->body[] = http_build_query($body);
    	}
    
    	//连接 url 
    	public function conn($url){
    		$this->url = parse_url($url);
    		//判断端口
    		if(!isset($this->url['port'])){
    			$this->url['port'] = 80;
    		}
    		$this->fh = fsockopen($this->url['host'], $this->url['port'], $this->errno, $this->errstr, 3);
    	}
    
    	//构造 GET 请求的数据
    	public function get(){
    		$this->setLine('GET');
    		//发送请求
    		$this->request();
    		return $this->response;
    	}
    
    	//构造 POST 请求的数据
    	public function post($body = array()){
    		//构造请求行
    		$this->setLine('POST');
    
    		//设置 Content-type 和 Content-length
    		$this->setHeader('Content-type: application/x-www-form-urlencoded');
    		
    		//构造主体信息, 和 GET 请求不一样的地方
    		$this->setBody($body);
    		
    		$this->setHeader('Content-length: ' . strlen($this->body[0]));
    
    		//发送请求
    		$this->request();
    		return $this->response;		
    	}
    
    	//发送请求
    	public function request(){
    		//把请求行、头信息、主体信息拼接起来
    		$req = array_merge($this->line, $this->header, array(''), $this->body, array(''));
    		$req = implode(self::CRLF, $req);
    		echo $req;exit;
    
    		fwrite($this->fh, $req);
    
    		while(!feof($this->fh)){
    			$this->response .= fread($this->fh, 1024);
    		}
    
    		//关闭连接
    		$this->close();
    	}
    
    	//关闭连接
    	public function close(){
    		fclose($this->fh);
    	}
    }
    

      

    执行 msg.php,页面的源代码显示:

    POST /cp.php?ac=pm&op=send&touid=0&pmid=0  HTTP/1.1
    Host:home.verycd.com
    Content-type: application/x-www-form-urlencoded
    Content-length: 171
    
    formhash=1cf47360&message=test&pmsubmit=true&pmsubmit_btn=%E5%8F%91%E9%80%81&refer=http%3A%2F%2Fhome.verycd.com%2Fspace.php%3Fdo%3Dpm%26filter%3Dprivatepm&username=d***2
    

    主体信息没有问题。注释 http.class.php line:106,运行 msg.php,由于直接发送 POST 请求会因为没有登录而发生页面跳转而无法很好的观察和调试,所以把返回的数据写入日志文件(res.html),在 msg.php 文件中:

    file_put_contents('./res.html', $http->post($body));

    res.html 中能清楚的显示:

    也就是说需要登录才能发送 POST 请求。

    通过 HTTP 请求分析 Cookie

    在 setcookie.php 中设置 cookie:

    <?php
    header('Content-type:text/html; charset=utf-8');
    
    setcookie('user', 'dee');
    echo '<a href="readcookie.php">跳转</a>';
    

    运行,通过 FireFox 的 Firebug 工具查看请求头信息:

    点击 “跳转”;

    在 readcookie.php 中读取 cookie:

    <?php
    header('Content-type:text/html; charset=utf-8');
    echo 'I Know U are '.$_COOKIE['user'];
    

    页面输出:

      

    响应头信息中有 Cookie user=dee

    同样可以使用 telnet 发送带 cookie 的 HTTP 请求:

      

    要模拟 VeryCD 的 POST 请求,就要知道 POST 请求时所带的 Cookie 信息,否则只会出现没有登录的提示。

    另外需要注意的是,Cookie 和其他头信息是有可能关联的(Cookie 防伪),比如 Referer,User-Agent,因此要把所有的头信息都放入请求头信息:修改 msg.php

    <?php
    require './http.class.php';
    
    $http = new Http('http://home.verycd.com/cp.php?ac=pm&op=send&touid=0&pmid=0');
    
    $http->setHeader('Accept: http://home.verycd.com/cp.php?ac=pmtext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8');
    $http->setHeader('Accept-Encoding: gzip, deflate');
    $http->setHeader('Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3');
    $http->setHeader('Connection: keep-alive');
    	
    $http->setHeader('Cookie: Hm_lvt_c7849bb40e146a37d411700cb7696e46=1436679571,1436789767,1436791505,1436795485; member_id=3***** ; member_name=h******2; mgroupId=93; pass_hash=649ca21c97da98c3d183192240099369; rememberme=true; uchome_auth=b992hkSzDMnvjuKlyf%2BmE8U%2Fbt2GlJbLGC5FID5izJw5eDOK0egZ5hnPjsPbeVJOPWuQd2qYN0zTysI2zMMCpPhkrMOb ; uchome_loginuser=h******2; CNZZDATA1479=cnzz_eid%3D34351862-1436678182-http%253A%252F%252Fwww.verycd .com%252F%26ntime%3D1436798382; __utma=248211998.1001521947.1436679596.1436798878.1436801507.4; __utmz =248211998.1436798878.3.2.utmcsr=verycd.com|utmccn=(referral)|utmcmd=referral|utmcct=/; sid=dd9375417f28d09be9b80dc3b462adf093a00e71 ; BAIDU_DUP_lcr=http://www.baidu.com/link?url=BlvZ-TX3pY_kRvw_pHazRyRsX-fNXNqXsSfSwEt1fjq&wd=&eqid=ecf1add60000205d0000000255a3bfc7 ; Hm_lpvt_c7849bb40e146a37d411700cb7696e46=1436795485; __utmc=248211998; uchome_sendmail=1; uchome_checkpm =1; __utmb=248211998.1.10.1436801507; __utmt=1; dcm=1');
    $http->setHeader('Referer: http://home.verycd.com/cp.php?ac=pm');
    $http->setHeader('User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:39.0) Gecko/20100101 Firefox/39.0');
    
    
    $body = array(
    		'formhash'=>'1cf47360',
    		'message'=>	'test send msg',
    		'pmsubmit'=>'true',
    		'pmsubmit_btn'=>'发送',
    		'refer'=>'http://home.verycd.com/space.php?do=pm&filter=newpm',
    		'username'=>'d****2'
    	);
    
    file_put_contents('./res.html', $http->post($body));
    echo 'complete';
    

      

    修改 http.class.php line:55,protected ---> puclic,因为 setHeader 方法在 msg.php 中被外部调用:

    <?php
    /*
    	PHP + socket 编程
    	@发送 HTTP 请求
    	@模拟下载
    	@实现注册、登录、批量发帖
    */
    
    //http 请求类的接口
    interface Proto{
    	//连接 url 
    	function conn($url);
    
    	//发送 GET 请求
    	function get();
    
    	//发送 POST 请求
    	function post();
    
    	//关闭连接
    	function close();
    }
    
    class Http implements Proto{
    
    	//换行符
    	const CRLF = "
    ";
    
    	//fsocket 的错误号与错误描述
    	protected $errno = -1;
    	protected $errstr = '';
    
    	//响应内容
    	protected $response = '';
    
    	protected $url = null;
    	protected $version = 'HTTP/1.1';
    	protected $fh = null;
    
    	protected $line = array();
    	protected $header = array();
    	protected $body = array();
    
    	public function __construct($url){
    		$this->conn($url);
    		$this->setHeader('Host:' . $this->url['host']);
    	}
    
    	//写请求行
    	protected function setLine($method){
    		$this->line[0] = $method . ' ' . $this->url['path'] . '?' . $this->url['query'] . '  ' . $this->version;
    	}
    
    	//写头信息
    	public function setHeader($headerline){
    		$this->header[] = $headerline;
    	} 
    
    	//写主体信息
    	protected function setBody($body){
    		//构造 body 的字符串
    		$this->body[] = http_build_query($body);
    	}
    
    	//连接 url 
    	public function conn($url){
    		$this->url = parse_url($url);
    		//判断端口
    		if(!isset($this->url['port'])){
    			$this->url['port'] = 80;
    		}
    		$this->fh = fsockopen($this->url['host'], $this->url['port'], $this->errno, $this->errstr, 3);
    	}
    
    	//构造 GET 请求的数据
    	public function get(){
    		$this->setLine('GET');
    		//发送请求
    		$this->request();
    		return $this->response;
    	}
    
    	//构造 POST 请求的数据
    	public function post($body = array()){
    		//构造请求行
    		$this->setLine('POST');
    
    		//设置 Content-type 和 Content-length
    		$this->setHeader('Content-type: application/x-www-form-urlencoded');
    		
    		//构造主体信息, 和 GET 请求不一样的地方
    		$this->setBody($body);
    		
    		$this->setHeader('Content-length: ' . strlen($this->body[0]));
    
    		//发送请求
    		$this->request();
    		return $this->response;		
    	}
    
    	//发送请求
    	public function request(){
    		//把请求行、头信息、主体信息拼接起来
    		$req = array_merge($this->line, $this->header, array(''), $this->body, array(''));
    		$req = implode(self::CRLF, $req);
    		//echo $req;exit;
    
    		fwrite($this->fh, $req);
    
    		while(!feof($this->fh)){
    			$this->response .= fread($this->fh, 1024);
    		}
    
    		//关闭连接
    		$this->close();
    	}
    
    	//关闭连接
    	public function close(){
    		fclose($this->fh);
    	}
    }
    

    发送成功:

    注意:在拼接 header 头信息时,cookie 要写成一行,否则会报 400 错误。

      

  • 相关阅读:
    大数据学习day31------spark11-------1. Redis的安装和启动,2 redis客户端 3.Redis的数据类型 4. kafka(安装和常用命令)5.kafka java客户端
    大数据学习day29-----spark09-------1. 练习: 统计店铺按月份的销售额和累计到该月的总销售额(SQL, DSL,RDD) 2. 分组topN的实现(row_number(), rank(), dense_rank()方法的区别)3. spark自定义函数-UDF
    大数据学习day28-----hive03------1. null值处理,子串,拼接,类型转换 2.行转列,列转行 3. 窗口函数(over,lead,lag等函数) 4.rank(行号函数)5. json解析函数 6.jdbc连接hive,企业级调优
    大数据学习----day27----hive02------1. 分桶表以及分桶抽样查询 2. 导出数据 3.Hive数据类型 4 逐行运算查询基本语法(group by用法,原理补充) 5.case when(练习题,多表关联)6 排序
    大数据学习day26----hive01----1hive的简介 2 hive的安装(hive的两种连接方式,后台启动,标准输出,错误输出)3. 数据库的基本操作 4. 建表(内部表和外部表的创建以及应用场景,数据导入,学生、分数sql练习)5.分区表 6加载数据的方式
    大数据学习day25------spark08-----1. 读取数据库的形式创建DataFrame 2. Parquet格式的数据源 3. Orc格式的数据源 4.spark_sql整合hive 5.在IDEA中编写spark程序(用来操作hive) 6. SQL风格和DSL风格以及RDD的形式计算连续登陆三天的用户
    大数据学习day24-------spark07-----1. sortBy是Transformation算子,为什么会触发Action 2. SparkSQL 3. DataFrame的创建 4. DSL风格API语法 5 两种风格(SQL、DSL)计算workcount案例
    大数据学习day23-----spark06--------1. Spark执行流程(知识补充:RDD的依赖关系)2. Repartition和coalesce算子的区别 3.触发多次actions时,速度不一样 4. RDD的深入理解(错误例子,RDD数据是如何获取的)5 购物的相关计算
    大数据学习day22------spark05------1. 学科最受欢迎老师解法补充 2. 自定义排序 3. spark任务执行过程 4. SparkTask的分类 5. Task的序列化 6. Task的多线程问题
    [POJ] 1325 Machine Schedule(最小点覆盖)
  • 原文地址:https://www.cnblogs.com/dee0912/p/4640862.html
Copyright © 2011-2022 走看看