import kotlinx.coroutines.*
import android.net.LocalSocket
import android.net.LocalServerSocket
import android.net.LocalSocketAddress
import java.io.IOException
private val localSocketName = "com.example.testLocalSocket"
private suspend fun localServerListen() {
val localServerSocket = LocalServerSocket(localSocketName)
while(true) {
try {
println("server listen...")
val localSocket = localServerSocket.accept()// will block to listen
println("server accept $localSocket")
val array32 = ByteArray(32)
val len = localSocket.getInputStream().read(array32)// will block to read
println("server receive:")
for (x in 0 until len) print("${array32.get(x)},")
println("\nserver finish: $len")
localSocket.close()
} catch (e: IOException) {
e.printStackTrace();
break;
}
}
localServerSocket.close();
}
private suspend fun testLocalSocket(start:Int = 100) {
try {
val localSocket = LocalSocket()
val endpointServer = LocalSocketAddress(
localSocketName,
LocalSocketAddress.Namespace.ABSTRACT
)
println("client connect ...")
localSocket.connect(endpointServer)
println("client send: $localSocket")
val len = 32
val array32 = ByteArray(len)
for (x in 0 until len) array32.set(x, (start + x).toByte())
localSocket.getOutputStream().write(array32, 0, len)
localSocket.close()
} catch (e: IOException) {
e.printStackTrace();
}
}
在主程序用 GlobalScope.launch 同時發動三條 coroutines 一起執行:
GlobalScope.launch(Dispatchers.IO) {
launch { localServerListen() }
launch { testLocalSocket(90) }
launch { testLocalSocket(80) }
}
後記: 用 c 寫 LocalSocket 通信程式, 使用 Unix Socket_Stream,並採用"虛根"的命名空間 (abstract namespace):
1. // localSocket.h used in client and server
char localSocketName[ ] = "com.example.testLocalSocket";// strlen < 106
2. // localSocket.c for client
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
#include "localSocket.h"
int main() {
int localSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (localSocket < 0) exit(1);
struct sockaddr_un endpointServer; // socket for loopback addr
memset(&endpointServer, 0, sizeof(endpointServer));// init
endpointServer.sun_family = AF_UNIX;
int sunOffset = offsetof(struct sockaddr_un, sun_path);
endpointServer.sun_path[0] = '\0';
strcpy(endpointServer.sun_path + 1, localSocketName);
socklen_t len = 1 + strlen(localSocketName) + sunOffset;
if(connect(localSocket, (struct sockaddr *)&endpointServer, len) >= 0) {
char msg[] = "Hello";
send(localSocket, msg, strlen(msg), 0);
}
close(localSocket);
}
3. // localServerSocket.c for server
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
#include "localSocket.h"
int main() {
int localSocket = socket(AF_UNIX, SOCK_STREAM, 0);
if (localSocket < 0) exit(1);
struct sockaddr_un endpointServer; // socket for loopback addr
memset(&endpointServer, 0, sizeof(endpointServer));// init
endpointServer.sun_family = AF_UNIX;
int sunOffset = offsetof(struct sockaddr_un, sun_path);
endpointServer.sun_path[0] = '\0';
strcpy(endpointServer.sun_path + 1, localSocketName);
socklen_t len = 1 + strlen(localSocketName) + sunOffset;
if (bind(localSocket, (struct sockaddr *)&endpointServer, len) >= 0 && listen(localSocket, 5) >= 0) {
int socketfd = accept(localSocket, (struct sockaddr *)&endpointServer, &len);
if(socketfd >= 0) {
char buf[1024];
int n = read(socketfd, buf, sizeof(buf));
buf[n] = 0;
printf("server receive: %s\n", buf);
close(socketfd);
}
}
close(localSocket);
}
編譯上述兩個程式並執行看看:
g++ localServerSocket.c -o server && ./server &
g++ localSocket.c -o client && ./client
理解"虛根"(abstract root)的用意:上述 endpointServer.sun_path[0] = '\0'一開頭處填入 0 (零, 也是字串結尾 End Of String), 會讓 bind localSoket 時,
不用在檔案系統內產生檔案,否則執行的程式將在檔案系統目錄內建構出一個 domain name(長度為 0
的檔案 com.example.testLocalSocket),導致不同目錄下執行的程式將有不同的 domain name(整個 domain name 包含前置目錄,除非在同根的目錄用相同名稱,才會有相同 domain name), 執行 domain name 各異的 LocalSocket 程式,除非在命名上取巧,基本上是無法相互溝通的.LocalSocket 採用"虛根"是一個很聰明的方式,可想像 0 就是一個虛根,當執行的程式在虛根上長出 domain name,也就不需在檔案系統下留下任何軌跡,LocalSocket 通信程式只要在虛根上採用了相同名稱,自然就能相互通訊,解決了 domain name 命名的麻煩.
沒有留言:
張貼留言