Socket的使用和原理
socket套接字本来是设计给基于TCP/IP协议的网络通信使用的,但由于它是一种C/S架构模型,即客户端服务器端架构,这种模型能带来很大的安全性以及快速的响应能力,所以也常常用在进程之间的通信上。
Socket的使用方式比上面前面提到的其他IPC都要复杂很多,我们先通过下图了解它的使用流程。
236c1533b78642b5bf5e3d6c5959b12e_tplv-k3u1fbpfcp-zoom-1.png
我们在看看具体的函数
#include
#include
#include
int socket(int protofamily, int type, int protocol);//创建socket
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//绑定socket
int listen(int sockfd, int backlog);//监听端口号
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//客户端请求建立连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//服务端接收连接请求
ssize_t send(int sockfd, const void *buf, size_t len, int flags); //IO写函数
ssize_t recv(int sockfd, void *buf, size_t len, int flags);//IO读函数
int close(int fd); //关闭函数
Linux系统中万物皆文件,所以Socket也是一个虚拟文件,socket文件的数据结构中包含了当前主机的ip地址,当前主机进程的端口号,发送端主机的ip地址等信息,通过这些信息,我们可以在虚拟文件系统中唯一定位到一个Socket文件,通过对这个文件的读写达到通信的目的。
Socket在Android系统中的使用场景
当我们使用socket来进行进程间的通信时,实际是通过将IP设置为127.0.0.1这个本地IP来实现的,Android系统为我们提供了LocalSocket来进行进程间的通信,LocalSocket的实质也是对Socket的封装,通过直接使用LocalSocket,我们省掉了设置本机IP等一系列繁琐的操作。
我们看一个LocalSocket的使用场景:当我们启动一个App应用时,如果当前的应用的进程不存在,AMS会通过Socket通知Zygote去Fork新进程。
//文件-->frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
// 服务端
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
……
zygoteServer.registerServerSocket(socketName);
……
Log.i(TAG, "Accepting command socket connections");
zygoteServer.runSelectLoop(abiList);
zygoteServer.closeServerSocket();
……
}
ZygoteInit启动时,会创建一个ZygoteServer,然后fork生成System Server进程,接着启动整个Framwork的Server,最终执行zygoteServer的runSelectLoop函数,始终等待其他进程发送过来的fork进程的消息。
我们接着看registerServerSocket函数和runSelectLoop函数的实现
//文件-->/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce(this);
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
可以看到registerServerSocket函数实际是创建了LocalServerSocket,这个LocalServerSocket的名字就叫“zygote”,runSelectLoop函数将ServerSocket加入多路复用模型里,当收到消息时便调用runOnce方法去fork进程。
//文件-->/frameworks/base/core/java/android/os/Process.java
//客户端
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
//文件-->/frameworks/base/core/java/android/os/ZygoteProcess.jav
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
private Process.ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] extraArgs)
throws ZygoteStartFailedEx {
……
synchronized(mLock) {
//连接服务端socket,并发送数据
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(mSocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}
……
}
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList args)
throws ZygoteStartFailedEx {
int sz = args.size();
for (int i = 0; i < sz; i++) {
if (args.get(i).indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("embedded newlines not allowed");
}
}
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
Process.ProcessStartResult result = new Process.ProcessStartResult();
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
}
从上面的代码实现可以看到,当AMS调用Process的start()函数时,最终执行到了ZygoteProcess类中的openZygoteSocketIfNeeded() 函数,连接socket,然后调用zygoteSendArgsAndGetResult() 函数通过LocalSocket 往LocalServerSocket发送消息 。