• 2015-05-08

    在Linux进行IO的正确姿势

    Views: 23657 | 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: 28001 | 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: 21752 | 8 Comments

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

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

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

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

    Posted by ideawu at 2015-05-05 14:39:32
  • 2015-04-23

    Python 二进制, 十进制, 十六进制之间转换

    Views: 12970 | No Comments
    bin()
    hex()
    ord()
    chr()
    0x12
    0b1001
    
    Posted by ideawu at 2015-04-23 15:43:28
  • 2015-04-08

    Objective C urlencode urldecode

    Views: 8998 | No Comments
    NSString *urlencode(NSString *str){
    	CFStringEncoding cfEncoding = kCFStringEncodingUTF8;
    	str = (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(
    		NULL,
    		(CFStringRef)str,
    		NULL,
    		CFSTR("!*'();:@&=+$,/?%#[]"),
    		cfEncoding
    		);
    	return str;
    }
    
    NSString *urldecode(NSString *str){
    	CFStringEncoding cfEncoding = kCFStringEncodingUTF8;
    	str = (__bridge NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding (
    		NULL,
    		(CFStringRef)str,
    		CFSTR(""),
    		cfEncoding
    		);
    	return str;
    }
    
    Posted by ideawu at 2015-04-08 13:14:49
  • 2015-03-30

    SSDB 支持 iOS 设备使用了

    Views: 85563 | 4 Comments

    我很高兴地告诉大家, SSDB 支持在 iOS 设备上使用了! 当然, 这不是要在 iOS 设备上运行一个 ssdb server, 而是将 SSDB 库嵌入到你的 App 里.

    例如, 你在开发一个 iOS 邮件客户端, 你可以使用 SSDB 来存储邮件列表, 利用 SSDB 支持亿级别数据列表的功能特性, 轻松管理大量邮件. 例如, 你在开发一个阅读器, 你可以把成千上万的文章存储到 SSDB 中. 例如, 你在开发一个 iOS IM 应用, 你可以把消息历史用 SSDB 来存储管理.

    使用方法:

    1. 编译 SSDB 静态链接库

    make ios
    # ls ios/
    include/ libleveldb-ios.a libsnappy-ios.a libssdb-ios.a libutil-ios.a
    

    2. 将静态链接库和头文件引入你的 Xcode 项目.

    源码下载: https://github.com/ideawu/ssdb/tree/dev

    Posted by ideawu at 2015-03-30 17:16:00 Tags:
|<<<252627282930313233>>>| 29/138 Pages, 825 Results.