3.Android FrameWork之Binder图解 从binder驱动层详细解析binder通信

219 阅读17分钟

1. Binder 整体的架构

image.png binder包含4部分: 我将Binder机制分为了Java Binder、Native Binder、Kernel Binder

  1. 四层架构

    • 应用层:AIDL接口
    • 框架层:IBinder/BinderProxy
    • JNI层:android_util_Binder.cpp
    • 驱动层:/dev/binder
  2. 核心角色

    • BpBinder:客户端代理(Proxy)
    • BBinder:服务端基类(Base)
    • ServiceManager:服务注册中心
  3. 注册服务(addService) :Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。

3.2 . 获取服务(getService) :Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。

3.3 使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。

2. Binder 机制整个过程

image.png

1).Binder 驱动的全局链表 binder_procs 中插入服务端的信息(binder_proc 结构体,每个 binder_proc 结构体中都有 todo 任务队列),然后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。

2). 有了服务端,客户端就可以跟服务端通讯了,通讯之前需要先获取到服务,拿到服务的代理,也可以理解为引用

获取服务端的方式就是通过 ServiceManager 向 svcinfo 列表中查询一下返回服务端的代理,svcinfo 列表就是所有已注册服务的通讯录,保存了所有注册的服务信息。

3).有了服务端的引用我们就可以向服务端发送请求了,通过 BinderProxy 将我们的请求参数发送给 ServiceManager,通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态,然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并没有拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成了一次通讯。

3.Binder驱动

image.png

重要的几个方法:open ,mmap ,iocontro。特别是mmap内存映射 ##3.1 open - 打开Binder驱动

作用:建立进程与Binder驱动的连接

// kernel/drivers/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    
    // 初始化关键数据结构
    proc->tsk = current->group_leader;
    INIT_LIST_HEAD(&proc->todo);
    init_waitqueue_head(&proc->wait);
    proc->default_priority = task_nice(current);
    
    // 插入全局进程列表
    hlist_add_head(&proc->proc_node, &binder_procs);
    
    filp->private_data = proc;
}

核心操作

  1. 创建 binder_proc 结构体
  2. 初始化任务队列和等待队列
  3. 设置默认优先级(继承当前进程nice值)
  4. 加入全局进程列表 binder_procs

典型调用示例

int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);

3.2、mmap - 内存映射(最核心机制)

作用:建立用户空间与内核的共享内存

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    struct binder_proc *proc = filp->private_data;
    
    // 1. 检查映射大小(通常1M-8M)
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        return -ENOMEM;
    
    // 2. 记录映射区域
    proc->vma = vma;
    proc->buffer = vmalloc_user(vma->vm_end - vma->vm_start);
    
    // 3. 建立页表映射
    binder_insert_free_buffer(proc, proc->buffer);
    
    // 4. 设置内存操作回调
    vma->vm_ops = &binder_vm_ops;
}
  1. 内存共享

    • 内核缓冲区同时映射到通信双方
    • 物理页面仅一份,通过页表实现共享
void* addr = mmap(NULL, 128*1024, PROT_READ, MAP_PRIVATE, binder_fd, 0);
// 参数说明:
// - PROT_READ:只读映射(Binder特殊处理写权限)
// - MAP_PRIVATE:私有映射(但实际共享)

3.3、ioctl - 控制命令交互

作用:进程与驱动间的主要通信接口

命令分类

命令类型功能使用频率
BINDER_WRITE_READ读写数据(90%调用)⭐⭐⭐⭐⭐
BINDER_SET_MAX_THREADS设置最大线程数⭐⭐
BINDER_SET_CONTEXT_MGR设置ServiceManager

BINDER_WRITE_READ 数据结构

struct binder_write_read {
    binder_size_t write_size;    // 发送数据大小
    binder_size_t write_consumed;// 已消费数据
    binder_uintptr_t write_buffer;// 发送缓冲区
    
    binder_size_t read_size;     // 接收缓冲区大小
    binder_size_t read_consumed; // 已读取数据
    binder_uintptr_t read_buffer; // 接收缓冲区
};

典型调用流程

// 用户空间调用示例
struct binder_write_read bwr = {
    .write_size = sizeof(cmd),
    .write_buffer = (uintptr_t)&cmd,
    .read_size = sizeof(reply),
    .read_buffer = (uintptr_t)&reply
};
ioctl(fd, BINDER_WRITE_READ, &bwr);

// 驱动处理逻辑
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    case BINDER_WRITE_READ:
        ret = binder_ioctl_write_read(filp, cmd, arg);
        break;
    // 其他命令处理...
    }
}

微信截图_20250818221323.png

4. Binder 进程与线程

  1. 进程管理

    • binder_proc:每个进程唯一,管理所有资源
    • 全局链表维护所有 Binder 进程
  2. 线程池

    • 默认启动 16 个 Binder 线程

    • 线程状态:

      • BINDER_LOOPER_STATE_REGISTERED
      • BINDER_LOOPER_STATE_ENTERED
    • 驱动通过 BR_SPAWN_LOOPER 动态创建线程

image.png

对于底层Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应,且每个进程有且只有一个 ProcessState 对象,这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应一个 IPCThreadState 对象,IPCThreadState 对象也是单例模式,即一个线程对应一个 IPCThreadState 对象,在 Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。

Binder 线程池:每个 Server 进程在启动时创建一个 binder 线程池,并向其中注册一个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制,默认为16个 binder 线程,例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的 binder 请求都是交由 Server 端进程的 binder 线程来处理的。

5.Service的注册过程

一句话总结:把service服务通过binder驱动添加到serverManager的过程

自己的总结添加服务注册的过程: 重要的是找到 servermanager, 然后调用添加service到sm

  1. 找到ser verManager, 通过binder驱动 (在binder驱动中),通过handler值=0找到serverManager
  2. 在biner驱动中添加服务-----》通过bp binder,  打开驱动, 得到了servermanager就可以添加 , 同
  3. 时把handler, name, 写入binder驱动

流程图:

image.png

5.1 总结:

1.addService函数将数据打包发送给BpBinder来进行处理。

2.BpBinder新建一个IPCThreadState对象,并将通信的任务交给IPCThreadState。

3.IPCThreadState的writeTransactionData函数用于将命令协议和数据写入到mOut中。

4.IPCThreadState的waitForResponse函数主要做了两件事,一件事是通过ioctl函数操作mOut和mIn来与Binder驱动进行数据交互,另一件事是处理各种命令协议。

5.2 问题1:

请问MediaPlayerService所在的进程,如何和binder驱动通信的?(重点)

MediaPlayerService是如何注册的。通过了解MediaPlayerService是如何注册的,可以得知系统服务的注册过程

1).打开binder驱动

2). 把对应的参数,封装成bpbinder序列化的对象,交给binder驱动

打开/dev/binder设备,这样的话就相当于和内核binder机制有了交互的通道

 映射fd到内存,设备的fd传进去后,估计这块内存是和binder设备共享的

binder_ioctl()

5.3 问题2:MediaPlayerService和Servermanager是不同进程,如何通信的?

通过binder驱动。内核,内存映射关系。

5.4 问题2:bpbinder有什么作用?

BpBinder中的成员变量mHandler表示Binder引用对象的句柄值.封装传handle值

Bn 是Binder Native的含义,是和Bp相对的,Bp的p是proxy代理的意思,那么另一端一定有一个和代理打交道的东西,这个就是Bn。

讲到这里会有点乱喔。先分析下,到目前为止都构造出来了什么。

BpServiceManager

BnMediaPlayerService

数据流向:

具体的数据有 interfaceToken(远程服务的名称)、handle(远程服务的句柄)、cookie(本地服务的地址),

有两个结构体 flat_binder_object 和 binder_write_read。

5.5 问题4:Media 服务是如何添加到servermanager中的?

1).Media 进程是由 init 进程通过解析 init.rc 文件而创建的。

2).  最后是交给了binder 驱动的 ioctl 方法

6.ServiceManager和binder通信过程

image.png

6.1 ServiceManager 进程启动的三个阶段:

ServiceManager 进程启动的三个阶段:ServiceManager: 也可以说是守护进程

  • 打开 binder 驱动:binder_open;
  • 调用binder_become_context_manager函数,将servicemanager注册成为Binder机制的上下文管理者。(告诉驱动自己是serviceManager)
  • 进入无限循环,binder_loop函数,循环等待和处理client端发来的请求。(循环,不停的解析和处理客户端的数据和请求)

最终会通过 binder 驱动跨进程执行 ServiceMananger 的 do_add_service 方法。

6.2 binder驱动是如何和serviceManager进行通信的?数据是如何传递的?

打开驱动,然后进行读取文件!内存映射

6.3 ServiceManager 是如何管理service信息的?

image.png

6.4 ServiceManager存在的意义

原来,Android系统中Service信息都是先add到ServiceManager中,由ServiceManager来集中管理,

这样就可以查询当前系统有哪些服务。而且,Android系统中某个服务例如MediaPlayerService的客户端想要和MediaPlayerService通讯的话,必须先向ServiceManager查询MediaPlayerService的信息,然后通过ServiceManager返回的东西再来和MediaPlayerService交互。

毕竟,要是MediaPlayerService身体不好,老是挂掉的话,客户的代码就麻烦了,就不知道后续新生的MediaPlayerService的信息了,所以只能这样:

l         MediaPlayerService向SM注册

l         MediaPlayerClient查询当前注册在SM中的MediaPlayerService的信息

l         根据这个信息,MediaPlayerClient和MediaPlayerService交互

另外,ServiceManager的handle标示是0,所以只要往handle是0的服务发送消息了,最终都会被传递到ServiceManager中去。

6.5.binder驱动是怎么帮我们找到servermanager进程的

handle = 0 , 驱动层会创建一个静态的变量 binder_context_mgr_node 

里面有4给红黑树,存放了很多信息!

6.6.servermanager进程是怎进入等待,怎么被唤醒的?

6.5.1 我自己的理解:开始servermanager在等待,然后因为客户端往binder写入了数据,然后binder驱动往serverManager发送了

东西,之后就把它唤醒了!

等待是在 wait 队列上等,条件是 todo 队列是否为空【重点:队列】

5.5.2 客户端找到 tagert_prco 了之后把数据拷贝到目标进程,什么时候去找?这个代码在哪里?

找到了tagert_prco之后就能找到等待队列!****

同时把数据拷贝到目标进程往目标进程写入处理的命令,

然后往自己进程写入一个接受请求的命令(让自己进入等待),

最后唤醒服务进程的wait队列,这个时候服务进程被唤醒就会继续往下处理todo队列请求。

6.5.3. 客户端是怎么进入等待的

客户端是因为在请求服务端的时候驱动层会写入一个 BINDER_WORK_TRANSACTION_COMPLETE , 

然后会清空 writeData , 从新进入驱动层,进入读方法,因为todo队列里面没东西进入等待,等待服务端执行完毕唤醒客户端

6.5.4. 客户端等待响应过程分析

6.7 和serviceManger结合,整个流程

重要的是找到 servermanager, 然后调用添加service到sm 1.打开binder驱动 2.serverManager成为 Binder 驱动管理者

  1. 找到serverManager, 通过binder驱动 (在binder驱动中),通过handler值=0找到serverManager
  2. serverManager一直在等待, 等待服务注册 serverr要注册, 添加服务,binder驱动 把serverManager唤醒
  3. 在biner驱动中通过sm添加服务-----》通过bp binder,  打开驱动, 得到了servermanager就可以添加 , 同时把handler, name, 写入binder驱动,

7.唤醒流程图,Binder协议中BC与BR的区别

内核层内核层的通信都是通过ioctl来进行的,client打开一个ioctl,进入到轮询队列,一直阻塞直到时间到或者有消息

image.png

流程:

1.开始servermanager处于等待状态

2.然后media进程发送BP_transation

3.binder收到后发送br_transation。把servermanager唤醒,同时br_transtation_complete发送给客户端

4.客户端收到指令后,客户端处于等待的状态!

5.同上serverManager收到指令后。开始执行执行完后发送br_replay给binder驱动。同时还会发送

br_transtation_complete给servermanager。binder驱动发送br_rep给客户端唤醒

image.png

7.2 bpBinder和bBbinder

deepseek_mermaid_20250818_8cf0cb.png

7.2.1 、BpBinder(客户端代理)

核心作用:跨进程调用的代理对象

  1. 远程句柄管理

    • 持有服务端的handle(驱动分配的标识)
    • 示例:mHandle=5 代表ActivityManager服务
  2. 请求转发机制

    // frameworks/native/libs/binder/BpBinder.cpp
    status_t BpBinder::transact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        IPCThreadState* ipc = IPCThreadState::self();
        return ipc->transact(mHandle, code, data, reply, flags);
    }
    
    // Java层等价体现
    public class BinderProxy implements IBinder {
        private final int mNativeData; // 对应native的handle
    }
    

7.2.2 、BBinder(服务端基类)

核心作用:服务实现的基类对象

```
// frameworks/native/libs/binder/Binder.cpp
status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case INTERFACE_TRANSACTION:
            reply->writeString16(getInterfaceDescriptor());
            return NO_ERROR;
        default:
            return onTransact(code, data, reply, flags);
    }
}
```
关键差异对比
特性BpBinderBBinder
定位客户端代理服务端基类
内核关联无直接关联对应binder_node
核心方法转发transact到驱动实现onTransact处理请求
内存管理不涉及通过binder_node管理生命周期
典型子类BinderProxy(Java层)AIDL生成的Stub类

8.读写数据

deepseek_mermaid_20250818_03606f.png

binder_thread_write 和 binder_thread_read  和  copy_from_user 和copy_to_user

8.1 、binder_thread_write - 写操作(用户→内核)

作用:处理用户空间发送的BC_*协议命令

用户空间ioctl(BINDER_WRITE_READ)
→ binder_ioctl()
→ binder_thread_write()

关键代码

static int binder_thread_write(
    struct binder_proc *proc,
    struct binder_thread *thread,
    binder_uintptr_t binder_buffer,
    size_t size)
{
    while (ptr < end) {
        // 1. 从用户空间拷贝命令头
        copy_from_user(&cmd, user_ptr, sizeof(uint32_t));
        
        switch (cmd) {
        case BC_TRANSACTION:
            // 2. 拷贝完整事务数据
            copy_from_user(&tr, user_ptr, sizeof(tr));
            // 3. 处理事务
            binder_transaction(proc, thread, &tr);
            break;
        case BC_REPLY:
            // 类似处理回复...
            break;
        }
        ptr += sizeof(cmd) + data_size;
    }
}

典型处理流程

  1. 从用户缓冲区读取命令码(BC_*类型)
  2. 根据命令类型拷贝对应数据结构
  3. 执行具体命令(如发起事务、回复等)

8.2 、binder_thread_read - 读操作(内核→用户)

作用:向用户空间返回BR_*协议响应

用户空间ioctl(BINDER_WRITE_READ)
→ binder_ioctl()
→ binder_thread_read()
static int binder_thread_read(
    struct binder_proc *proc,
    struct binder_thread *thread,
    binder_uintptr_t binder_buffer,
    size_t size)
{
    wait_for_proc_work = thread->transaction_stack == NULL;
    
    // 1. 从待处理队列获取工作项
    binder_get_thread_work(thread, wait_for_proc_work);
    
    while (1) {
        // 2. 准备返回数据
        struct binder_work *w = thread->todo.next;
        switch (w->type) {
        case BINDER_WORK_TRANSACTION:
            // 3. 拷贝事务到用户空间
            copy_to_user(user_ptr, &tr, sizeof(tr));
            break;
        case BINDER_WORK_RETURN_ERROR:
            // 错误处理...
            break;
        }
    }
}

典型处理流程

  1. 检查线程的待处理队列(todo list)
  2. 根据工作项类型准备响应数据
  3. 通过copy_to_user返回给用户空间

8.3 、copy_from_user - 数据拷贝(用户→内核)

作用:安全地从用户空间拷贝数据

调用场景

  • 在binder_thread_write中拷贝:

    • BC_*命令头
    • binder_transaction_data结构体
    • 附加数据(如Parcel内容)
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
{
    if (access_ok(from, n)) {
        // 执行实际拷贝
        __copy_from_user(to, from, n);
    }
    return n;
}

安全机制

  1. 检查用户指针有效性(access_ok)
  2. 处理页错误异常
  3. 返回未拷贝的字节数(0表示成功)

8.4 、copy_to_user - 数据拷贝(内核→用户)

作用:安全地向用户空间写入数据

  • 在binder_thread_read中返回:

    • BR_*响应头
    • 事务处理结果
    • 错误码等

9. handle回调函数详解

数据是如何传递的,handle和type是怎么被计算和管理的

binder 驱动会判断 type 是什么,然后呢会往自己的进程挂上一个 binder_node ,

 会往目标进程挂上两个 binder_node 一个挂在以 handle 值为 key 的红黑树上,一个挂在以 cookie 为 key 的红黑树上,handle 值是根据红黑树的数据了累加的。

主要在: 驱动层有两个核心复杂方法 binder_thread_write 和 binder_thread_read

if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {

  goto fail_open;

}

  


// ServiceManager 进程:让 ServiceManager 进程成为管理者

int binder_become_context_manager(struct binder_state *bs)

{

  return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

}

  


// ServiceManager 进程:binder 线程进入循环等待

readbuf[0] = BC_ENTER_LOOPER;

binder_write(bs, readbuf, sizeof(uint32_t));

  


// ServiceManager 进程:进入循环不断的读写 binder 内容

for (;;) {

  bwr.read_size = sizeof(readbuf);

  bwr.read_consumed = 0;

  bwr.read_buffer = (uintptr_t) readbuf;

  res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

  ...

}

// media 进程:添加 MediaPlayerService 服务

do {

  // 通过 ioctl 不停的读写操作,跟 Binder Driver 进行通信,转发给 ServiceManager 进程

  if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

    err = NO_ERROR;

    ...

} while (err == -EINTR); //当被中断,则继续执行

10. 数据结构binder_node,binder_ref,binder_proc,binder_thread

binder_proc 就是用来存放 binder 相关数据的结构体,每个进程独有一份。

10.1 、binder_proc(进程描述符)

核心作用:描述一个使用Binder的进程

struct binder_proc {
    struct hlist_node proc_node;      // 全局进程链表节点
    struct rb_root threads;           // 线程红黑树(key: pid)
    struct rb_root nodes;             // binder_node红黑树(key: ptr)
    struct rb_root refs_by_desc;      // binder_ref红黑树(key: desc)
    struct rb_root refs_by_node;      // binder_ref红黑树(key: node)
    struct list_head todo;            // 待处理事务队列
    wait_queue_head_t wait;           // 等待队列
    struct vm_area_struct *vma;       // 内存映射区域
    struct mm_struct *vma_vm_mm;      // 内存管理结构
    pid_t pid;                        // 进程ID
    struct task_struct *tsk;          // 进程控制块
    int max_threads;                  // 最大Binder线程数
};

典型生命周期

  1. 创建open("/dev/binder")时初始化

  2. 管理

    • nodes树管理本进程提供的Binder实体
    • refs_by_desc树管理引用的远程服务
  3. 销毁:进程退出时清理所有资源

10.2 、binder_thread(线程描述符)

核心作用:描述一个Binder线程

struct binder_thread {
    struct binder_proc *proc;         // 所属进程
    struct rb_node rb_node;           // 进程线程树的节点
    struct list_head waiting_thread_node; // 等待线程链表
    int pid;                         // 线程ID
    int looper;                      // 线程状态标志
    struct binder_transaction *transaction_stack; // 事务栈
    struct list_head todo;           // 线程专属任务队列
    wait_queue_head_t wait;          // 等待队列
    struct binder_stats stats;       // 统计信息
};

关键行为

  1. 事务处理:从todo队列获取任务

  2. 线程休眠:无任务时在wait队列阻塞

    // 驱动通知创建新线程
    if (need_new_thread) {
        thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
        write32_cmd(BR_SPAWN_LOOPER);
    }
    

10.3 、binder_node(Binder实体)

核心作用:描述一个Binder服务实体

struct binder_node {
    int debug_id;                     // 调试标识
    struct binder_proc *proc;         // 所属进程
    struct hlist_node dead_node;      // 死亡节点链表
    struct hlist_head refs;           // 所有引用的链表
    int strong_refs;                  // 强引用计数
    int weak_refs;                    // 弱引用计数
    void __user *ptr;                 // 用户空间BBinder指针
    void __user *cookie;              // 用户空间Service对象
    struct list_head async_todo;      // 异步任务队列
};

引用计数规则

  1. 强引用+1:Binder.attachInterface()
  2. 弱引用+1:linkToDeath()
  3. strong_refs=0 && weak_refs=0时销毁节点

10.4 、binder_ref(Binder引用)

核心作用:描述对远程Binder实体的引用

struct binder_ref {
    int debug_id;                     // 调试标识
    uint32_t desc;                    // 引用句柄(用户可见)
    struct rb_node rb_node_desc;      // 按desc排序的红黑树节点
    struct rb_node rb_node_node;      // 按node排序的红黑树节点
    struct binder_node *node;         // 关联的binder_node
    struct binder_proc *proc;         // 引用方进程
    struct hlist_node node_entry;     // 节点引用链表
};

句柄分配机制

  1. 动态分配:binder_get_ref_for_node()
  2. 范围限制:1~INT_MAX
  3. 特殊句柄:0固定为ServiceManager

11. Binder驱动对于binder机制的总结:

image.png

11.1 我们在使用 Binder 时基本都是调用 framework 层封装好的方法,AIDL 就是 framework 层提供的傻瓜式是使用方式。假设服务已经注册完,我们来看看客户端怎么执行服务端的方法。

11.2 首先我们通过 ServiceManager 获取到服务端的 BinderProxy 代理对象,通过调用 BinderProxy 将参数,方法标识(例如:TRANSACTION_test,AIDL中自动生成)传给 ServiceManager,同时客户端线程进入等待状态。

11.3 ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通讯结束。