http://blog.csdn.net/u013686019/article/details/78012861
关于socket的基本知识参见Linux Socket编程。本文通过分析Telephony子系统学习Android如何使用本地socket进行IPC的。
一、socket的创建
在网络通信的世界中,socket通过"IP+协议+端口"连接彼此,对于本地socket,纽带则是设备节点,位于/dev/socket/目录下。Server端监听socket节点,Client端以对应节点为参数建立和Server进行通信的链路。
1、socket声明在init.rc
socket rild stream 660 root radio
字段规定:
- socket标识,init进程见到该字串后调用parse_line_service()函数进行解析
- socket名称,创建成功后出现节点/dev/socket/rild
- socket类型,支持"stream"、"dgram"、"seqpacket"
- socket权限
- socket user ID
- socket group ID
- socket的SELinux上下文(此处未设置)
2、init进程启动时解析socket行
static void parse_line_service(struct parse_state *state, int nargs, char **args) {
case K_socket: {/* name type perm [ uid gid context ] */
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
break;
}
if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
&& strcmp(args[2],"seqpacket")) {
parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
break;
}
socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
if (!si) {
parse_error(state, "out of memory\n");
break;
}
si->name = args[1];
si->type = args[2];
si->perm = strtoul(args[3], 0, 8);
if (nargs > 4)
si->uid = decode_uid(args[4]);
if (nargs > 5)
si->gid = decode_uid(args[5]);
if (nargs > 6)
si->socketcon = args[6];
si->next = svc->sockets;
svc->sockets = si;
break;
}
}
3、init进程创建socket入口
void service_start(struct service *svc, const char *dynamic_args) {
for (si = svc->sockets; si; si = si->next) {
int socket_type = (
!strcmp(si->type, "stream") ? SOCK_STREAM :
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid, si->socketcon ?: scon);
if (s >= 0) {
publish_socket(si->name, s);
}
}
}
4、创建本地socket
int create_socket(const char *name, int type, mode_t perm, uid_t uid,
gid_t gid, const char *socketcon)
{
struct sockaddr_un addr;
int fd, ret;
char *filecon;
// 1, PF_UNIX/PF_LOCAL, AF_UNIX/AF_LOCAL用于本地进程间通信的socket
fd = socket(PF_UNIX, type, 0);
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
// 2, addr.sun_path = "/dev/socket/rild"
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name);
// 3, 如果"/dev/socket/rild"已经存在,删除它
ret = unlink(addr.sun_path);
ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
// 4, 设置socket所属user、group、权限位
chown(addr.sun_path, uid, gid);
chmod(addr.sun_path, perm);
return fd;
}
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
static void publish_socket(const char *name, int fd)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
char val[64];
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, name, sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
snprintf(val, sizeof(val), "%d", fd);
// key: ANDROID_SOCKET_rild, val: socket(PF_UNIX, type, 0)创建的socket描述符
add_environment(key, val);
/* make sure we don't close-on-exec */
// 去掉fd的close-on-exec标志,这样当fork一个新进程时,fd不会被系统关闭,在新进程中fd依旧有效
fcntl(fd, F_SETFD, 0);
}
这里增加了一个名为"ANDROID_SOCKET_rild"的系统属性,且其值为socket的描述符。之后其他进程就可以通过该属性直接获取rild这个socket的描述符。
至此,本地socket通信的准备工作就做完了,下面开始使用。
二、本地socket的服务端
ril.cpp(hardware\ril\libril)
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {
int fdListen = -1;
int ret;
char socket_name[10];
memset(socket_name, 0, sizeof(char)*10);
strncpy(socket_name, "rild", 9);
// 1, 根据之前设置的"ANDROID_SOCKET_rild"的系统属性获取socket描述符
fdListen = android_get_control_socket(socket_name);
// 2, 监听客户端的连接请求
ret = listen(fdListen, 4);
// 3, 一旦有请求进来,调用listenCallback回调函数进行处理
ril_event_set (socket_listen_p->listen_event, fdListen, false, listenCallback, socket_listen_p);
}
流程:socket->bind->listen->accept->read/write,标准的服务器处理方式。
三、本地socket的客户端
对于Telephony子系统,客户端位于Java域:
RIL.java(frameworks\opt\telephony\src\java\com\android\internal\telephony)
class RILReceiver implements Runnable {
@Override public void run() {
int retryCount = 0;
String rilSocket = "rild";
try { for (;;) {
LocalSocket s = null;
LocalSocketAddress l;
try {
// 1, 创建名为"rild"的本地socket
s = new LocalSocket();
l = new LocalSocketAddress(rilSocket, LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
} catch (IOException ex){ }
// 2, 赋值给mSocket成员,send数据的时候需要用
mSocket = s;
// 3, socket通信的数据处理
try {
InputStream is = mSocket.getInputStream();
for (;;) {
length = readRilMessage(is, buffer);
processResponse(p);
}
} catch (java.io.IOException ex) {}
}
}
}
Java的LocalSocket接口的实现,其实还是传统的socket,具体实现位于:
socket_local_client.c/socket_local_server.c(system\core\libcutils)