• 2016-03-08

    Xcode 如何让 Objective C 项目链接 C++ 静态库

    Views: 4279 | 1 Comment

    如果你的 OC 项目引入了一个 C++ 静态库, 那么编译时会提示找不到 vtable 之类的的错误, 因为 OC 是 C 语言, 无法识别 C++ 的静态库, 解决方法是让 Xcode 认为你在使用 C++/Objective-C++, 假装也行.

    所以解决方法是:

    • 往项目里添加一个.cpp文件, 文件是空的.
    • 将 main.m 改名为 main.mm
    Posted by ideawu at 2016-03-08 18:50:50
  • 2015-07-16

    经典的 TCP socket 读取报文错误

    Views: 10412 | 5 Comments

    面试了很多做了多年网络编程的人, 从TCP socket中读取报文这项基本技能, 许多人都做不对. 经典的错误用法是:

    char buf[1024]; // 1024或者更大
    read(sock, buf, sizeof(buf));
    if(parse(buf) == 1){
        // 报文解析完毕
    }else{
        // 不是一个完整的报文, 丢弃
    }
    

    这是非常经典的错误! 没有任何文档或者手册表明, read()会读到*完整*的报文, 对于read()函数来说, 它只知道字节流, 不知"报文"为何物. read()可能只读到了1个字节的数据就返回... 而且, read()返回的数据的长度, 和对方write()不是一一对应的. 对方调用1次write(), 可能本方要调用1次或者2次或者更多次read().

    这个错误之所以经典, 是因为在局域网条件下, 且报文非常小的情况下, 一般(仅仅是一般, 不是100%)write()和read()是一一对应的, 所以, 有些人即使是在BAT公司写了10年网络编程, 也发现不了这个错误. 这个错误就是所谓的"TCP粘包/断包".

    看看这个PDF: 高性能并发网络服务器设计与实现.pdf, 看看如何正确地从TCP socket中读取报文. 或者看看 Sim C++ 框架的源码, 了解下 Sim 是如何从 TCP socket 中读取一个报文: https://github.com/ideawu/sim/blob/master/src/server.cpp#L143

    Posted by ideawu at 2015-07-16 16:41:23
  • 2015-05-18

    简单的 C++ 网络服务框架 Sim 介绍

    Views: 9190 | 5 Comments

    可测性

    Sim 采用了一种极简的报文格式, 在你没有开始写 Client 时, 你就可以利用手上现成的工具, 如 nc, telnet 来测试. 可测性非常重要, 你可以随时查看代码的执行效果, 这对开发者的心理会产生非常正面的促进作用. 即使你不想使用 nc 这样的命令, 你想写 PHP 或者 Python 脚本, 也是非常简单的.

    例如, 你采用 Sim 框架写了一个计算加法的服务器. 这时怎么测试呢? 很简单! 在命令行执行

    $ nc 127.0.0.1 8800
    add 1 3
    ok 4
    

    随时随地地通过实际的输入和输出来测试程序, 会让程序员的心理更愉悦. 如果通过枯燥的单元测试, 进行大量参数配置, 只会让人心慌, 影响开发效率.

    内置 Log/Config 工具

    Sim 采用了 SSDB 项目的日志和配置代码, 让你的服务器立即就拥有日志输出功能和可配置性. 日志非常重要, 我几乎不使用 gdb 这样底层的调试工具. 有了 Config, 像修改端口这样的简单需求, 就不用再修改代码重新编译了.

    进程管理

    Sim 提供的工具可以让你的程序启动为后台进程, 不用借助 supervisor, nohup 这样的额外工具.

    关于性能

    记住, 不要一开始就关心性能, 计算机和 C++ 比你相像的要快得多!

    其它

    Sim 完全开源, 基于 New BSD 协议.

    项目地址: https://github.com/ideawu/sim

    Posted by ideawu at 2015-05-18 17:22:10
  • 2015-05-08

    在Linux进行IO的正确姿势

    Views: 9133 | 9 Comments

    很多C/C++程序虽然在做网络编程, 但大多用别人封装好的库, 对底层不甚了解, 感觉 IO 操作不是很简单吗? 我敢说, 大多数人进行 IO 的姿势都不对, 所谓的 IO, 主要是 read()/write() 两个函数.

    先说错误的 IO 读操作:

    int ret = read(fd, buf, len);
    if(ret == -1){
        exit(-1);
    }else if(ret == 0){
        close(fd);
    }
    

    看起来好像很正确的样子, 返回值也判断了, 不仅判断 -1, 还判断 0, 应该姿势正确吧? 错! 完全错误! 因为你忽略了 errno 的处理. 仔细看文档, 函数返回 -1 不能完全代表 fd 错误, 还需要结合 errno.

    接下来这样改:

    int ret = read(fd, buf, len);
    if(ret == -1){
        if(errno == EINTR){
            // 怎么办?
        }else if(errno == EAGAIN){
            // 怎么办?
        }
        exit(-1);
    }else if(ret == 0){
        close(fd);
    }
    

    EINTR 表示 read() 函数调用被系统中断了, 调用者和 fd 都没有问题, 有问题的是操作系统. 而 EAGAIN 是在非阻塞 IO 时会出现. 上面的代码判断了 errno, 但不知道下一步该怎么做, 还不行.

    在 Linux 下进行 IO 操作的正确姿势是:

    while(1){
        int ret = read(fd, buf, len);
        if(ret == -1){
            if(errno == EINTR){
                continue;
            }else if(errno == EAGAIN){
                // 根据你和调用者的约定, 返回一个数值告诉它再次重试
                // 一般是结合 select/epoll 等 IO 多路复用函数
            }
            exit(-1);
        }else if(ret == 0){
            close(fd);
        }
        // proc
        break;
    }
    

    没错, 在 read() 外面包一个 while(1). 以后你看到有谁写的 read() 代码不是被包含在一个死循环里, 你就可以下结论说这段代码不完善.

    写 IO 的 write() 函数也是类似用法. 建议有心人可以参考 sim 框架的代码, 看看别人是怎么做的, 程序员应该多造轮子, 但不要关起门来造车.

    Posted by ideawu at 2015-05-08 10:56:24
  • 2015-05-07

    PHP 解码 C 字符串

    Views: 10415 | 2 Comments

    有时候, 需要将 C 语言字符串字面量编码的字符串转换成其在内存中实际的值, 如果用 Python, 是有内置的函数, 但 PHP 没有, 只能写一个.

    C 语言字符串字面量编码, 就是类似:

    \r\n\x90
    

    它们在内存中肯定不是这样.

    完整代码如下, 需要 PHP 5.4+.

    function unescape_c_str($str){
        $ret = '';
        $len = strlen($str);
        for($i=0; $i<$len; $i++){
            if($str[$i] != '\\'){
                $ret .= $str[$i];
                continue;
            }
            $i++;
            switch($str[$i]){
                case 's':
                    $ret .= ' ';
                    break;
                case 'a':
                    $ret .= "\a";
                    break;
                case 'b':
                    $ret .= "\b";
                    break;
                case 'f':
                    $ret .= "\f";
                    break;
                case 'v':
                    $ret .= "\v";
                    break;
                case 'r':
                    $ret .= "\r";
                    break;
                case 'n':
                    $ret .= "\n";
                    break;
                case 't':
                    $ret .= "\t";
                    break;
                case '\\':
                    $ret .= "\\";
                    break;
                case 'x':
                    $hex = substr($str, $i+1, 2);
                    $ret .= hex2bin($hex);
                    $i += 2;
                    break;
                default:
                    $ret .= $str[$i];
                    break;
            }
        }
        return $ret;
    }
    
    Posted by ideawu at 2015-05-07 12:51:28
  • 2015-05-05

    C/C++编程的现代习惯

    Views: 10854 | 8 Comments

    相对于汇编语言是一门操作 CPU 寄存器的语言, C/C++ 是一门操作内存的语言, 这是传统的观点. 但现代的程序应用开发, 大多是把 C/C++ 当作一门应用层语言, 所以必须适当地减少对内存的关注. 这也是本文所要讲的 - C/C++ 编程的现代习惯.

    1. 不要害怕返回结构体和类的实例

    在一些古董级的编程书里, 你绝对看不到返回结构体或者类的实例, 它们告诉你"不能返回局部变量的内存". 事实上, 返回结构体(类)的实例, 并不是把局部变量的内存(指针)返回给调用者使用, 而把局部变量复制到调用者栈上的内存. 而且, 很多情况下编译器会优化, 根本就不会发生内存拷贝.

    返回结构体(类)的实例, 比返回 malloc() 分配的内存的指针在实践上具有更多的优势, 既能使代码更清晰, 也可以完全避免内存泄漏.
    Continue reading »

    Posted by ideawu at 2015-05-05 14:39:32
|<<<123456789>>>| 1/12 Pages, 67 Results.