2016年12月29日13:45:27 
 
关于接口设计要说的东西很多,可能写一个系列都可以,vsd图都得画很多张,但是由于个人时间和精力有限,所有有些东西后面再补充
 
说道接口设计第一反应就是restful api 请理解一点,这个只是设计指导思想,也就是设计风格 ,比如你需要遵循这些原则

原则条件
REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。
在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI (Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。Hypermedia 是应用程序状态的引擎,资源表示通过超链接互联。

REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的

但是规则是很早之前的人设计的,但是it这个行业日新月异,业务结构复杂,变化性大,所有,你可以选择遵守,也是适当改变,方便开发,比如现在移动化发展快速,app接口,微信网页接口等

第一点要做的就是HTTPS  自2017年1月1号,开始新的ios都需要是https才能访问,版本好像是10.0系列开始,并且满足 苹果ATS安全

如果您的APP如果仍采用HTTP传输,那么,在Apple Store中您的APP将不再能被用户下载使用 ,所以,呵呵

场景分析和理论

首先说下几种运用场景:

1,后台服务端->app客户端 ios 安卓

一般来说单向信任加密解密就可以,客户端向服务器请求进行数据加密,发送服务器端进行解密,然后返回数据,

但是请求返回的数据是不加密的,信任是单向的,有时候也需要加密就是双向加密,效率来说就比较麻烦和文件返回用

base64进行传输,处理效率也比较差,特别是服务器端还有专门的文件服务器的,代码层级处理起来就比较麻烦,效率也

很差,做过ios和java的同学应该比较了解,直接作为文件传输,php文件上传的时候,会先把文件上传系统的临时文件目录

而不是先去验证权限在上传,全局变量$_FILE php在上传到其他文件服务器,或者移动到文件目录,如果客户端

没有做验证,或者允许大文件上传,就会存在temp文件夹爆炸,服务器宕机的危险,对于中小项目来说,不是被人恶意攻击

问题不大,这种情况下需要使用base64传输就可以先验证在判断是不是在做处理,然后就是在服务器验证时候加上针对每

个接入端标识存储,线上好处理具体问题来自哪些方面的接口,可以比较好的定位

2,后台服务端->html5 微信 浏览器

因为现在狠多为了一种开发多处使用,必须现在流行的html5混合app开发简单方便,如果不是做游戏,性能一般app足够

使用,不要被原生性能更好的屁话左右,为了适配机器app需要做无数处理,麻烦的很,而且对于小公司人力成本是巨大的,

而且不同语言的数据对接本身就是时间消耗,精力消耗,各种未知bug的调试,特别是第一次做个的人,有些东西在不同语言

显示方式根本不一样,比如 一个数组(php)

key=>0,name=>z key在ios解析的时候key不显示,安卓key显示NULL,呵呵,所以一些细节很麻烦,但是也并不是原生的

不好,只是就现在的技术潮流来说是这样,比如如果一些ios新特性,在app的混合开发框架肯定不会那么及时的更新api接口

,比如hbuilder,做的HTML5+,整体性能也肯定没有object-c好,swift个人没什么了解,所以不清楚,所以有利有弊,需要技

术经理,公司,开发人员来衡量。

htnl5页面在微信和浏览器里面需要的快速,如果每次都需要验证数据加密,机密,效率首先需要考虑,而且js处理session

和cookies,在页面不太好处理,很多现在很多都是纯js去渲染数据,如果使用php来混合编写也是可以的,使用php来模拟

数据加密,请求接口,这样写比较容易,但是无法再混合app中使用,如果你只是单独开发手机浏览器和微信里面来使用php

混写是没有问题的,如果是页面需要纯js处理数据的话么就需要注意我说的上面的问题

数据认证的话,可以简单做用户登录,比如微信自动等,根据name和password,salt计算一个唯一值作为token去数据库校验,

接口请求的时候js发送请求的时候校验即可

3,文件服务器 特殊处理

很多项目到了后期都需要考虑单独的文件服务器的问题,比如我前面博客说道的,挂在nfs文件服务,或者文件服务器接口

文件服务器处理起来比较麻烦,但是对于大的项目来说也是必要的,所有服务都需要接口化,在接口认证里面进行权限和

资源分配,这就是SOA的过程,比如使用文件接口,在富文本编辑器上传文件的时候,就需要改造富文本编辑器的文件上传

所以有点麻烦,uedit修改文件上传路劲,支持api文件接口 我这篇博客就有介绍,文件服务器也有好处,就是可以规避一部

分文件安全问题

4,后台服务端->游戏客户端

这个和html有些相似,又有些不同,一个是为了高效数据传输,保持socket稳定,或者数据传输的速度,还有就是保证

数据不被篡改,比如游戏作弊,考虑的东西比较多,还有比如一局游戏某个用户断线了,多久T掉,保证游戏继续进行下去,接口里面

需要处理的东西很多,因为游戏需要更新的状态很多,合理的设计表结构也是无比重要,比如一个buff就增加一个字段的话,一个玩家状态

对应就有几百个字段,传输数据当然就会大,一次更新那么数据,速度和性能就得再次考虑

(后面更新)

API接口理论设计实践

1, 加密解密

使用https的 公钥 私钥 加密解密,但是其实也是发送数据不加密,只是通过签名pkf crt来加密和验证签名的正确性,

很多借口采用的就是这种设计,但是数据安全性,我不是很理解,发送的是明文数据,木马程序可以很轻松的监听,

这种单向信任的明文数据发送验证借口,在浏览器访问的时候是有自带https的公钥,但是php curl的是可以不带证书访问的,

所有依然是接口里面是明文发送出去的,(如果这段有错,请反馈)

2接口理论

现在普遍采用3des加密,只是因为方便,现在都出都有php ios java的通用demo方便,不方便的地方也有很多,发送的就是加密的数据,

虽然是对称加密解密,单向信任,但是也是根据单个接口的账号密码进行二次验证,如果你key和sercet,被别人知道,也是可以解密出加密发送的数据,如

果想使用接口获取数据,还是得有该用的用户名和秘密的,当然现在流行的手机号码+手机短信验证码也是可以,但是服务器对应实现才可以,而且

最好ios和安卓开发框架需要支持session,cookies,https等为好,后面会有说明

推荐框架,但是需要支持这个多个协议

AFNetworking  ios
 OkGo  安卓  

3,数据传输格式

json xml  数据流  二进制 等等,但是数据解析方便来说json还是最方便的。建议json,主要是怕麻烦

4,支持请求方法POST GET 文件上传

麻烦一点就是文件上传,base64上传,或者直接文件上传,但是临时文件会出现上面说的系统临时文件爆了情况

5,兼容旧代码进行模拟登陆session cookies ,权限兼容

其他很多系统都需要去旧的rabc的里面去获取数据,模拟登录的时候,就会涉及到权限问题,当然你也可以单独剥离出来

这些功能,时间花的也比较长,特别是需要在客户端也要实现一定的权限的时候就麻烦了,你单独剥离出来,你又要去模拟

权限,当然,最好办法就是,接口对应的账号里面也去实现一套RBAC,这样后续开发人员就轻松了,看起来有点蛋疼,但是是比较

实际的问题,这个问题如果在设计接口的时候没有就考虑进去的话,就在对接一些旧功能就忒麻烦,特别是没有独立方法化的代码时候,

耦合度大,改起来麻烦的要死

6,目前接口优缺点。另一种接口设计方式

这个接口是所有终端同一个加密的秘钥,也就说一个终端秘钥丢失,就是可以获得其他终端发送的数据进行解密,获得发送数据,

所以中小项目,或者某个公司内部项目使用还不错,但是大项目api化的数据安全性就不是很好

另一种接口设计,就是每个终端都是自己使用的8位key和32位的 value 然后加密还是下面的加密方法,但是在吧user_key直接明文也带过

来先把数据库的这个用户的key 和value ,先解密,后吧对比解密数据里面的value 对比一样就通过,去处理数据,就像一般的

用户名密码登录校验一样,或者你想更安全就是md5('value .key') 去对比,全部不明文发送过来的数据

如果循环数据库的key 和value去解密,效率太低,app接口需要就速度和效率

7,一些细节问题

$_REQUEST['header'] 为什么要这样获取数据,因为在ios和安卓,字典必须是要key和vaule的,所以兼容
urldecode建议不要使用这对函数 使用

rawurldecode($str);
rawurlencode($str);

会出现+ 和%2D 空格,多种语言交互的时候这样的问题很多,所以要注意,特别是对接接口的时候,签名的时候

签名加密算法:sha1 长度40 (因为语言可能导致生产长度不一样)



 foreach (unserialize($res['contract']['idcards_file_ids']) as $k => $v) {

             $rr['user_idcard_data'][$k]['file_paths'] = app_standard_path_new($file_path['file_path']);
$rr['user_idcard_data'][$k]['file_id'] = $v['file_id'];
$rr['user_idcard_data'][$k]['name'] = $v['name']; }
}

如果把 $rr['user_idcard_data'] json传给app端,他就是个字典不是数组,对于ios和安卓来说

"customer_idcard_file": {
"1": {
"file_paths": "http://app.xinyzx.com/Uploads/personal_app_ios/201704/11/58ec42d23c090.jpeg",
"file_id": "717892",
"name": "work_card"
}
},

$rr['user_idcard_data'] = array_values($rr['user_idcard_data']);

需要处理成以下格式

"file_id":[
{
"file_paths":"58ec42d22f310.jpeg",
"file_id":"717891",
"name":"bank_card1"
},
{
"file_paths":"58ec42d23c090.jpeg",
"file_id":"717892",
"name":"work_card"
}
]
 

demo实例代码,测试代码

本代码基于tp3.1.2 目前不提供完整测试类,后面有时间在更新

目前只支持 key 8位 secket 32位,支持更多位数的后续跟新

Crypt3Des.class.php  加密算法 3DES

<?php

class Crypt3Des {

    private $key = "Symetric";
private $iv = "Symetric"; /**
* 构造,传递二个已经进行base64_encode的KEY与IV
*
* @param string $key
* @param string $iv
*/ function __construct($key, $iv) {
if (empty($key) || empty($iv)) {
echo 'key and iv is not valid';
exit();
}
$this->key = $key;
$this->iv = $iv;
} /**
*加密
* @param $value
* @return
*/
public function encrypt($value) {
$td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
$iv = base64_decode($this->iv);
$value = $this->PaddingPKCS7($value);
$key = base64_decode($this->key);
mcrypt_generic_init($td, $key, $iv);
$ret = base64_encode(mcrypt_generic($td, $value));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $ret;
} /**
*解密
* @param $value
* @return
*/
public function decrypt($value) {
$td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
$iv = base64_decode($this->iv);
$key = base64_decode($this->key);
mcrypt_generic_init($td, $key, $iv);
$ret = trim(mdecrypt_generic($td, base64_decode($value)));
$ret = $this->UnPaddingPKCS7($ret);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $ret;
} private function PaddingPKCS7($data) {
$block_size = mcrypt_get_block_size('tripledes', 'cbc');
$padding_char = $block_size - (strlen($data) % $block_size);
$data .= str_repeat(chr($padding_char), $padding_char);
return $data;
} private function UnPaddingPKCS7($text) {
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text)) {
return false;
} if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, - 1 * $pad);
} }

BaseAction.class.php

<?php

//API父类
class BaseAction extends Action { protected $user_id;
protected $appkey;
protected $secret;
protected $appIDname; //接入用户来源标示,可能在用户数据录入的时候会用到 public function _initialize() { myLog($_REQUEST, 'api_log');
if (empty($_REQUEST) && empty($_FILES)) {
$this->response(array('code' => 0, 'msg' => '发送数据不能为空'), 'json', 200);
}
$data = $_REQUEST['header']; if (empty($data)) {
$data = urldecode(file_get_contents('php://input')); //兼容php发送数据接收 和 php模拟测试,正式不一定 if (empty($data)) {
$this->response(array('code' => 0, 'msg' => '发送数据不能为空'), 'json', 200);
}
} $data = $this->crypt3des()->decrypt($data);
myLog($data, 'api_log');
$_POST = json_decode($data, true);
//api接口日志记录--打印发送数据
myLog($_POST, 'api_log'); $this->origin = $this->check_sign($_POST);
} function crypt3des() {
import('App.ORG.Crypt3Des');
$crypt3des = new Crypt3Des(base64_encode(C('crypt_key')), base64_encode(C('crypt_iv')));
return $crypt3des;
} function _empty() {
$this->response(array('code' => 0, 'msg' => '_empty,非法操作'), 'json', 200);
} protected function check_sign($data, $expires = 300) {
$params = array();
$params['timestamp'] = $data['timestamp'];
if (time() - strtotime($params['timestamp']) > $expires) {
$this->response(array('code' => 0, 'msg' => '签名已过期'), 'json', 200);
}
//数据库查询校验相关用户数据
$where['app_api_name'] = $data['appkey'];
$res = M('app_api_partner')->where($where)->find(); if (empty($res)) {
$this->response(array('code' => 0, 'msg' => 'appkey错误'), 'json', 200);
}
if ($res['api_status'] !== '0') {
$this->response(array('code' => 0, 'msg' => '目前api账户不可用'), 'json', 200);
}
if ($res['app_api_key'] !== $data['secret']) {
$this->response(array('code' => 0, 'msg' => '通信密钥错误'), 'json', 200);
}
$params['appkey'] = $res['app_api_name'];
$params['secret'] = $res['app_api_key']; $sign = $this->data_auth_sign($params); // $this->response(array('msg' => $params, 'code' => 0), 'json', 200);
if ($sign !== $data['sign']) {
$this->response(array('code' => 0, 'msg' => '签名错误'), 'json', 200);
} else {
myLog('签名解析正确', 'api_log');
$this->appIDname = $res['app_api_mark']; //返回接入的使用的名称
}
    }

    private function data_auth_sign($data) {
if (!is_array($data)) {
$data = (array) $data;
}
ksort($data);
$param = array();
foreach ($data as $key => $val) {
$param[] = $key . "=" . $val;
}
$param = join("&", $param);
$sign = sha1($param);
return $sign;
} }
C 获取系统配置数据

myLog 打印系统日志
function myLog($str, $flag = 'default') {
//if( APP_DEBUG != true )return '';
is_array($str) && $str = print_r($str, true);
$dir = SYSTEM_ROOT . '/Uploads/logs/' . $flag . '/'; !is_dir($dir) && @mkdir($dir, 0755, true);
$file = $dir . date('Ymd') . '.log.txt';
$fp = fopen($file, 'a');
if (flock($fp, LOCK_EX)) {
$content = "[" . date('Y-m-d H:i:s') . "]\r\n";
$content .= $str . "\r\n\r\n";
fwrite($fp, $content);
flock($fp, LOCK_UN);
fclose($fp);
return true;
} else {
fclose($fp);
return false;
}
}
随机生成一个8位随机字符串
function get_rand_str($len) {
$chars = array(
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
);
$charsLen = count($chars) - 1;
shuffle($chars);
$output = "";
for ($i = 0; $i < $len; $i++) {
$output .= $chars[mt_rand(0, $charsLen)];
}
return $output;
}
 
 

集成这个父控制就可以写你自己的代码了,下面是一个实力和一个接口测试的demo

<?php

//客户信息相关api接口类

class CustomerAction extends BaseAction {

    public function test() {

        $this->response(array('code' => 1, 'msg' => '测试成功', 'data' => $data), 'json', 200);
} }

测试接口的demo

 import('app.ORG.Crypt3Des');
$crypt3des = new Crypt3Des(base64_encode(C('crypt_key')), base64_encode(C('crypt_iv'))); $data['appkey'] = $_data['appkey'] = '11111111'; // 用于签名参数 对应C的取到的值
$data['secret'] = $_data['secret'] = md5('11111111'); //用于签名参数
$data['timestamp'] = $_data['timestamp'] = date('YmdHis', time()); //用于签名参数 $data['sign'] = $this->data_auth_sign($_data); $data = json_encode($data); $crypt_data = (urlencode($crypt3des->encrypt($data))); $url = "http://127.0.0.1/app.php/customer/test"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $crypt_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $return_data = curl_exec($ch); print_r($return_data);
echo '<hr />';
print_r(json_decode($return_data, true)); if ($error = curl_error($ch)) {
die($error);
}
// $rinfo = curl_getinfo($ch);
// P($rinfo);
curl_close($ch);

2017年6月24日23:17:10

这里补充一点,这个接口有个比较大的问题,就是没有版本控制,比如在接口加个V参数。

最新碰到一个问题就是,有个比较大的改版,因为ios发布不通过,导致安卓可以升级但是ios不能,所以整体升级就只能直接进行

解决发难,就是根据V参数的版本号去访问不同的比如,在访问控制器的ZxAction.class.php 的test方法的时候,实际访问的是Zxv3Action.class.php,或者 V3_zxAction.class.php,

在基础控制器里面处理一下,不然在大版本更新,如果某些接口是不能升级的话,就很需要这个参数进行处理对应的接口

/* 新增版本控制参数v很重要,根据版本控制,比如
*
* 访问 m=test&a=zx
* 里面有$_POST['v'] =v1
* 实际访问的就是 m=test&a=v1_zx
*/
if (!empty($_POST['v'])) {
$module = 'App_admin://' . MODULE_NAME;
$function = $_POST['v'] . '_' . ACTION_NAME;
A("$module")->$function(); 这里实例化的时候,会再次经过_initialize方法,有问题
die;
} 这个如果写在父控制器就会出现无限循环,出现问题 需要在自控制器里面加入这个,因为在初期没有考虑到这个,就只能补充这个,唉,经验不足
  public function __construct() {
parent::__construct();
if (!empty($_POST['v'])) {
$function = trim($_POST['v']) . '_' . ACTION_NAME;
$this->$function();
die;
}
}

其实还可以在父控制使用二级域名来控制,进行版本控制

php后台对接ios,安卓,API接口设计和实践完全攻略,涨薪必备技能的更多相关文章

  1. atitit.基于http json api 接口设计 最佳实践 总结o7

    atitit.基于http  json  api 接口设计 最佳实践 总结o7 1. 需求:::服务器and android 端接口通讯 2 2. 接口开发的要点 2 2.1. 普通参数 meth,p ...

  2. API接口设计

    1.场景描述 比如说我们要做一款APP,需要通过api接口给app提供数据.假设我们是做商城,比如我们卖书的.我们可以想象下这个APP大概有哪些内容: 1)首页:banner区域(可以是一些热门书籍的 ...

  3. Web API接口设计经验总结

    在Web API接口的开发过程中,我们可能会碰到各种各样的问题,我在前面两篇随笔<Web API应用架构在Winform混合框架中的应用(1)>.<Web API应用架构在Winfo ...

  4. Web API接口设计(学习)

    1.在接口定义中确定MVC的GET或者POST方式 由于我们整个Web API平台是基于MVC的基础上进行的API开发,因此整个Web API的接口,在定义的时候,一般需要显示来声明接口是[HttpG ...

  5. Java生鲜电商平台-API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)

    Java生鲜电商平台-API接口设计之token.timestamp.sign 具体设计与实现 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃 ...

  6. API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)

    Java生鲜电商平台-API接口设计之token.timestamp.sign 具体设计与实现 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃 ...

  7. Retrofit/OkHttp API接口加固技术实践(下)

    作者/Tamic http://blog.csdn.net/sk719887916/article/details/65448628 imageMogr2/auto-orient/strip%7Cim ...

  8. 免费安卓IOS测试API接口,后续会陆续增加接口

    各位博友好!开发的安卓或者ios的朋友们,经常会遇到想测试但是没有公开的api接口进行进行测试.但自己又不会开发服务端或者没有服务器,这里我免费提供了一整套API接口.欢迎大家调用,目标是方便大家. ...

  9. 微信小程序的Web API接口设计及常见接口实现

    微信小程序给我们提供了一个很好的开发平台,可以用于展现各种数据和实现丰富的功能,通过小程序的请求Web API 平台获取JSON数据后,可以在小程序界面上进行数据的动态展示.在数据的关键 一环中,我们 ...

随机推荐

  1. 【Zookeeper】源码分析之服务器(四)之FollowerZooKeeperServer

    一.前言 前面分析了LeaderZooKeeperServer,接着分析FollowerZooKeeperServer. 二.FollowerZooKeeperServer源码分析 2.1 类的继承关 ...

  2. Effective Java 第三版——61. 基本类型优于装箱的基本类型

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  3. 10.2.翻译系列:使用Fluent API进行属性映射【EF 6 Code-First】

    原文链接:https://www.entityframeworktutorial.net/code-first/configure-property-mappings-using-fluent-api ...

  4. 【转载】Mysql load data infile用法(万级数据导入,在几秒之内)

    https://blog.csdn.net/u014082714/article/details/53173975 http://blog.itpub.net/26506993/viewspace-2 ...

  5. Django-启动文件的制作

    在Django项目下的app.py中写入这几行代码,当启动的时候会找项目下名为:stark.py的文件并先加载 #app.py from django.apps import AppConfig cl ...

  6. Android Studio updating indices 一直刷新和闪烁

    Android Studio 更新到了 3.1.3 版本,在导入了工程以后,一直出现了 updating indices 刷新的情况,造成闪烁,在切换到其他视图以后,Android Studio 会一 ...

  7. 2.4 Apache Axis2 快速学习手册之XMLBeans 构建Web Service

    4. 使用XMLBeans生成服务(通过xml bean 命令将wsdl 文件生成java 代码) 要使用XMLBeans生成服务,请执行以下步骤. 通过在Axis2_HOME / samples / ...

  8. win8使用技巧

    windows 8操作系统相信大家已经不再陌生了,虽然正式版本还未发布,但不少朋友已经在使用微软事先推出的windows 消费者预览版,直白的说就是公测版,预览版是免费的,但仅可以使用一年,但其功能与 ...

  9. Socket网络编程--聊天程序(1)

    很早的一段时间,看了APUE和UNPv1了解了网络编程,但是但是只是看而已,没有具体的实践,趁现在没有什么事做,就来实践了解一下网络编程.写博客保存下来,方便以后用到的时候可以查到. 此次的聊天程序是 ...

  10. Socket网络编程--聊天程序(5)

    上一小节我们讲了使用select来避免使用多进程的资源浪费问题.上次只是实现了从多个客户端发送数据给服务器端,接下来就要实现从服务器端发送数据给各个客户端. 使用select多路转换处理聊天程序2 c ...