Android LocalSocket、LocalServerSocket原理分析

Android LocalSocket、LocalServerSocket原理分析

在这里插入图片描述
查看Android源码会发现很多地方如Zygote就用到了LocalServerSocket来监听请求,它可以用用于本地进程、线程通信,也可以用于网络通信,LocalSocket底层实质用到了C++的Socket套接字,对其做了一定的封装;在此,理解其背后的运作原理,可以帮助我们在分析源码时,快速了解一些源码机制问题


LocalSocket基本使用

server服务端

//创建socket并绑定监听 新创建的
LocalServerSocket server = new LocalServerSocket(SOCKET_NAME);
while (true) {
    
    
  //等待建立连接
  LocalSocket receiver = server.accept();
  InputStream input = receiver.getInputStream(); 
}

client客户端

LocalSocket client =null;
LocalSocketAddress add;

client = new LocalSocket();
服务端名字和端口
add = new LocalSocketAddress(SOCKET_NAME,LocalSocketAddress.Namespace.RESERVED);
client.connect(add);

源码分析

从服务端分析入手,创建一个LocalServerSocket,如下源码:

public LocalServerSocket(String name) throws IOException
{
    
       
    impl = new LocalSocketImpl();
	配置socket套接字的协议、传输格式等
    impl.create(LocalSocket.SOCKET_STREAM);
	配置socket地址
    localAddress = new LocalSocketAddress(name);
    绑定地址
    impl.bind(localAddress);
	开始监听,并且最大并发数为LISTEN_BACKLOG
    impl.listen(LISTEN_BACKLOG);
} 

第一步 — 创建Socket

先来一副流程图,方便理解:
在这里插入图片描述
先是create创建Socket套接字,流程执行如上截图,重点讲解如上流程图的部分知识:

Os.createOs.socket(OsConstants.AF_UNIX, osType, 0)参数解释

这个socket创建实质和[C++创建Socket][1]类似,参数也是一样的

  • 参数一 协议域,决定socket底层访问地址的地址格式,如:
    AF_UNIX 本地访问,用path路径方式来标识目标
    AF_INET 支持ipv4访问地址格式
    AF_INET6 支持ipv6访问地址格式
  • 参数二 数据交互方式
    SOCKET_STREAM 流式数据交互格式,即TCP方式
    SOCKET_DGRAM 基于UDP方式交互
    SOCKET_SEQPACKET 基于SCTP交互格式
    还有其他很多种,不同的方式数据读取调用也不相同
  • 参数三 使用协议
    这个协议指传输层和网络层两层的协议,如IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP等,并且必须要和参数二的格式相同才行,比如SOCKET_STREAM,协议就要选IPPROTO_TCP
    以上这三个参数的意义同样也适用于C++的Socket创建方式

java方法Linux.socket如何调用C++函数Linux_socket ?

Linux.java源码位于:/libcore//luni/src/main/java/libcore/io/Linux.java
Linux_socket源码位于:./libcore//luni/src/main/native/libcore_io_Linux.cpp
在Linux java中定义:

public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; 

在libcore_io_Linux.cpp中定义:

static JNINativeMethod gMethods[] = {
    
    
	省略部分代码
	NATIVE_METHOD(Linux, socket, "(III)Ljava/io/FileDescriptor;")
};

void register_libcore_io_Linux(JNIEnv* env) {
    
    
    jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));
}

不难看出,使用了jni的动态注册函数,把libcore/io/Linux这个java类里面的native方法注册进来,具体注册了哪些native函数,在gMethods中存放着,而gMethods使用NATIVE_METHOD宏定义如下:

#define NATIVE_METHOD(className,functionName,signature) \
{
    
     #functionName,signature,reinterpret_cast(className ## _ ## functionName) }
按照上面的socket定义的结果转换为
{
    
    "socket", "(III)Ljava/io/FileDescriptor;", (强转为一个函数指针)Linux_socket}

其中#是字符串化,而##则是字符串拼接的意思,这样就解释得通了

最后,在Linux_socket方法中:

static jobject Linux_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
    
    
    if (domain == AF_PACKET) {
    
    
        protocol = htons(protocol);  // Packet sockets specify the protocol in host byte order.
    }    
    //socket函数创建套接字
    int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
    //返回套接字的文件描述符
    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;                                                                                                                                                                                                                           
}

第二步 — 为Socket绑定地址

在这里插入图片描述
绑定逻辑也很简单,最终都是走到C++层的bind函数,执行地址绑定,这里最终绑定的是一个字符串的名字如"server",以后客户端如果需要连接这个服务端,只要指定LocalSocket的服务名字为"server"即可

第三步 — 开始监听

其逻辑如上类似,都是java封装socket,最后到C++的listen()函数,只是注意LocalServerSocket传递的listen参数是50,说明最大并发支持50个连接数

客户端的读取发送数据过程就不在深入探究,其背后都是走的C++的Socket那一套流程,感兴趣的可以在此基础上继续分析

抛出一个问题,探讨下

跨进程数据传递不止Socket可以实现,还有管道、共享内存等等,Android上还特有Binder,那么Socket这一方式其性能如何?和他们比较之?楼主没有深入了解过,猜测其性能应该是最差的一类,猜测依据是因为Socket方式始终是走网络那一套流程,数据要经过网络模块、网卡等转一圈,再跑回来,这样一圈就很耗时,所以性能应该很差,不知道是不是这样,有懂的请留言告知,我们探讨一番!

更多精彩博文,加入我们,一同进步!

在这里插入图片描述
[1]:https://www.cnblogs.com/yskn/p/9346570.html

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/107459772