ADB(八)_WiFi连接(host端和设备端通过TCP连接的过程分析)

10 篇文章 2 订阅
8 篇文章 13 订阅

前言

前文

ADB(一)_概况了解
ADB(二)_ADBD_main()函数代码梳理
ADB(三)_ADBD_adbd_main()函数代码梳理
ADB(四)_host端的启动流程代码梳理
ADB(五)_host端adb server相关的代码梳理
ADB(六)_调试ADB(ADB设置自身日志的代码梳理和设置ADB自身日志可见)
ADB(七)_USB连接 (ABD通过USB连接的流程分析)

我们已经知道,ADB经常使用的是USB连接Android开发设备,不过ADB不光只能使用USB来连接设备。它还可以使用WiFi来链接局域网内的设备,就这为某些设备在不方便使用USB的情况提供了巨大的便利。
今天我们主要针对host端和Android设备通过TCP连接的情况来说明,这里就当是大家已经对TCP通信有所了解,至少是知道在开发中的创建一个使用C++实现的C/S的socket通信的简单过程。

首先,我们从Android 设备端说起,因为在这个过程中,我们的Android设备就相当于一个TCP通信的Server,而我们的host端运行的adb就是通信的Client.

在正式对adbd的代码进行梳理之前,我们先对TCP通信编程的步骤进行简单的介绍,
- tcp编程的 server端

  1. 调用socket()函数来创建socket
  2. 调用setsockopt()函数来设置相关属性
  3. 调用bind()函数绑定IP和端口信息到socket上
  4. 调用listen()函数来开启监听
  5. 调用accept()函数来接受client端的通信请求
  6. 调用send()/write()函数来发送数据;调用reve()/read()函数来接受数据
  7. 调用close()/shutdown()函数来关闭通信

- tcp编程的 client 端

  1. 调用socket()函数来创建socket
  2. 调用setsockopt()函数来设置相关属性
  3. 调用bind()函数绑定IP和端口信息到socket上
  4. 调用connect()函数来连接server端
  5. 调用send()/write()函数来发送数据;调用reve()/read()函数来接受数据
  6. 调用close()/shutdown()函数来关闭通信

Android设备端的TCP连接 初始化和传输处理流程

在adbd的初始化过程中,就在adbd_main()函数中是有对tcp通信初始化的,我们现在就来看看:

int adbd_main(int server_port) {
    ...
    std::string prop_port = android::base::GetProperty("service.adb.
    tcp.port", "");
    if (prop_port.empty()) {
        prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
    }
    int port;
    if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
        setup_port(port);
    } else if (!is_usb) {
        setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
        // DEFAULT_ADB_LOCAL_TRANSPORT_PORT =5555;
    }
    ...
    return 0;
}

我们可以看到,adbd会首先读取Android的系统属性"service.adb.
tcp.port",这个属性就是用于我们的adbd进行tcp通信的端口设置,如果此端口被设置,我们就会使用设置的端口来初始化我们的tcp,如果没有设置,并且当前不是使用usb通信的,那么就会使用默认的端口DEFAULT_ADB_LOCAL_TRANSPORT_PORT = 5555来初始化tcp,不管使用设置的端口还是使用默认的端口,接下来都会调用setup_port()函数,我们就到setup_port()函数中一探究竟:

1. setup_port()

static void setup_port(int port) {
    local_init(port);
    setup_mdns(port);
}

setup_port()函数内部只要就是调用了两个函数,第一个就是我们的tcp传输设置的local_init()函数.第二个是Android组播域名服务设置的setup_mdns(),我们主要对第一个函数进行说明:

2. local_init()

void local_init(int port)
{
    ...
    func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
    debug_name = "server";
    ...
    std::thread(func, port).detach();
}

local_init()函数内部也很简单,首先通过调用use_qemu_goldfish()判断是emulator还是Android设备中,当前我们分析的是Android设备端的代码,所以这里我们需要的func为server_socket_thread(),然后,启动一个新的进程专门用来执行server_socket_thread()函数,并且传入端口号.

3.server_socket_thread()

接下来我们就要看看server_socket_thread()的实现是怎么样子的:

static void server_socket_thread(int port) {
    int serverfd, fd;
    serverfd = -1;
    for(;;) {
        if(serverfd == -1) {
            std::string error;
            serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
            if(serverfd < 0) {
                std::this_thread::sleep_for(1s);
                continue;
            }
            close_on_exec(serverfd);
        }

        fd = adb_socket_accept(serverfd, nullptr, nullptr);
        if(fd >= 0) {
            ...
            std::string serial = android::base::StringPrintf("host-%d", fd);
            if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
                adb_close(fd);
            }
        }
    }
}

如上代码所示:server_socket_thread()函数主要就是处理tcp通信中server端的初始化,先概括一下函数内部的处理逻辑:

  • 首先函数进入一个大的死循环中,然后通过调用network_inaddr_any_server()函数来创建server端的socket.
  • 调用 adb_socket_accept()来接受client端的请求信息,然后fd.用于tcp的通信
  • 最后就调用register_socket_transport()函数来注册TCP传输事件.

接下来,我们就对上述的三个处理部分进行详细说明:

3.1 network_inaddr_any_server()

inline int network_inaddr_any_server(int port, int type, std::string* error) {
  return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}

network_inaddr_any_server()只是调用了socket_inaddr_any_server()来创建socket,socket_inaddr_any_server()函数的具体实现在/system/core/libcutils/下,如下所示:

/system/core/libcutils/socket_inaddr_any_server_unix.cpp

int socket_inaddr_any_server(int port, int type)
{
    struct sockaddr_in6 addr;
    int s, n;

    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(port);
    addr.sin6_addr = in6addr_any;

    s = socket(AF_INET6, type, 0);
    if (s < 0) return -1;
    n = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        close(s);
        return -1;
    }
    if (type == SOCK_STREAM) {
        int ret;
        ret = listen(s, LISTEN_BACKLOG);
        if (ret < 0) {
            close(s);
            return -1;
        }
    }
    return s;
}

3.2 adb_socket_accept

在过去server端的socket后,我们就会调用adb_socket_accept()函数来接受请求;其实内部实现也还是调用accept()方法,如下所示:

static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
{
    int fd;
    fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
    if (fd >= 0)
        close_on_exec(fd);
    return fd;
}

但accept()成功接受到数据,就会返回一个新的fd,这个fd就代表这这个通信连接,我们就可以在对这个fd进行write()/send()操作来发送传输数据,和read()/reve()操作来接收传输数据.

最后,我们会将代表的通信连接的fd传入到register_socket_transport()函数中进行tcp通信事件的注册.

4.register_socket_transport()

int register_socket_transport(int s, const char* serial, int port, int local) {
    atransport* t = new atransport();
    ...
    D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
    if (init_socket_transport(t, s, port, local) < 0) {
        delete t;
        return -1;
    }
    ...
    pending_list.push_front(t);
    t->serial = strdup(serial);
    ...
    register_transport(t);
    return 0;
}

register_socket_transport()函数中,

  • 首先获取到atransport实例,然后调用init_socket_transport()函数来给这个atransport赋值
  • 然后将当前赋值完成后的atransport添加到pending_list列表中.
  • 最后调用register_transport()函数进行对当前的atransport实例的注册,
    接下来我们到register_transport()的实现中看看:

5. register_transport()

/* the fdevent select pump is single threaded */
static void register_transport(atransport* transport) {
    tmsg m;
    m.transport = transport;
    m.action = 1;
    D("transport: %s registered", transport->serial);
    if (transport_write_action(transport_registration_send, &m)) {
        fatal_errno("cannot write transport registration socket\n");
    }
}

在register_transport,将传进来的transport封装成tmsg,然后通过transport_write_action()函数写入到transport_registration_send中,这个transport_registration_send我们多次遇到,这前面的usb的通信的梳理中已经说明,之后的处理情况就和usb类似,这里就不在赘述.

接下来我们就去看看host算的TCP初始化是怎么样子的.

host端的TCP连接初始化和传输处理流程

host端在tcp通信中充当的就是client,client并不需要早早初始化好.一般情况需要等到server端准备就绪后,client才会去连接server并进行通信,
一般情况下,host端的tcp连接不存在的.需要我们手动键入"adb connect ip:port"来手动连接tcp.并且在连接tcp之前会手动设置端口.所以,在host连接TCP是需要先知道目标server的IP_address和端口,才能去连接server端的,我们现在就去看看源码中是怎么实现的.

说明

在终端键入"adb connect ip_address:port"后,首先会有一段adb clientadb server通信的过程,然后adb server 会对adb client 的请求做出相应的处理,并返回结果在终端上;这里的adb client可以理解为终端,adb server才是实际处理adb命令的真正主体.这里的tcp连接可以理解为是host端的adb server和Android设备端的adbd连接,adb server是tcp通信中的client, adbd 是server

我们就从adb server 连接TCP是开始看起:

1. host_service_to_socket

host_service_to_socket()函数会帮助adb server处理adb client的请求,我们看当我们此时的请求为"adb connect ip_address:port"时,代码运行的状态:

asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
    if (!strcmp(name, "track-devices")) {
        return create_device_tracker(false);
        ...
    } else if (!strncmp(name, "connect:", 8)) {
        char* host = strdup(name + 8);
        int fd = create_service_thread("connect", connect_service, host);
        if (fd == -1) {
            free(host);
        }
        return create_local_socket(fd);
    }
    return NULL;
}   

我们就根根据上面的代码来看看,首先host_service_to_socket()函数会根据adb client的请求名称来匹配.如果当前的请求就是需要connectAndroid设备时:

  • 首先会调用create_service_thread()函数将name【请求名称】;connect_device【处理函数】; host【连接目标】传入.
  • 然后调用create_local_socket()将上面create_service_thread()函数返回的fd传入,用来后续处理和adbd的连接

1.1 create_service_thread

static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
    int s[2];
    if (adb_socketpair(s)) {
        printf("cannot create service socket pair\n");
        return -1;
    }
    ...
    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
    if (sti == nullptr) {
        fatal("cannot allocate stinfo");
    }
    sti->service_name = service_name;
    sti->func = func;
    sti->cookie = cookie;
    sti->fd = s[1];

    std::thread(service_bootstrap_func, sti).detach();
    return s[0];
}

create_service_thread()内部主要是创建stinfo,并进行赋值,然后创建一个新的现成调用service_bootstrap_func()

1.1.1 service_bootstrap_func

static void service_bootstrap_func(void* x) {
    stinfo* sti = reinterpret_cast<stinfo*>(x);
    adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
    sti->func(sti->fd, sti->cookie);
    free(sti);
}

service_bootstrap_func()函数其实也没有做什么其他的,只是执行了一个函数,就是在内部调用了封装在stinfo中的方法connect_service(),
接着进入到connect_service()函数中看看:

2 connect_service

static void connect_service(int fd, void* data) {
    ...
    if (!strncmp(host, "emu:", 4)) {
        connect_emulator(host + 4, &response);
    } else {
        connect_device(host, &response);
    }
    ...
    adb_close(fd);
}

进入到connect_service中,会有一个判断,就是判断请求连接的目标是emulator还是Android`s device ,由于我们是要连接Android 设备,所以会走到函数中connect_device()中;

2.1 connect_device

void connect_device(const std::string& address, std::string* response) {
    ...
    std::string serial;
    std::string host;
    int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
    if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
        return;
    }

    std::string error;
    int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
    ...
    // Send a TCP keepalive ping to the device every second so we can detect disconnects.
    if (!set_tcp_keepalive(fd, 1)) {
        D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
    }

    int ret = register_socket_transport(fd, serial.c_str(), port, 0);
    ...
}

这个connect_device()函数主要就是处理TCP 中创建socket,然后注册一个socket传输事件;

2.1.1 network_connect

  • 首先调用network_connect()去了创建socket去连接目标地址,
inline int network_connect(const std::string& host, int port, int type,
                           int timeout, std::string* error) {
    int getaddrinfo_error = 0;
    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
                                         &getaddrinfo_error);
    ...
    return fd;
    ...
}

network_connect()函数是调用socket_network_client_timeout()函数的实现在/system/core/libcutils/socket_network_client_unix.cpp中,这主要就是对socket编程中的一些操作,然后直接表示连接的fd,我们可以通过这个fd进行write()/send()操作进行数据发送任务,或者进行read()/reve()操作进行数据接受的任务.

-然后,调用register_socket_transport()函数对当前的socket连接注册一个socket传输事件;

2.1.2 register_socket_transport

int register_socket_transport(int s, const char* serial, int port, int local) {
    atransport* t = new atransport();
    ...
    D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
    if (init_socket_transport(t, s, port, local) < 0) {
    ...
    pending_list.push_front(t);
    t->serial = strdup(serial);
    ...
    register_transport(t);
    return 0;
}

这个函数已经多次讲过.就是创建atransport实例,然后赋值初始化,将当前atransport实例装进init_socket_transport中,最后调用register_transport()注册一次传输事件,不论是usb,还是tcp传输,都是调用这个函数进行的:

2.1.2.1 register_transport
static void register_transport(atransport* transport) {
    tmsg m;
    m.transport = transport;
    m.action = 1;
    D("transport: %s registered", transport->serial);
    if (transport_write_action(transport_registration_send, &m)) {
        fatal_errno("cannot write transport registration socket\n");
    }
}

register_transport()函数的主要逻辑就是:将传进来的transport封装成tmsg,然后通过transport_write_action()函数写入到transport_registration_send中,这个transport_registration_send我们多次遇到,这前面的usb的通信的梳理中已经说明,之后的处理情况就和usb类似,这里也不在赘述.

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值