Android多个网络连接,校园安卓面试题

return 0;
}
// Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
// with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())
// might itself cause another check with the fwmark server, which would be wasteful.
int socketFd;
if (libcSocket) {
socketFd = libcSocket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
} else {
socketFd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
}
if (socketFd < 0) {
return -errno;
}
int error = setNetworkForSocket(netId, socketFd);
if (!error) {
*target = netId;
}
close(socketFd);
return error;
}

extern “C” int setNetworkForSocket(unsigned netId, int socketFd) {
if (socketFd < 0) {
return -EBADF;
}
FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
return FwmarkClient().send(&command, socketFd);
}

extern “C” int setNetworkForProcess(unsigned netId) {
return setNetworkForTarget(netId, &netIdForProcess);
}

3. 客户端发送 FwmarkCommand::SELECT_NETWORK 通知服务端处理,代码在 /system/netd/server/FwmarkServer.cpp

int FwmarkServer::processClient(SocketClient* client, int* socketFd) {
// …
Fwmark fwmark;
socklen_t fwmarkLen = sizeof(fwmark.intValue);
if (getsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) {
return -errno;
}

switch (command.cmdId) {
// …
case FwmarkCommand::SELECT_NETWORK: {
fwmark.netId = command.netId;
if (command.netId == NETID_UNSET) {
fwmark.explicitlySelected = false;
fwmark.protectedFromVpn = false;
permission = PERMISSION_NONE;
} else {
if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),
command.netId)) {
return ret;
}
fwmark.explicitlySelected = true;
fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());
}
break;
}
// …
}

fwmark.permission = permission;

if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,
sizeof(fwmark.intValue)) == -1) {
return -errno;
}

return 0;
}

union Fwmark {
uint32_t intValue;
struct {
unsigned netId : 16;
bool explicitlySelected : 1;
bool protectedFromVpn : 1;
Permission permission : 2;
};
Fwmark() : intValue(0) {}
};

最后其实只是给 socketFd 设置了 mark,为什么这样就可以达到使用特定网络的目的呢。这里的实现原理大致为:
1. 该进程在创建socket时(app首先调用setProcessDefaultNetwork()),android底层会利用setsockopt函数设置该socket的SO_MARK为netId(android有自己的管理逻辑,每个Network有对应的ID),以后利用该socket发送的数据都会被打上netId的标记(fwmark 值)。
2. 利用策略路由,将打着netId标记的数据包都路由到指定的网络接口,例如WIFI的接口wlan0。
Linux 中的策略路由暂不在本章展开讨论,这里只需要了解通过这种方式就能达到我们的目的。

Hook socket api

也就是说只要在当前进程中利用setsockopt函数设置所有socket的SO_MARK为netId,就可以完成所有的请求都走特定的网络接口。

1. 先来看一下 /bionic/libc/bionic/socket.cpp

int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
}

2. /bionic/libc/private/NetdClientDispatch.h

struct NetdClientDispatch {
int (accept4)(int, struct sockaddr, socklen_t*, int);
int (connect)(int, const struct sockaddr, socklen_t);
int (*socket)(int, int, int);
unsigned (*netIdForResolv)(unsigned);
};

extern LIBC_HIDDEN struct NetdClientDispatch __netdClientDispatch;

3. /bionic/libc/bionic/NetdClientDispatch.cpp

extern “C” __socketcall int __accept4(int, sockaddr*, socklen_t*, int);
extern “C” __socketcall int __connect(int, const sockaddr*, socklen_t);
extern “C” __socketcall int __socket(int, int, int);

static unsigned fallBackNetIdForResolv(unsigned netId) {
return netId;
}

// This structure is modified only at startup (when libc.so is loaded) and never
// afterwards, so it’s okay that it’s read later at runtime without a lock.
LIBC_HIDDEN NetdClientDispatch __netdClientDispatch attribute((aligned(32))) = {
__accept4,
__connect,
__socket,
fallBackNetIdForResolv,
};

4. /bionic/libc/bionic/NetdClient.cpp

template
static void netdClientInitFunction(void* handle, const char* symbol, FunctionType* function) {
typedef void (InitFunctionType)(FunctionType);
InitFunctionType initFunction = reinterpret_cast(dlsym(handle, symbol));
if (initFunction != NULL) {
initFunction(function);
}
}

static void netdClientInitImpl() {
void* netdClientHandle = dlopen(“libnetd_client.so”, RTLD_NOW);
if (netdClientHandle == NULL) {
// If the library is not available, it’s not an error. We’ll just use
// default implementations of functions that it would’ve overridden.
return;
}
netdClientInitFunction(netdClientHandle, “netdClientInitAccept4”,
&__netdClientDispatch.accept4);
netdClientInitFunction(netdClientHandle, “netdClientInitConnect”,
&__netdClientDispatch.connect);
netdClientInitFunction(netdClientHandle, “netdClientInitNetIdForResolv”,
&__netdClientDispatch.netIdForResolv);
netdClientInitFunction(netdClientHandle, “netdClientInitSocket”, &__netdClientDispatch.socket);
}

static pthread_once_t netdClientInitOnce = PTHREAD_ONCE_INIT;

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

最后针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

需要资料的朋友可以点击我的GitHub免费领取

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

917)]
[外链图片转存中…(img-ccAfN0GW-1710960739917)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-nZRpmX6P-1710960739918)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值