进程间通信(3)本地socket

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;
}


5、设置本地socket的系统属性
#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)

猜你喜欢

转载自blog.csdn.net/u013686019/article/details/78012861