Android下的LocalSocket

来源:互联网 发布:js中的splice 编辑:程序博客网 时间:2024/06/05 06:11

之前无意之中看到了这个术语,简单的以为无非就是在127.0.0.1的地址上建立了一个socket,然后在这个socket上进行通讯。直到后来和一个朋友聊起,才发现不是这么回事。于是回头上网找资料,果然有较大的差异。趁今天中午有时间,把看过的东西记下来,万一以后忘了,可以回放。首先看LocalSocket的使用场景,有demo,如下:
LocalServerSocket ss;
LocalSocket ls;
LocalSocket ls1;
ss = new LocalServerSocket(“android.net.LocalSocketTest”);
ls = new LocalSocket();
ls.connect(new LocalSocketAddress(“android.net.LocalSocketTest”));
ls1 = ss.accept();
由此可知,LocalSocket在实际使用时,就是使用LocalServerSocket(服务器端)和LocalSocket(客户端)进行通信。
先来看LocalServerSocket,包含成员变量:
private final LocalSocketImpl impl;
private final LocalSocketAddress localAddress;这个也好理解,就是地址信息
/* 50 seems a bit much, but it’s what was here /
private static final int LISTEN_BACKLOG = 50; 这个好理解,就是listen()函数的参数

其成员函数包括:
LocalServerSocket(String name);
LocalServerSocket(FileDescriptor fd);
LocalSocketAddress getLocalSocketAddress();
LocalSocket accept();
FileDescriptor getFileDescriptor();
void close();
通过这些东西可以知道:其实LocalServerSocket是在LocalSocket基础上再封装。而且和普通tcp的socket操作很类似,就是listen,然后accept。区别就是c/c++中listen的主体是socket,accept返回的也是socket.而在这里,listen的主体是LocalSocketImpl,如下:
public LocalServerSocket(FileDescriptor fd) throws IOException
{
impl = new LocalSocketImpl(fd);
impl.listen(LISTEN_BACKLOG);
localAddress = impl.getSockAddress();
}
而accept返回的是一个LocalSocket。必然的,LocalSocket和LocalSocketImpl之间存在密切关系。LocalSocket的定义中包含变量如下:
private LocalSocketImpl impl;
private volatile boolean implCreated;
private LocalSocketAddress localAddress;
private boolean isBound;
private boolean isConnected;
private final int sockType;

/** unknown socket type (used for constructor with existing file descriptor) *//* package */ static final int SOCKET_UNKNOWN = 0;/** Datagram socket type */public static final int SOCKET_DGRAM = 1;/** Stream socket type */public static final int SOCKET_STREAM = 2;/** Sequential packet socket type */public static final int SOCKET_SEQPACKET = 3;

可知,LocalSocketImpl是LocalSocket成员变量。而且通过观察LocalSocket的成员函数的实现可知,实际上,真正实现socket通信的就是成员impl.比如
LocalSocket::bind实现如下:
public void bind(LocalSocketAddress bindpoint) throws IOException {
implCreateIfNeeded();
synchronized (this) {
if (isBound) {
throw new IOException(“already bound”);
}
localAddress = bindpoint;
impl.bind(localAddress);
isBound = true;
}
}
也就是说,bind实际调用的就是impl.bind
LocalSocket的connect则实现如下:
public void connect(LocalSocketAddress endpoint) throws IOException {
synchronized (this) {
if (isConnected) {
throw new IOException(“already connected”);
}
implCreateIfNeeded();
impl.connect(endpoint, 0);
isConnected = true;
isBound = true;
}
}
实际就是impl的connect。
而impl的bind和connect则是这样:
public void bind(LocalSocketAddress endpoint) throws IOException
{
if (fd == null) {
throw new IOException(“socket not created”);
}
bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
}
protected void connect(LocalSocketAddress address, int timeout)
throws IOException
{
if (fd == null) {
throw new IOException(“socket not created”);
}
connectLocal(fd, address.getName(), address.getNamespace().getId());
}
这里的fd其实就是通过socket(int domain, int type, int protocol)创建的一个socket。这个socket可以是外部创建好,在构造impl的时候作为形参传入,也可以由impl通过create来创建。create实现如下:
public void create (int sockType) throws IOException {
// no error if socket already created
// need this for LocalServerSocket.accept()
if (fd == null) {
int osType;
switch (sockType) {
case LocalSocket.SOCKET_DGRAM:
osType = OsConstants.SOCK_DGRAM;
break;
case LocalSocket.SOCKET_STREAM:
osType = OsConstants.SOCK_STREAM;
break;
case LocalSocket.SOCKET_SEQPACKET:
osType = OsConstants.SOCK_SEQPACKET;
break;
default:
throw new IllegalStateException(“unknown sockType”);
}
try {
fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
mFdCreatedInternally = true;
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
}
}
到现在为止,socket已经看到了,但是impl中的bind和connect并不是通常c/c++中看到的bind,connect,而是bindLocal和connectLocal,这两个函数又是怎么回事?这两个函数声明如下:
private native void connectLocal(FileDescriptor fd, String name,
int namespace) throws IOException;
private native void bindLocal(FileDescriptor fd, String name, int namespace)
throws IOException;
看到了native,不懂,百度一下,native的意思如下:
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
这不就是和调用动态链接库中的函数类似吗?
那么connectLocal和bindLocal函数的实现呢?继续百度,高手们提示在android_net_LocalSocketImpl.cpp中,于是在github中找到了这个文件,链接地址如下:
https://github.com/android/platform_frameworks_base/blob/master/core/jni/android_net_LocalSocketImpl.cpp
看了下这个代码,其中有一段如下:
这里写图片描述

这个应该是就是c/c++中函数和java中native函数的对应关系了,connectLocal对应于socket_connect_local,bindLocal则对应于socket_bind_local了。再看这两个函数:
这里写图片描述
可知socket_bind_local实际调用的就是socket_local_server_bind,做过c/c++下socket开发的朋友估计一看就知道这个函数的实现了,该函数的实现在https://github.com/android/platform_system_core/blob/master/libcutils/socket_local_server_unix.c,打开一看,熟悉的味道,熟悉的配方:
这里写图片描述
最后就是调用bind。
但是有疑问的是,难道Android下不支持NON-Block的socket通信?因为在setoption中没有看到该选项,有懂得朋友可以指点一下,先谢过了。

原创粉丝点击