linux local socket,关于Local socket

最近做项目,遇到一个需要通过Localsocket通信的问题,实现Library建立local socket服务端,APK实现Local socket客户端,以前自己也做过,直接在网上找的代码,客户端Java,服务器端C,没有问题。这次客户端还是照着之前的做,服务器端同时做的,然后怎么都连接不上,发现他建立Local socket的方式跟我以前的不一样,然后仔细研究了一下才发现,Local socket的实现方式也分好几种情况。

下面就Android App和Linux中关于Local socket的定义分别介绍。

一 ,Android App中的Local socket

1. Android的LocalSocketAddress

先看看关于LocalSocketAddress的两个构造函数:

/**

* The namespace that this address exists in. See also

* include/cutils/sockets.h ANDROID_SOCKET_NAMESPACE_*

*/

public enum Namespace {

/** A socket in the Linux abstract namespace */

ABSTRACT(0),

/**

* A socket in the Android reserved namespace in /dev/socket.

* Only the init process may create a socket here.

*/

RESERVED(1),

/**

* A socket named with a normal filesystem path.

*/

FILESYSTEM(2);

/** The id matches with a #define in include/cutils/sockets.h */

private int id;

Namespace (int id) {

this.id = id;

}

/**

* @return int constant shared with native code

*/

/*package*/ int getId() {

return id;

}

public LocalSocketAddress(String name, Namespace namespace) {

this.name = name;

this.namespace = namespace;

}

public LocalSocketAddress(String name) {

this(name,Namespace.ABSTRACT);

}

可以看到NameSpace有三种:

ABSTRACT:在系统中维护一个socket描述符,不会在系统上建立文件。

FILESYSTEM:系统会在用户指定的路径下创建一个描述符文件。

RESEVED:系统保留,其本质也是FILESYSTEM,只是文件在/dev/socket/目录下,并且只有init进程才有权限创建

可以看到,如果我们直接用LocalSocketAddress(String name);来指定一个socket的话,那么系统默认它是一个ABSTRACT类型的Local socket。

2.android中socket server端的建立

public LocalServerSocket(String name) throws IOException

{

impl = new LocalSocketImpl();

impl.create(true);

localAddress = new LocalSocketAddress(name);

impl.bind(localAddress);

impl.listen(LISTEN_BACKLOG);

}

/**

* Create a LocalServerSocket from a file descriptor that's already

* been created and bound. listen() will be called immediately on it.

* Used for cases where file descriptors are passed in via environment

* variables

*

* @param fd bound file descriptor

* @throws IOException

*/

public LocalServerSocket(FileDescriptor fd) throws IOException

{

impl = new LocalSocketImpl(fd);

impl.listen(LISTEN_BACKLOG);

localAddress = impl.getSockAddress();

}

我们可以看到,有两种方式建立Local socket的server端,一种是指定名字,这种就对于的是"ABSTRACT"方式,另一中需要指定文件描述符,而且需要实现创建文件描述符并且bound。在网上搜索了一下,没有发现用这种方式创建socket server的,在framework的源码中搜索了一下,发现只有一个地方用到了这个,就是在ZygoteInit.java中。其中createFileDescritor是一个Native的函数,最终调用的是jniCreateFileDescriptor来创建一个文件描述符。在上层socket应用中,一般用不到这种方式。

private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";

String env = System.getenv(ANDROID_SOCKET_ENV);

fileDesc = Integer.parseInt(env);

sServerSocket = new LocalServerSocket(createFileDescriptor(fileDesc));

3. android中客户端socket的建立

/**

* Creates a AF_LOCAL/UNIX domain stream socket.

*/

public LocalSocket() {

this(new LocalSocketImpl());

isBound = false;

isConnected = false;

}

/**

* Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor.

* @hide

*/

public LocalSocket(FileDescriptor fd) throws IOException {

this(new LocalSocketImpl(fd));

isBound = true;

isConnected = true;

}

/**

* for use with AndroidServerSocket

* @param impl a SocketImpl

*/

/*package*/ LocalSocket(LocalSocketImpl impl) {

this.impl = impl;

this.isConnected = false;

this.isBound = false;

}

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;

}

}

LocalSocket有三种构造函数,通常我们用无参数的构造函数,然后用connect中的LocalSocketAddress来指定需要连接的socket的名字以及类型。

二, Linux中的Local socket

A UNIX domain socket address is represented in the following

structure:

#define UNIX_PATH_MAX 108

struct sockaddr_un {

sa_family_t sun_family; /* AF_UNIX */

char sun_path[UNIX_PATH_MAX]; /* pathname */

};

Linux通过sockaddr_un来指定特定的socket,下面是关于socket的说明:

* pathname: a UNIX domain socket can be bound to a null-terminated

filesystem pathname using bind(2). When the address of a pathname

socket is returned (by one of the system calls noted above), its

length is

offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1

and sun_path contains the null-terminated pathname. (On Linux,

the above offsetof() expression equates to the same value as

sizeof(sa_family_t), but some other implementations include other

fields before sun_path, so the offsetof() expression more portably

describes the size of the address structure.)

For further details of pathname sockets, see below.

* unnamed: A stream socket that has not been bound to a pathname

using bind(2) has no name. Likewise, the two sockets created by

socketpair(2) are unnamed. When the address of an unnamed socket

is returned, its length is sizeof(sa_family_t), and sun_path

should not be inspected.

* abstract: an abstract socket address is distinguished (from a

pathname socket) by the fact that sun_path[0] is a null byte

('\0'). The socket's address in this namespace is given by the

additional bytes in sun_path that are covered by the specified

length of the address structure. (Null bytes in the name have no

special significance.) The name has no connection with filesystem

pathnames. When the address of an abstract socket is returned,

the returned addrlen is greater than sizeof(sa_family_t) (i.e.,

greater than 2), and the name of the socket is contained in the

first (addrlen - sizeof(sa_family_t)) bytes of sun_path. The

abstract socket namespace is a nonportable Linux extension.

简单来说,"Pathname"方式在sun_path变量中指定路径名字,"abstract"方式sun_path的首位一定要是’\0',并且按照它要求的特定方式设置path,“unamed”方式不需要指定名字。

“Pathname”方式对于android中的FILESYSTEM, "abstract"对于android中的“ABSTRACT”,“unamed”非命名的应该是在最开始的时候就创建一对socket,然后利用这一对socket来通信。

对于跨平台的socket通信开发,一定要指定相同的LocalSocket方式,否则通信不会成功。对于相同的平台,可以不用指定,都是用系统默认的方式大部分时候也是没有问题的。

你可能感兴趣的:(linux,local,socket)