`
兰迪RANDY
  • 浏览: 98895 次
文章分类
社区版块
存档分类
最新评论

分享一个PHP写的简单webservice服务端+客户端

阅读更多
首先说明一下,这个小程序是我自己用PHP写成的一个简单的webservice系统,包括服务端的程序和客户端的程序,无论是服务端还是客户端在使用起来都非常的简单方便,也可以很方便的移植到自己的项目里,我自己也已经在稍微改造后用在了自己的项目里,应用到生产环境2个多月以来都很稳定,没有出过什么问题。

这个简单的webservice小程序有以下几个优点:
1. 简单、易用,几乎没有什么学习成本
2. 可扩展性很强,因为简单,所以你可以在这个基础上扩展出很多的东西,比如返回的数据格式上可以加上xml的支持等,这个就需要自己动手了
3. 数据传输量小,服务端到客户端的数据传输采用gzip压缩的方式,极大的减小了数据的体积,我自己做的测试是,一份4.7M的html数据在压缩后只有113K
4. 有一定的安全性,首先服务端和客户端之间的通讯会有密钥机制,同时又采取限定IP的方式保护了接口的安全。

当然,也有缺点:比如程序过于简单,没有对安全性和数据过过多的校验,这个在应用到生产环境之前一定要记得加强一下;客户端到服务端的请求默认采用get形式,传输的数据量有限,这个我会考虑在以后的改进中改为post,同时数据也采用gzip压缩以后传输。

好了,言归正传,下面介绍一下代码本身:

首先是服务端,服务端有一个主要的class组成:apiServer.php
<?php
/**
 * apiServer.php
 *
 * webservice主类
 *
 * @filename apiServer.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact	 homingway@gmail.com
 * @package  webservice
 */
define('API_AUTH_KEY',	'i8XsJb$fJ!87FblnW');
class apiServer{

	//请求参数
	public $request = array();

	//是否ip限制
	public $ip_limit = true;
    //允许访问的IP列表
	public $ip_allow = array('127.0.0.1','192.168.0.99');

	public $default_method = 'welcome.index';
	public $service_method = array();

	//私有静态单例变量
	private static $_instance = null;

    /**
     * 构造方法,处理请求参数
     */
	private function __construct(){
		$this->dealRequest();
	}

	/**
	 * 单例运行
	 */
	public static function getInstance(){
		if(self::$_instance === null){
			self::$_instance = new self();
		}
		return self::$_instance;
	}

	/**
	 * 运行
	 */
	public function run(){
		//授权
		if(!$this->checkAuth()){
			exit('3|Access Denied');
		}
		$this->getApiMethod();
		include_once(API_SERVICE_PATH.'/'.$this->service_method['service'].'.php');
		$serviceObject = new $this->service_method['service'];
		if($this->request['param']){
			$result = call_user_func_array(array($serviceObject,$this->service_method['method']),$this->request['param']);
		} else {
			$result = call_user_func(array($serviceObject,$this->service_method['method']));
		}
		if(is_array($result)){
			$result = json_encode($result);
		}
		$result = gzencode($result);
		exit($result);
	}

	/**
	 * 检查授权
	 */
	public function checkAuth(){
		//检查参数是否为空
		if(!$this->request['time'] || !$this->request['method']	|| !$this->request['auth']){
			return false;
		}

		//检查auth是否正确
		$server_auth = md5(md5($this->request['time'].'|'.$this->request['method'].'|'.API_AUTH_KEY));
		if($server_auth != $this->request['auth']){
			return false;
		}

		//ip限制
		if($this->ip_limit){
			$remote_ip = $this->getIP();
			$intersect = array_intersect($remote_ip,$this->ip_allow);
			if(empty($intersect)){
				return false;
			}
		}

		return true;
	}

	/**
	 * 获取服务名和方法名
	 */
	public function getApiMethod(){
		if(strpos($this->request['method'], '.') === false){
			$method = $this->default_method;
		} else {
			$method = $this->request['method'];
		}
		$tmp = explode('.', $method);
		$this->service_method = array('service'=>$tmp[0],'method'=>$tmp[1]);
		return $this->service_method;
	}

	/**
	 * 获取和处理请求参数
	 */
	public function dealRequest(){
		$this->request['time'] = $this->_request('time');
		$this->request['method'] = $this->_request('method');
		$this->request['param'] = $this->_request('param');
		$this->request['auth'] = $this->_request('auth');
		if($this->request['param']){
			$this->request['param'] = json_decode(urldecode($this->request['param']),true);
		}
	}

	/**
	 * 获取request变量
	 * @param string $item
	 */
	private function _request($item){
		return isset($_REQUEST[$item]) ? trim($_REQUEST[$item]) : '';
	}

	/**
	 * 设置IP限制
	 * @param bool $limit
	 */
	public function setIPLimit($limit=true){
		$this->ip_limit = $limit;
	}

	/**
	 * 获取客户端ip地址
	 */
	public function getIP(){
		$ip = array();
		if(isset($_SERVER['REMOTE_ADDR'])){
			$ip[] = $_SERVER['REMOTE_ADDR'];
		}
		if(isset($_SERVER['HTTP_VIA'])){
			$tmp = explode(', ',$_SERVER['HTTP_X_FORWARDED_FOR']);
			$ip = array_merge($ip,$tmp);
		}
		$ip = array_unique($ip);
		return $ip;
	}

}


然后在服务端的入口文件中调用该class,并启动服务即可,如:
<?php
/**
 * server.php
 *
 * 自定义数据接口的入口
 *
 * @filename server.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact	 homingway@gmail.com
 * @package  webservice
 */

//API的根目录
define('API_PATH',dirname(__FILE__));

//服务目录
define('API_SERVICE_PATH',API_PATH.'/service');
define('API_LIB_PATH',	API_PATH.'/lib');

//服务核心class
include_once(API_LIB_PATH.'/apiServer.php');

//运行
apiServer::getInstance()->run();


然后创建一个service的目录,里面就是自己的接口class,如welcome.php:
<?php
/**
 * welcome.php
 *
 * 功能代码
 *
 * @filename welcome.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact	 homingway@gmail.com
 * @package  webservice
 */

class welcome{

	public function index(){
		return 'hello service';
	}

}



下面是客户端的主程序:apiClient.php
<?php
/**
 * apiClient.php
 *
 * webservice客户端程序
 *
 * @filename apiClient.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact	 homingway@gmail.com
 * @package  webservice
 */

define('API_AUTH_KEY',	'i8XsJb$fJ!87FblnW');

class apiClient{

	public static function send($url,$method,$param=array()){
		$time = time();
		$auth = md5(md5($time.'|'.$method.'|'.API_AUTH_KEY));
		if(!is_array($param) || empty($param)){
			$json_param = '';
		} else {
			$json_param = urlencode(json_encode($param));
		}
		$api_url = $url.'?method='.$method.'&time='.$time.'&auth='.$auth.'&param='.$json_param;
		$content = file_get_contents($api_url);
		if(function_exists('gzdecode')){
			$content = gzdecode($content);
		} else {
			$content = self::gzdecode($content);
		}
		return $content;
	}

	public static function gzdecode($data) {
		$len = strlen ( $data );
		if ($len < 18 || strcmp ( substr ( $data, 0, 2 ), "\x1f\x8b" )) {
			return null; // Not GZIP format (See RFC 1952)
		}
		$method = ord ( substr ( $data, 2, 1 ) ); // Compression method
		$flags = ord ( substr ( $data, 3, 1 ) ); // Flags
		if ($flags & 31 != $flags) {
			// Reserved bits are set -- NOT ALLOWED by RFC 1952
			return null;
		}
		// NOTE: $mtime may be negative (PHP integer limitations)
		$mtime = unpack ( "V", substr ( $data, 4, 4 ) );
		$mtime = $mtime [1];
		$xfl = substr ( $data, 8, 1 );
		$os = substr ( $data, 8, 1 );
		$headerlen = 10;
		$extralen = 0;
		$extra = "";
		if ($flags & 4) {
			// 2-byte length prefixed EXTRA data in header
			if ($len - $headerlen - 2 < 8) {
				return false; // Invalid format
			}
			$extralen = unpack ( "v", substr ( $data, 8, 2 ) );
			$extralen = $extralen [1];
			if ($len - $headerlen - 2 - $extralen < 8) {
				return false; // Invalid format
			}
			$extra = substr ( $data, 10, $extralen );
			$headerlen += 2 + $extralen;
		}
		$filenamelen = 0;
		$filename = "";
		if ($flags & 8) {
			// C-style string file NAME data in header
			if ($len - $headerlen - 1 < 8) {
				return false; // Invalid format
			}
			$filenamelen = strpos ( substr ( $data, 8 + $extralen ), chr ( 0 ) );
			if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
				return false; // Invalid format
			}
			$filename = substr ( $data, $headerlen, $filenamelen );
			$headerlen += $filenamelen + 1;
		}

		$commentlen = 0;
		$comment = "";
		if ($flags & 16) {
			// C-style string COMMENT data in header
			if ($len - $headerlen - 1 < 8) {
				return false; // Invalid format
			}
			$commentlen = strpos ( substr ( $data, 8 + $extralen + $filenamelen ), chr ( 0 ) );
			if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
				return false; // Invalid header format
			}
			$comment = substr ( $data, $headerlen, $commentlen );
			$headerlen += $commentlen + 1;
		}

		$headercrc = "";
		if ($flags & 1) {
			// 2-bytes (lowest order) of CRC32 on header present
			if ($len - $headerlen - 2 < 8) {
				return false; // Invalid format
			}
			$calccrc = crc32 ( substr ( $data, 0, $headerlen ) ) & 0xffff;
			$headercrc = unpack ( "v", substr ( $data, $headerlen, 2 ) );
			$headercrc = $headercrc [1];
			if ($headercrc != $calccrc) {
				return false; // Bad header CRC
			}
			$headerlen += 2;
		}

		// GZIP FOOTER - These be negative due to PHP's limitations
		$datacrc = unpack ( "V", substr ( $data, - 8, 4 ) );
		$datacrc = $datacrc [1];
		$isize = unpack ( "V", substr ( $data, - 4 ) );
		$isize = $isize [1];

		// Perform the decompression:
		$bodylen = $len - $headerlen - 8;
		if ($bodylen < 1) {
			// This should never happen - IMPLEMENTATION BUG!
			return null;
		}
		$body = substr ( $data, $headerlen, $bodylen );
		$data = "";
		if ($bodylen > 0) {
			switch ($method) {
				case 8 :
					// Currently the only supported compression method:
					$data = gzinflate ( $body );
					break;
				default :
					// Unknown compression method
					return false;
			}
		} else {

		// I'm not sure if zero-byte body content is allowed.
		// Allow it for now...  Do nothing...
		}

		// Verifiy decompressed size and CRC32:
		// NOTE: This may fail with large data sizes depending on how
		//       PHP's integer limitations affect strlen() since $isize
		//       may be negative for large sizes.
		if ($isize != strlen ( $data ) || crc32 ( $data ) != $datacrc) {
			// Bad format!  Length or CRC doesn't match!
			return false;
		}
		return $data;
	}
}


使用起来非常简单,下面是一个调用程序:
<?php
/**
 * demo.php
 *
 * 客户端调用示例
 *
 * @filename demo.php
 * @version  v1.0
 * @update   2011-12-22
 * @author   homingway
 * @contact	 homingway@gmail.com
 * @package  webservice
 */

include_once('../client/apiClient.php');

$server_uri = 'http://localhost/webservice/server/server.php';

print_r(apiClient::send($server_uri,'welcome.index'));


本文所涉及到的所有代码及主程序我都打包到下面的zip文件中,可以直接下载,有什么疑问可以直接在下面留言回复。

转载请著名出处,本文地址:http://hmw.iteye.com/blog/1322406
2
0
分享到:
评论
3 楼 wangzuojun 2014-09-14  
wangzuojun 写道
您好,我想问一下这种方式,能够使用多种语言来连接服务端吗,意思就是,用.net怎么连接这个程序的服务端? 这个服务端是php写的,而且还做了密钥机制和限定IP的方式保护了接口,如果用.net来连接服务端是不是需要重新开发一个.net的服务端才行? 还是只需要用.net编写SDK就行了?
2 楼 wangzuojun 2014-09-14  
您好,我想问一下这种方式,能够使用多种语言来连接服务端吗,意思就是,用.net怎么连接这个程序的服务端? 这个服务端是php写的,如果用.net来连接服务端是不是需要重新开发一个.net的服务端才行? 还是只需要用.net编写SDK就行了?
1 楼 QQ223449684 2014-07-15  
 

相关推荐

    php编写webservice soap服务端及客户端

    描述中的“示例”和“说明文件”表明这个压缩包可能包含一个完整的PHP SOAP服务端和客户端示例代码,以及解释如何运行和测试这些示例的文档。文件名“CreateSoap”可能是创建SOAP服务或客户端的示例脚本。 在实际...

    使用php搭建webService服务器(代码实例)

    以下是一个简单的 WSDL 文件示例: ```xml 自定义名称" targetNamespace="命名空间" xmlns:tns="命名空间" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd=...

    webService服务端.rar

    WebService是一种基于网络的、分布式的模块化组件,...总的来说,"webService服务端.rar"包含了一个运行WebService服务端所需的基础元素。理解并掌握上述知识点,你就能有效地创建、部署和维护自己的WebService服务端。

    android webservice 客户端、服务端实例

    本实例将深入探讨如何构建一个Android Web Service客户端和服务端,并提供可直接运行的代码示例。以下是对这两个重要组件的详细说明。 **一、Android Web Service客户端** 在Android客户端,通常使用HTTP库来调用...

    ios移动社交app的客户端+webservice服务器端源码

    本资源提供的"ios移动社交app的客户端+webservice服务器端源码"正是一个完整的解决方案,涵盖了用户交互、数据交换以及服务端处理的核心部分。下面我们将深入探讨其中涉及的关键知识点。 1. **iOS客户端开发**: ...

    一个PHP+JSON的简单的WebService实例

    构建一个简单的PHP+JSON的WebService,首先需要定义一个PHP文件作为服务端,该文件会接收HTTP请求,并根据请求的参数执行相应的操作。例如,我们可能有一个函数`getUsers()`,它从数据库中获取用户列表,并使用json_...

    java+xfire(web service) + php 客户端 并实现文件加密

    标题 "java+xfire(web service) + php 客户端 并实现文件加密" 描述了一个集成项目,其中涉及到Java、XFire(一个基于Java的Web服务实现框架)和PHP的交互,以及文件加密功能。这个项目的目标是构建一个安全的分布式...

    Java调用php的webservice

    在本案例中,我们使用了NuSOAP库来创建一个简单的PHP Webservice服务端。NuSOAP是一个轻量级的PHP库,支持SOAP 1.1和1.2标准,能够快速构建或调用Webservice。 ##### 1. NuSOAP配置与引入 ```php require_once("lib...

    PHP写的webservice可以供C#调用

    标题中的“PHP写的webservice可以供C#调用”意味着我们正在讨论如何使用PHP创建一个Web服务,这个服务可以通过SOAP协议被C#客户端所调用。Web服务是一种通过网络(通常是HTTP)提供功能的方式,允许不同系统之间的...

    最全面的 PHP webservice 接口,服务器端,客户端接口

    WebService是一种跨编程语言和跨操作系统平台的远程调用技术。仅仅有通过Web Service,client和server才可以自由的用HTTP进行通信。不论两个程序的平台和变成语言是什么。 XML、SOAP和WSDL是Web Service平台的三大...

    .net动态调用php的webservice例子[含源码]

    内含nusoap 写的一个简单php服务端,客户端采用.net写的一个动态调用类 相关帖子 http://topic.csdn.net/u/20090819/19/94f3754b-7635-4377-8dc4-08cab4cd1aad.html?87815 如果还有什么不清楚。可以联系我

    php 实现 webservice

    在现代软件开发中,Web服务(WebService)作为一种标准的通信协议,允许不同平台、不同语言的应用程序之间进行数据交换和服务调用,极大地促进了系统的集成与互操作性。PHP作为广受欢迎的服务器端脚本语言,也提供了...

    webservice 第一个小项目

    通过这个"webservice 第一个小项目",你将能够掌握基本的WebService开发流程,理解其核心概念,并具备创建和使用简单WebService的能力。随着深入学习,你还将了解更多的高级特性,如WS-Security、WS-...

    基于gSoap的Webservice开发指导说明

    本文档旨在为开发者提供关于如何使用gSOAP工具来构建Webservice服务端和客户端的具体指导。通过本指南,开发者能够深入了解gSOAP的基本原理及其在Webservice开发中的应用。 #### 目的 本文的目的在于提供一系列...

    C#使用PHP服务端的Web Service.zip

    在实际应用中,C#客户端首先会通过HTTP请求发送一个SOAP消息到PHP服务端。服务端接收到请求后,nusoap库会解析SOAP消息,执行对应的服务方法,并返回一个响应。C#客户端再解析这个响应,获取结果。整个过程涉及到了...

    WebService.zip

    本压缩包包含的“WebService.zip”提供了作者亲自编写的WebService服务端及客户端示例,旨在解决网络上教程往往只涉及基础概念而缺乏实践应用的问题。 首先,让我们详细了解WebService的核心概念。WebService是一种...

    PHP实现WebService的简单示例和实现步骤

    在开发WebService时,需要创建WSDL(Web Services Description Language)文件,这是一个用于描述网络服务的XML语言文件,它能够告诉客户端如何与服务进行通信。 接下来,文章详细列出了实现PHPWebService的步骤: ...

    nusoap构建webservice (php)

    PHP作为一个强大的服务器端脚本语言,提供了多种实现SOAP客户端和服务端的方法。nusoap库就是其中之一,它是一个轻量级且功能强大的工具,允许开发者在PHP环境中轻松创建和消费SOAP Web服务。 **nusoap库介绍** ...

Global site tag (gtag.js) - Google Analytics