swoole
编译安装swoole: http://www.tudou.com/programs/view/bldF3zxVE-U/?resourceId=0_06_02_99
源码安装php扩展
apt-get install php5 php5-dev gcc autoconf
github上下载, 进入 src/ 源码目录
phpize -> 使用 autoconf 生成 configure 文件
configure 文件 -> shell脚本, 用来检测系统版本, 是否缺少类库, ./configure --help 看看有哪些编译参数可以用
./configure -> 生成 makefile
make -> 检测文件的依赖关系, 如果文件没有改动, 就不需要重新编译 / make -j 使用多线程, 注意最好加数字, 限制线程数, 防止占用所有cpu / 默认都是动态编译, 生成动态链接库, 也可以 make xxx_static 静态编译, 生成 .a 文件, 这样使用的源码被编译后就会包含使用到的 .a 文件
php -i |grep php.ini -> 找到 php.ini 文件的位置, 然后添加 extension=swoole.so 到文件最后即可
php -m |grep swoole -> 查看是否安装成功
- Linux版本: 2.6.27之上
- 编译器: gcc(C语言) / g++(c++) / clang(mac上经常使用), 修改 makefile -> 查找 cc, 即可使用不同的编译器
- autoconf -> phpize + ./configure 来编译源码 / cmake: 直接使用 src/makelist 完成上面2步
- ldconfig: 一般C语言的动态链接库都放到 /usr/lib 下, 可以新增一个配置放到 /etc/ld.so.conf.d/ 目录下执行 ldconfig, 就可以添加一个目录了
- nm/strip: 查看一个可执行文件(c文件), 定义了哪些函数
pecl安装php扩展: 直接使用 pecl search xxx 看看这个扩展是否添加到 pecl 库中, 如果有, 直接使用 pecl install xxx 即可
TCP服务器端/客户端的开发
网络4层协议: 我们做好 应用层 基本就够了, 如果不需要更底层方面的开发, 只做了解即可
- 3中网络类型:
127.0.0.1(本地主机, 只有本地可以连接) / 内网 / 外网;0.0.0.0, 允许上面3种网络接入 - tcpdump: 网络抓包工具
进程/线程模型
- 查看进程树:
pstree -a|grep server.php - 查看线程:
strace -pf - 模型: manager 进程 -> 管理所有 work 进程; work 进程: 完成业务逻辑
udp服务器/客户端开发
- netcat: 用来取代 telnet 来进行Linux下的网络编程测试
- tcp/udp的选择: 了解不够深刻, 只能简单的说, 除了特定场景, 直接使用tcp就行
开发php扩展
http://www.tudou.com/programs/view/Tp2HpLU9Ncg/?resourceId=0_06_02_99
c/c++编译器
php 原生 socket 编程
需求
使用socket进行通信, 服务端使用c++, 客户端使用php
基础
- socket相关函数:
socket_create() / socket_connct() / socket_write() / socket_read() / socket_recv() / socket_close() - pack/unpack 使用
- c/c++ 等语言的数据结构
- 网络通信使用的 大端/小端
主要难点
- php是弱类型语言, 和c++通信就涉及到数据格式的转化, 具体就是 字符流 <-> 字节流
- php7 之前的版本在window平台是只有32位的, 所有无法使用 pack() 中的 P/Q/J/j 等参数(见上面链接)
- 传输字符串给服务器, 因为 utf8 不是定长的, 服务器需要使用定长的编码来接收数据
基本概念
- 字节流, 最小单位是 1byte(8bit), 即 c 中的 char 类型, 因为是最小的传输单位, 所以不区分顺序
- 大端序/小端序/机器序: 实际就是字节的传输顺序, 比如现在有双字节 0x1234, 大端序在传输的时候为 0x34 0x12, 小端序为 0x12 0x34, 机器序即使用操作系统的顺序, 由于平台不同, 顺序不固定, 所以一般不作为传输使用的顺序
- c 中使用
unsigned xx时, 需要使用 pack 中的 小端序参数(N V P) 进行 pack/unpack - utf8 是变长编码(1-3个字节), ucs-2, utf-16 都是固定2个字节编码
- strlen() 是按照直接来计算的
踩到的坑
php需要64位支持才能使用 pack 的 q/Q/P/J 等参数, 但是window平台不支持, 最后只好使用php7, 但是 php7+laravel5.1 无法定位parseError(比如调用的某个函数参数不对)
2 .最开始的思路是使用>>运算符(移位), 但是如果不支持64位, 移位是使用不了的php中int是不会溢出的, 因为会自动转为float, 但是大家都知道, php的浮点计算的精度问题, 所以并不能保证最后我们传输的数是我们想要的
使用 iconv() 转了编码, 并不能直接发送给服务器, 而应该发送编码对应ascall值到服务器, 服务器才能正确的解析
解决64位的问题
使用64位环境: php7
使用32位环境
- 使用php的gmp库,
gmp_div_qr($int64, 0x100000000) - 如果数据源是mysql, 使用mysql进行数据处理, 比如
select bigint64 4294967296(使用pow(2,32)或者0x100000000都不行)
解决发送字符串的问题:
使用 iconv() 转码, 使用 ord() 获取转码之后的ascall编码
代码片段
- Buffer.php
class Buffer { // 处理 unsigned char // 其他位数类似处理 static function writeUInt8($num) { if (is_array ( $num )) { $args = array_merge ( (array)str_repeat ( 'C', count ( $num ) ), array_map('intval', $num) ); return call_user_func_array ( 'pack', $args ); } else { return pack ( 'C', intval($num) ); } } static function readUInt8($data, $len = 0) { if(($len = intval($len)) > 0) { return array_values(unpack ("C{$len}", $data)); } else { return unpack ('C', $data)[1]; } } // 处理字符串 static function writeString($str, $len = '*') { return pack("a{$len}", strval($str)); } static function readString($data, $len = '*') { return unpack("a{$len}", $data); } // 使用 gmp 扩展处理 uint64 数据类型 static function gmpUint64($num) { $arr = gmp_div_qr($num, '0x100000000'); $buffer = Buffer::writeUInt32($arr[1]) . Buffer::writeUInt32($arr[0]); return $buffer; } // 处理 utf8 字符串 static function utf16String($content) { $content = str_replace("\r\n", "\n", $content); $content = self::utf8_code($content, 'UTF-16LE'); $len = count($content); if($len < 255) { $size = 1 + $len; $buffer = Buffer::writeUInt8($len/2) . Buffer::writeUInt8($content); } else { $size = 1 + 4 + $len; $buffer = Buffer::writeUInt8(255) . Buffer::writeUInt32($len/2) . Buffer::writeUInt8($content); } return [$size, $buffer]; } // utf8 转码 static function utf8_code($name, $code){ $name = iconv('UTF-8', $code, $name); $len = strlen($name); $content=[]; // 每次处理2个字节 for($i=0;$i<$len-1;$i=$i+2){ $c = $name[$i]; $c2 = $name[$i + 1]; // 小端序 if (ord($c) > 0){ $content[] = ord($c); $content[] = ord($c2); }else{ $content[] = '0'; $content[] = ord($c2); } }