现在的位置: 首页 > 综合 > 正文

[Android-Gingerbread-Vold]Vold启动详解

2013年08月15日 ⁄ 综合 ⁄ 共 20071字 ⁄ 字号 评论关闭

 

Android Vold

本文简要介绍Android Gingerbread中Vold的启动过程。根据网上的资料和code改写了一下。

1       Vold

     Vold的全称是volume daemon。主要负责系统对大容量存储设备(USB/SD)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔。自Android 2.2开始,Vold升级为vold 2.0,之前的配置文件路径在system/etc/vold.conf,Android 2.3之后变为system/etc/vold.fstab,目前的vold版本为2.1。

2        Vold工作流程

    Vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理。

     (1)创建监听

     创建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制SD卡的挂载与卸载,这里所说的链接也就是Socket。在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:

Service vold /system/bin/vold
             Socket vold stream 0660 root mount
             Iprio be 2

     这样系统会在启动的时候创建与上层通信的Socket。

      在Android 2.3源码/system/vold路径下的main.cpp中创建了与内核通信的Socket。在main.cpp中通过实例化VolumeManager和NetlinkManager时创建。

     (2)引导

     Vold进程启动时候会对现有的外部存储设备进行检查。首先加载并解析vold.fstab,因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点。
vold.fatab中最重要的语句:

dev_mount        sdcard         /mnt/sdcard              auto                    /devices/platform/goldfish_mmc.0              /devices/platform/msm_sdcc.2/mmc_host/mmc1

dev_mount       <lable>     <mount_point>           <part>                   <sysfs_path…>
挂载命令            标签                挂载点              子分区个数                挂载路径
注:
       子分区个数如果为auto则表示只有1个子分区,也可以为任何不为0的整数。
       参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的)。
       如果vold.fstab解析无误,VolueManager将创建DirectVolume。

     (3)事件处理

     通过对两个socket的监听,完成对事件的处理以及对上层应用的响应。

       a.     Kernel发出uevent
       NetlinkManager检测到kernel发出的uevent,解析后调用NetlinkHandler::onEvent()方法。该方法会分别处理不同的事件,这里重要的事件有:
       “block”事件主要指Volume的mount、unmount、createAsec等。由VolumeManager的handleBlockEvent(evt)来处理,根据多态性最终将会调用AutoVolume或者DirectVolume的handleBlockEvent方法来处理。
       “switch”事件主要指Volume的connet、disconnet等。根据相关操作,改变设备参数(设备类型、挂载点等)通过CommandListener告知FrameWork层。

       b.    FrameWork发出控制命令
       与a相反,CommandListener检测到FrameWork层的命令(MountService发出的命令)调用VolumeManager的函数,VolumeManager找出对应的Volume,调用Volume函数去挂载/卸载操作。而Volume类中的相关操作最终通过调用Linux函数完成。

3        Vold启动过程详细介绍

Vold的入口函数在system/vold/main.cpp中。

int main() {

 

    VolumeManager *vm;

    CommandListener *cl;

    NetlinkManager *nm;

//注释1

    SLOGI("Vold 2.1 (the revenge) firing up");

 

    mkdir("/dev/block/vold", 0755);

 

    /* Create our singleton managers */

    if (!(vm = VolumeManager::Instance())) {

        SLOGE("Unable to create VolumeManager");

        exit(1);

    }; 

//注释2

    if (!(nm = NetlinkManager::Instance())) {

        SLOGE("Unable to create NetlinkManager");

        exit(1);

    }; 

//注释3

cl = new CommandListener();

//注释4

    vm->setBroadcaster((SocketListener *) cl);

    nm->setBroadcaster((SocketListener *) cl);

//注释5

    if (vm->start()) {

        SLOGE("Unable to start VolumeManager (%s)", strerror(errno));

        exit(1);

    }

 //注释6

    if (process_config(vm)) {

        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));

    }

//注释7

    if (nm->start()) {

        SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));

        exit(1);

    }

//注释8

coldboot("/sys/block");

//注释9

//    coldboot("/sys/class/switch");

 

/*

     * Now that we're up, we can respond to commands

     */

    if (cl->startListener()) {

        SLOGE("Unable to start CommandListener (%s)", strerror(errno));

        exit(1);

    }

//注释10

 

    // Eventually we'll become the monitoring thread

    while(1) {

        sleep(1000);

}

     SLOGI("Vold exiting");

    exit(0);

}

 

4        注释

4.1     注释1

声明VolumeManager,CommandListener 和NetlinkManager

4.2     注释2

实例VolumeManager,具体代码在VolumeManager.cpp中,并读取了initial mass storage enabled state 和initial USB connected state

4.3     注释3

实例化一个NetlinkManager对象,具体的构造函数位于文件NetlinkManager.cpp中

4.4     注释4

构造一个CommandListener对象,这个类定义在文件CommandListener.h中,继承了类FrameworkListener,这个类定义在sysutils/FrameworkListener.h中这个类又继承了类SocketListener,这个类定义在文件SocketListener.h中,

首先来看CommandListener.cpp:

CommandListener::CommandListener() :

                 FrameworkListener("vold") {

    registerCmd(new DumpCmd());

    registerCmd(new VolumeCmd());

    registerCmd(new AsecCmd());

    registerCmd(new ObbCmd());

    registerCmd(new ShareCmd());

    registerCmd(new StorageCmd());

    registerCmd(new XwarpCmd());

}

在system/core/libsysutils/src/FrameworkListener.cpp

FrameworkListener::FrameworkListener(const char *socketName) :

                            SocketListener(socketName, true) {

mCommands = new FrameworkCommandCollection();

//typedef android::List<FrameworkCommand *> FrameworkCommandCollection;

}

在system/core/libsysutils/src/SocketListener.cpp中

SocketListener::SocketListener(const char *socketName, bool listen) {

    mListen = listen;

    mSocketName = socketName;

    mSock = -1;

    pthread_mutex_init(&mClientsLock, NULL);

    mClients = new SocketClientCollection();

}

 

回头我们再看看CommandListener的构造函数
CommandListener::CommandListener() :

                 FrameworkListener("vold") {

    registerCmd(new DumpCmd());

    registerCmd(new VolumeCmd());

    registerCmd(new AsecCmd());

    registerCmd(new ObbCmd());

    registerCmd(new ShareCmd());

    registerCmd(new StorageCmd());

    registerCmd(new XwarpCmd());

}

 

这里会调用其继承类的protected成员函数registerCmd,其参数是一个指向类FrameworkCommand的指针。类CommandListener包含几个私有的内部类class DumpCmd : public VoldCommand

           class VolumeCmd : public VoldCommand

            class ShareCmd : public VoldCommand

            class AsecCmd : public VoldCommand

            class StorageCmd : public VoldCommand

            class XwarpCmd : public VoldCommand

我们分析如何实例化一个DumpCmd

        CommandListener::DumpCmd::DumpCmd() :

                     VoldCommand("dump") {

        }

       其父类在文件system/vold/VoldCommand.cpp中

              VoldCommand::VoldCommand(const char *cmd) :

                      FrameworkCommand(cmd)  {

        }

       其父类FrameworkCommand::FrameworkCommand(const char *cmd) {

                mCommand = cmd;

        }

同理创建VolumeCmd, AsecCmd, ShareCmd, StorageCmd, XwarpCmd

    其mCommand私有成员分别取值为 dump,  volume, asec, share, storage, xwarp

 

    我们看看怎么注册指令的

    void FrameworkListener::registerCmd(FrameworkCommand *cmd) {

            mCommands->push_back(cmd);

     }

    mCommands指向FrameworkCommandCollection 而FrameworkCommandCollection是一个包含FrameworkCommand指针的链表

    typedef android::List<FrameworkCommand *> FrameworkCommandCollection

    因此这里是将创建的FrameworkCommand指针对象压入链表存储起来

4.5   注释5

设置vm的私有成员SocketListener        *mBroadcaster;

使得mBroadcaster指向cl

设置nm的私有成员mBroadcaster,同样指向cl

4.6     注释6

vm->start()始终返回0

4.7     注释7

   解析/system/etc/vold.fstab文件,

读取type, label, mount_point, part

(1). 构建DirectVolume对象 :如果part为auto, 则调用dv = new DirectVolume(vm, label, mount_point, -1);

(2). 添加vold.fstab中定义的某一挂载项对应的sysfs_path到 DirectVolume对象的mPaths容器  dv->addPath(sysfs_path);

(3). 将这个DirectVolume 对象添加到 VolumeManager对象的容器mVolumes中   vm->addVolume(dv);

4.8     注释8

   nm->start()会调用NetlinkManager类的start()方法
   int NetlinkManager::start() {

            struct sockaddr_nl nladdr;

            int sz = 64 * 1024;

            memset(&nladdr, 0, sizeof(nladdr));

            nladdr.nl_family = AF_NETLINK;

            nladdr.nl_pid = getpid();

            nladdr.nl_groups = 0xffffffff;

 

            if ((mSock = socket(PF_NETLINK,

                        SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {

                SLOGE("Unable to create uevent socket: %s", strerror(errno));

                return -1;

            }

 

            if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

                    SLOGE("Unable to set uevent socket options: %s", strerror(errno));

                return -1;

            }

 

            if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

                SLOGE("Unable to bind uevent socket: %s", strerror(errno));

                return -1;

            }
        //创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件

            mHandler = new NetlinkHandler(mSock);
        /*    
            利用新创建的socket实例化一个NetlinkHandler类对象,NetlinkHandler继承了类NetlinkListener,NetlinkListener又继承了类SocketListener
            我们看看其构造的过程
            NetlinkHandler::NetlinkHandler(int listenerSocket) :  
                            NetlinkListener(listenerSocket) {
                } //NetlinkHandler.cpp
            
            NetlinkListener::NetlinkListener(int socket) :
                                    SocketListener(socket, false) {
                }//NetlinkListener.cpp

            SocketListener::SocketListener(int socketFd, bool listen) {
                    mListen = listen;
                    mSocketName = NULL;
                    mSock = socketFd;
                    pthread_mutex_init(&mClientsLock, NULL);
                    mClients = new SocketClientCollection();
            }
            */
        
            if (mHandler->start()) {
                SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
                return -1;
            }
        /*
            mHandler->start()调用this->startListener()
            最终会调用SocketListener::startListener()

            int SocketListener::startListener() {

                    if (!mSocketName && mSock == -1) {
                        SLOGE("Failed to start unbound listener");
                        errno = EINVAL;
                        return -1;
                    } else if (mSocketName) {
                        if ((mSock = android_get_control_socket(mSocketName)) < 0) {
                                SLOGE("Obtaining file descriptor socket '%s' failed: %s",
                                 mSocketName, strerror(errno));
                                return -1;
                        }
                    }

                    if (mListen && listen(mSock, 4) < 0) {
                        SLOGE("Unable to listen on socket (%s)", strerror(errno));
                        return -1;
                    } else if (!mListen)
                        mClients->push_back(new SocketClient(mSock));

                //实例化一个SocketClient,并将其压入mClients容器中
                    if (pipe(mCtrlPipe)) {
                        SLOGE("pipe failed (%s)", strerror(errno));
                        return -1;
                    }
                //建立管道, 并将文件描述词存于数组mCtrlPipe[2]中,mCtrlPipe[0]为管道的读取端,mCtrlPipe[1]为管道的写入端

                    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
                        SLOGE("pthread_create (%s)", strerror(errno));
                        return -1;
                    }
                /*
                    创建线程,线程号为mThread,.
                    线程函数为
                    void *SocketListener::threadStart(void *obj) {
                            SocketListener *me = reinterpret_cast<SocketListener *>(obj);
         
                            me->runListener();//将this即NetlinkHandler对象强制转换为SocketListener类型,调用其成员函数runListener()

                            pthread_exit(NULL);//线程退出,不会跑到这里
                            return NULL;
                    }

******************************

                 线程真正执行的函数:mListen成员用来判定是否监听套接字
                    Netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用相关函数读取数据。    
                    void SocketListener::runListener() {

                            while(1) {//无线循环,一直监听
                            SocketClientCollection::iterator it;
                            fd_set read_fds;
                            int rc = 0;
                            int max = 0;

                            FD_ZERO(&read_fds); //清空文件描述符集read_fds

                            if (mListen) {
                                    max = mSock;
                                    FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds
                            }

                            FD_SET(mCtrlPipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds
                            if (mCtrlPipe[0] > max)
                                    max = mCtrlPipe[0];

                            pthread_mutex_lock(&mClientsLock);//对容器mClients的操作需要加锁
                            for (it = mClients->begin(); it != mClients->end(); ++it) {
                                    FD_SET((*it)->getSocket(), &read_fds); //遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
                                    if ((*it)->getSocket() > max)
                                        max = (*it)->getSocket();
                            }
                            pthread_mutex_unlock(&mClientsLock);

                            if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { //  等待文件描述符中某一文件描述符或者说socket有数据到来
                                    SLOGE("select failed (%s)", strerror(errno));
                                    sleep(1);
                                    continue;
                            } else if (!rc)
                                    continue;

                            if (FD_ISSET(mCtrlPipe[0], &read_fds))
                                    break;
    
                            if (mListen && FD_ISSET(mSock, &read_fds)) {//监听套接字处理
                                    struct sockaddr addr;
                                    socklen_t alen = sizeof(addr);
                                    int c;

                                    if ((c = accept(mSock, &addr, &alen)) < 0) {  //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,
                                                    将其添加到mClient容器;
                                        SLOGE("accept failed (%s)", strerror(errno));
                                        sleep(1);
                                        continue;
                                    }
                                    pthread_mutex_lock(&mClientsLock);
                                    mClients->push_back(new SocketClient(c));
                                    pthread_mutex_unlock(&mClientsLock);
                            }

        do {//非监听套接字处理
            pthread_mutex_lock(&mClientsLock);
            for (it = mClients->begin(); it != mClients->end(); ++it) {
                int fd = (*it)->getSocket();
                if (FD_ISSET(fd, &read_fds)) {//调用相应的数据读取函数,读取数据
                    pthread_mutex_unlock(&mClientsLock);
                    if (!onDataAvailable(*it)) {
                        close(fd);
                        pthread_mutex_lock(&mClientsLock);
                        delete *it;
                        it = mClients->erase(it);
                        pthread_mutex_unlock(&mClientsLock);
                    }
                    FD_CLR(fd, &read_fds);
                    continue;
                }
            }
            pthread_mutex_unlock(&mClientsLock);
        } while (0);
    }
}

***************************

分析函数: onDataAvailable(*it)
onDataAvailable是在类SocketListener中定义的纯虚函数,在Android2.2中共有五个类继承该类,并对函数 onDataAvailable进行了实现,分别是:
    DhcpListener(system\core\nexus)、
    FrameworkListener(system\core\libsysutils\src)、
    NetlinkListener(system\core\libsysutils\src)、
    SupplicantListener(system\core\nexus)、
    TiwlanEventListener(system\core\nexus);

针对Netlink创建的套接字,是由NetlinkHandler对象调用的startListen函数,并开启的相关线程,而且 NetlinkHandler继承了NetlinkListener,
所以此处调用的是NetlinkListener类的成员函数onDataAvailable;
system\core\libsysutils\src\NetlinkListener.cpp

bool NetlinkListener::onDataAvailable(SocketClient *cli)

{

    int socket = cli->getSocket();

    ssize_t count;

    char cred_msg[CMSG_SPACE(sizeof(struct ucred))];

    struct sockaddr_nl snl;

    struct iovec iov = {mBuffer, sizeof(mBuffer)};

    struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};

 

    if ((count = recvmsg(socket, &hdr, 0)) < 0) { //读取数据

        SLOGE("recvmsg failed (%s)", strerror(errno));

        return false;

    }  

 

    if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {

        SLOGE("ignoring non-kernel netlink multicast message");

        return false;

    }  

 

    struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);

 

    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {

        SLOGE("ignoring message with no sender credentials");

        return false;

    }  

 

    struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);

    if (cred->uid != 0) {

        SLOGE("ignoring message from non-root UID %d", cred->uid);

        return false;

    }  

 

    NetlinkEvent *evt = new NetlinkEvent();

    if (!evt->decode(mBuffer, count)) {

        SLOGE("Error decoding NetlinkEvent");

        goto out;

    }  

 

    onEvent(evt); //NetlinkListener类定义了纯虚函数,其子类NetlinkHandler对其进行了实现,所以此处调用子类 NetlinkHandler的onEvent函数;

out:

    delete evt;

    return true;

}

 

onEvent 函数分析:
system/vold/NetlinkHandler.cpp

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

    VolumeManager *vm = VolumeManager::Instance();

    const char *subsys = evt->getSubsystem();

 

    if (!subsys) {

        SLOGW("No subsystem found in netlink event");

        return;

    }  

 

    if (!strcmp(subsys, "block")) {

        vm->handleBlockEvent(evt);

    } else if (!strcmp(subsys, "switch")) {

        vm->handleSwitchEvent(evt);

    } else if (!strcmp(subsys, "usb_composite")) {

        vm->handleUsbCompositeEvent(evt);

    } else if (!strcmp(subsys, "battery")) {

    } else if (!strcmp(subsys, "power_supply")) {

    }  

}

至此即为获取uevent的事件通道

4.9     注释9

冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add\n” 字符也可以触发sysfs事件,相当执行了一次热插拔。

4.10        注释10

 调用其父类SocketListener的startListener()方法
    cl实例的mSocketName为vold
        调用内联函数android_get_control_socket(mSocketName),定义在文件system/core/include /cutils/sockets.h中
    根据参数mSocketName, 加上前缀ANDROID_SOCKET_,即字符串ANDROID_SOCKET_vold,调用getenv获取到相应socket的文件描述符
    例如,vold的socket在init.rc就写好了
    service vold /system/bin/vold
            socket vold stream 0660 root mount
            ioprio be 2
    init程序在读取init.rc文件后,在调用service_start启动service时调用create_socket在 ANDROID_SOCKET_DIR(/dev/socket)下建立相应的socket
    调用publish_socket-->add_environment, 将socket的文件描述符加上ANDROID_SOCKET_ENV_PREFIX添加到环境变量之中
    可以根据环境变量ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"), 调用getenv查找到相应的文件描述符

    if (mListen && listen(mSock, 4) < 0) {
            SLOGE("Unable to listen on socket (%s)", strerror(errno));
                return -1;
        } else if (!mListen)
            mClients->push_back(new SocketClient(mSock));
    这里cl是监听套接字,因此调用listen 用来等待参数mSock 的socket连线. 指定同时能处理的最大连接要求为4,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。通常listen()会在 socket(),bind()之后调用,接着才调用accept()
    
    if (pipe(mCtrlPipe)) {
            SLOGE("pipe failed (%s)", strerror(errno));
            return -1;
        }
        if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
            SLOGE("pthread_create (%s)", strerror(errno));
            return -1;
        }
    */

/*
    线程执行函数
void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);
    me->runListener();
    pthread_exit(NULL);
    return NULL;
}

void SocketListener::runListener() {

    while(1) {
        SocketClientCollection::iterator it;
        fd_set read_fds;
        int rc = 0;
        int max = 0;

        FD_ZERO(&read_fds);

        if (mListen) { //cl是监听套接字,所以这里mListen为真
            max = mSock;
            FD_SET(mSock, &read_fds);
        }

        FD_SET(mCtrlPipe[0], &read_fds);
        if (mCtrlPipe[0] > max)
            max = mCtrlPipe[0];

        pthread_mutex_lock(&mClientsLock);
        for (it = mClients->begin(); it != mClients->end(); ++it) { //遍历mClients容器,将所有的socket文件描述符添加到集合read_fds中
            FD_SET((*it)->getSocket(), &read_fds);
            if ((*it)->getSocket() > max)
                max = (*it)->getSocket();
        }
        pthread_mutex_unlock(&mClientsLock);

        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//  等待文件描述符中某一文件描述符或者说socket有数据到来
            SLOGE("select failed (%s)", strerror(errno));
            sleep(1);
            continue;
        } else if (!rc)
            continue;

        if (FD_ISSET(mCtrlPipe[0], &read_fds))
            break;

        if (mListen && FD_ISSET(mSock, &read_fds)) {//这里cl是监听套接字,所以mListen为真,并且mSock在集合read_fds中

            struct sockaddr addr;
            socklen_t alen = sizeof(addr);
            int c;

            if ((c = accept(mSock, &addr, &alen)) < 0) {
                SLOGE("accept failed (%s)", strerror(errno));
                sleep(1);
                continue;
            }//accept用来接受参数mSock的socket连线。参数mSock的socket必需先经bind()、listen()函数处理过,当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数mSock的 socket能继续使用accept()来接受新的连线要求
            pthread_mutex_lock(&mClientsLock);
            mClients->push_back(new SocketClient(c));//新的socket文件描述符将被实例化并加入到集合mClients中
            pthread_mutex_unlock(&mClientsLock);
        }

        do {
            pthread_mutex_lock(&mClientsLock);
            for (it = mClients->begin(); it != mClients->end(); ++it) {//遍历集合mClients,如果其成员描述符在集合read_fds中,读取数据,如果没有数据发送过来,
                                    //则从mClients中删除此socket,并清除集合read_fds中的文件描述符。
                int fd = (*it)->getSocket();

                if (FD_ISSET(fd, &read_fds)) {
                    pthread_mutex_unlock(&mClientsLock);
                    if (!onDataAvailable(*it)) { //这里的cl的onDataAvailable在FrameworkListener中实现,返回为false,说明没有读取到数据,如果有数据,
调用FrameworkListener::dispatchCommand发送数据到socket
                        close(fd);
                        pthread_mutex_lock(&mClientsLock);
                        delete *it;
                        it = mClients->erase(it);
                        pthread_mutex_unlock(&mClientsLock);
                    }
                    FD_CLR(fd, &read_fds);
                    continue;
                }
            }
            pthread_mutex_unlock(&mClientsLock);
        } while (0);
    }
}

************************************

In FrameworkListener.cpp

bool FrameworkListener::onDataAvailable(SocketClient *c) {

    char buffer[255];

    int len;

 

    if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {

        SLOGE("read() failed (%s)", strerror(errno));

        return false;

    } else if (!len)

        return false;

 

    int offset = 0;

    int i;

 

    for (i = 0; i < len; i++) {

        if (buffer[i] == '\0') {

            dispatchCommand(c, buffer + offset);

            offset = i + 1;

        }  

    }  

    return true;

}

 

5       参考文献

http://blog.csdn.net/yihongyuelan/article/details/6926034

http://apps.hi.baidu.com/share/detail/44662055#content

http://blog.csdn.net/sustzombie/article/details/6118256

 

抱歉!评论已关闭.