阅读Android源码,可以发现init.rc里面有很多利用socket通信的例子,比如说zygote进程(Android6.0位于init.${ro.zygote}.rc),比如说installd进程,比如说vold进程。下面,我们参考installd来自己实现一个利用socket通信的demo程序,以便我们更好理解系统socket的使用。
代码流程图:
1. 首先,在init.rc里面注册一个socketserver服务端,声明socket句柄为mysocket,权限为666,system:system用户组,最终会在/dev/socket生成一个mysocket的文件,客户端可以连接这个socket与服务端进行通信。
service socketserver /system/bin/socketserver
class main
socket mysocket stream 666 system system
2. socketserver的实现,可以看到,与一般的socket通信并无太大差异,注意的是socket描述符是通过android_get_control_socket("mysocket")获取的,监听客户端连接,并且读取两次数据,第一次读取传过来的字符串长度,第二次读取传过来的字符串,然后将字符串原封不动的发送给客户端。
#define LOG_TAG "SOCKET_SERVER"
#define SOCKET_PATH "mysocket"
#define BUFFER_MAX 1024
static int readx(int s, void *_buf, int count)
{
char *buf = _buf;
int n = 0, r;
if (count < 0) return -1;
while (n < count) {
r = read(s, buf + n, count - n);
if (r < 0) {
if (errno == EINTR) continue;
ALOGE("read error: %s\n", strerror(errno));
return -1;
}
if (r == 0) {
ALOGE("eof\n");
return -1; /* EOF */
}
n += r;
}
return 0;
}
static int writex(int s, const void *_buf, int count)
{
const char *buf = _buf;
int n = 0, r;
if (count < 0) return -1;
while (n < count) {
r = write(s, buf + n, count - n);
if (r < 0) {
if (errno == EINTR) continue;
ALOGE("write error: %s\n", strerror(errno));
return -1;
}
n += r;
}
return 0;
}
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s, count;
ALOGI("socketserver firing up\n");
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
exit(1);
}
if (listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
for (;;) {
alen = sizeof(addr);
s = accept(lsocket, &addr, &alen);
if (s < 0) {
ALOGE("Accept failed: %s\n", strerror(errno));
continue;
}
fcntl(s, F_SETFD, FD_CLOEXEC);
ALOGI("new connection\n");
for (;;) {
unsigned short count;
if (readx(s, &count, sizeof(count))) {
ALOGE("failed to read size\n");
break;
}
if ((count < 1) || (count >= BUFFER_MAX)) {
ALOGE("invalid size %d\n", count);
break;
}
if (readx(s, buf, count)) {
ALOGE("failed to read command\n");
break;
}
buf[count] = 0;
ALOGI("buf = %s count = %d\n", buf, count);
if (writex(s, &count, sizeof(count))) return -1;
if (writex(s, buf, count)) return -1;
}
ALOGI("closing connection\n");
close(s);
}
return 0;
}
3. SocketClient的实现,利用LocalSocket进行通信,服务器socket地址是具有相同的字符串mysocket的address对象,LocalSocketAddress("mysocket",LocalSocketAddress.Namespace.RESERVED),首先通过connect方法进行连接,然后从EditText获取字符串,给server端发送两次数据,第一次是字符串长度,第二次是字符串,最后从server端读取返回的字符串,显示在TextView控件上。
private boolean connect() {
if (mSocket != null) {
return true;
}
Log.i(TAG, "connecting...");
try {
mSocket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress("mysocket",
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
return false;
}
return true;
}
private void disconnect() {
Log.i(TAG, "disconnecting...");
try {
if (mSocket != null)
mSocket.close();
} catch (IOException ex) {
}
try {
if (mIn != null)
mIn.close();
} catch (IOException ex) {
}
try {
if (mOut != null)
mOut.close();
} catch (IOException ex) {
}
mSocket = null;
mIn = null;
mOut = null;
}
private boolean readBytes(byte buffer[], int len) {
int off = 0, count;
if (len < 0)
return false;
while (off != len) {
try {
count = mIn.read(buffer, off, len - off);
if (count <= 0) {
Log.e(TAG, "read error " + count);
break;
}
off += count;
} catch (IOException ex) {
Log.e(TAG, "read exception");
break;
}
}
Log.i(TAG, "read " + len + " bytes");
if (off == len)
return true;
disconnect();
return false;
}
private boolean readReply() {
int len;
buflen = 0;
if (!readBytes(buf, 2))
return false;
len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
if ((len < 1) || (len > 1024)) {
Log.e(TAG, "invalid reply length (" + len + ")");
disconnect();
return false;
}
if (!readBytes(buf, len))
return false;
buflen = len;
return true;
}
private boolean writeCommand(String _cmd) {
byte[] cmd = _cmd.getBytes();
int len = cmd.length;
if ((len < 1) || (len > 1024))
return false;
buf[0] = (byte) (len & 0xff);
buf[1] = (byte) ((len >> 8) & 0xff);
try {
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
} catch (IOException ex) {
Log.e(TAG, "write error");
disconnect();
return false;
}
return true;
}
private String doTransaction(String cmd){
if (!connect()) {
Log.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
Log.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
Log.i(TAG, "send: '" + cmd + "'");
if (readReply()) {
String s = new String(buf, 0, buflen);
tv.setText(s);
Log.i(TAG, "recv: '" + s + "'");
return s;
} else {
Log.i(TAG, "fail");
return "-1";
}
}
最后,重新烧录kernel,将socketserver推送到/system/bin/下面,可以看到,在/dev/socket/下生成了一个mysocket的socket文件
安装运行SocketClient,输入字符串,点击send,client端能正常获取server端返回的字符串,通信成功。
总结:理解本地的socket通信,有助于我们阅读源码,Android中很多service都是通过socket通信间接完成功能的实现,把握好socket通信的调用流程,即可抽丝剥茧,理清实际的业务逻辑。
点击下载本文代码