swoole

JerryXia 发表于 , 阅读 (2)

编译安装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 -> 查看是否安装成功

  1. Linux版本: 2.6.27之上
  2. 编译器: gcc(C语言) / g++(c++) / clang(mac上经常使用), 修改 makefile -> 查找 cc, 即可使用不同的编译器
  3. autoconf -> phpize + ./configure 来编译源码 / cmake: 直接使用 src/makelist 完成上面2步
  4. ldconfig: 一般C语言的动态链接库都放到 /usr/lib 下, 可以新增一个配置放到 /etc/ld.so.conf.d/ 目录下执行 ldconfig, 就可以添加一个目录了
  5. nm/strip: 查看一个可执行文件(c文件), 定义了哪些函数

pecl安装php扩展: 直接使用 pecl search xxx 看看这个扩展是否添加到 pecl 库中, 如果有, 直接使用 pecl install xxx 即可

TCP服务器端/客户端的开发

网络4层协议: 我们做好 应用层 基本就够了, 如果不需要更底层方面的开发, 只做了解即可

  1. 3中网络类型: 127.0.0.1(本地主机, 只有本地可以连接) / 内网 / 外网; 0.0.0.0, 允许上面3种网络接入
  2. tcpdump: 网络抓包工具

进程/线程模型

  1. 查看进程树: pstree -a|grep server.php
  2. 查看线程: strace -pf
  3. 模型: manager 进程 -> 管理所有 work 进程; work 进程: 完成业务逻辑

udp服务器/客户端开发

  1. netcat: 用来取代 telnet 来进行Linux下的网络编程测试
  2. tcp/udp的选择: 了解不够深刻, 只能简单的说, 除了特定场景, 直接使用tcp就行

开发php扩展

http://www.tudou.com/programs/view/Tp2HpLU9Ncg/?resourceId=0_06_02_99

c/c++编译器

php 原生 socket 编程

需求

使用socket进行通信, 服务端使用c++, 客户端使用php

基础

  1. socket相关函数: socket_create() / socket_connct() / socket_write() / socket_read() / socket_recv() / socket_close()
  2. pack/unpack 使用
  3. c/c++ 等语言的数据结构
  4. 网络通信使用的 大端/小端

主要难点

  1. php是弱类型语言, 和c++通信就涉及到数据格式的转化, 具体就是 字符流 <-> 字节流
  2. php7 之前的版本在window平台是只有32位的, 所有无法使用 pack() 中的 P/Q/J/j 等参数(见上面链接)
  3. 传输字符串给服务器, 因为 utf8 不是定长的, 服务器需要使用定长的编码来接收数据

基本概念

  1. 字节流, 最小单位是 1byte(8bit), 即 c 中的 char 类型, 因为是最小的传输单位, 所以不区分顺序
  2. 大端序/小端序/机器序: 实际就是字节的传输顺序, 比如现在有双字节 0x1234, 大端序在传输的时候为 0x34 0x12, 小端序为 0x12 0x34, 机器序即使用操作系统的顺序, 由于平台不同, 顺序不固定, 所以一般不作为传输使用的顺序
  3. c 中使用 unsigned xx 时, 需要使用 pack 中的 小端序参数(N V P) 进行 pack/unpack
  4. utf8 是变长编码(1-3个字节), ucs-2, utf-16 都是固定2个字节编码
  5. strlen() 是按照直接来计算的

踩到的坑

  1. php需要64位支持才能使用 pack 的 q/Q/P/J 等参数, 但是window平台不支持, 最后只好使用php7, 但是 php7+laravel5.1 无法定位parseError(比如调用的某个函数参数不对)
    2 .最开始的思路是使用 >> 运算符(移位), 但是如果不支持64位, 移位是使用不了的

  2. php中int是不会溢出的, 因为会自动转为float, 但是大家都知道, php的浮点计算的精度问题, 所以并不能保证最后我们传输的数是我们想要的

  3. 使用 iconv() 转了编码, 并不能直接发送给服务器, 而应该发送编码对应ascall值到服务器, 服务器才能正确的解析

解决64位的问题

  • 使用64位环境: php7

  • 使用32位环境

  1. 使用php的gmp库, gmp_div_qr($int64, 0x100000000)
  2. 如果数据源是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);            }        }