LocalSocket其通信方式与Socket差不多,只是LocalSocket没有跨越网络边界。对于*nix系统来说,“一切皆为文件”,Socket也不例外,Socket按照收发双方的媒介来说有三种类型:
1,通过网络端口:即通过本地回环接口(即LoopBack)127.0.0.1来收发数据;
2,通过文件系统: 通过文件作为收发数据的中转站;
3,通过内存映射文件:在内存中开辟一块区域作为收发数据的中转站,此区域仍然使用文件读写API进行访问;
LocalSocket支持方式2和方式3。
以下通过HAL层(c)作为server,App端(java)作为client,进行LocalSocket通信演示(核心部分代码):
C代码:
J_U8* sName="nano_server_socket";
J_Int server_sockfd, client_sockfd;
J_Int server_len, client_len;
J_Int reuse= 1;
J_Int err;struct sockaddr_un server_address; /*声明一个UNIX域套接字结构*/
structsockaddr_un client_address;
unlink (sName);/*删除原有server_socket对象*/
/*创建 socket, 通信协议为AF_LOCAL, SOCK_STREAM 数据方式*/server_sockfd= socket(AF_LOCAL, SOCK_STREAM, 0);if(server_sockfd < 0){
ALOGE("server_sockfd error : %s\n",strerror(errno));return;
}/*配置服务器信息(socket对象路径)*/
//Check with length +1 for the *initial* '\0'.
if ((strlen(sName) + 1) > sizeof(server_address.sun_path)) {
ALOGE("name too long\n");gotoEXIT;
}/** Note: The path in this case is *not* supposed to be
* '\0'-terminated. ("man 7 unix" for the gory details.)*/server_address.sun_path[0] = 0;
memcpy(server_address.sun_path+ 1, sName, strlen(sName));/*配置服务器信息(通信协议)*/server_address.sun_family=AF_LOCAL;/*默认设置resue FLAG*/
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
ALOGE("reuse error\n");gotoEXIT;
}/*配置服务器信息(服务器地址长度)*/client_len= server_len = strlen(sName) + offsetof(struct sockaddr_un, sun_path) + 1;/*绑定 socket 对象*/bind (server_sockfd, (struct sockaddr *)&server_address, server_len);/*监听网络,队列数为1*/
if(listen(server_sockfd,1)<0){
ALOGE("listen error : %s\n",strerror(errno));gotoEXIT;
}
/*接受客户端请求; 第2个参数用来存储客户端地址; 第3个参数用来存储客户端地址的大小*/
/*建立(返回)一个到客户端的文件描述符,用以对客户端的读写操作*/client_sockfd =accept (server_sockfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len);
if (client_sockfd == -1) {
ALOGE("accept error : %s\n",strerror(errno)); gotoEXIT; } else if (client_sockfd>FD_SETSIZE){ ALOGE("accept reach max\n"); close(client_sockfd); gotoEXIT; }
/*start read loop*/
while(1) {
char buf[1024]={0};
ssize_t res=read(client_socketfd,buf,sizeof(buf));
ALOGD("server get data : %s", buf);
if(res <=0){
if(errno == EAGAIN)
continue;
ALOGE("errno %s\n",strerror(errno));
close(client_socketfd);
return;
}
}
EXIT:
close(server_sockfd);
HAL层可具体参考Android源码的system\bt\osi\src\socket_utils\ 目录下的 socket_local_client.cc 和 socket_local_server.cc 代码(Android 8.0),
直接调用如下封装好的接口即可:
/***********/
/***服务端***/
/**********/
/*(1)创建server socket接收client端数据*/server_sockfd= osi_socket_local_server("server_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT,SOCK_STREAM);/*(2)等待client端连接*/client_sockfd= accept (server_sockfd, NULL, NULL);/*(3)数据读取线程*/
while(1){
ssize_t res=read(client_sockfd ,buf,sizeof(buf));
}/***********/
/***客户端***/
/**********/
/*(1)创建client socket*/client_sockfd= osi_socket_local_client("client_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);/*(2)发送数据到server端*/write(client_sockfd,"hello", strlen("hello"));
JAVA代码:
packagecom.example.administrator.localsocket;importandroid.net.LocalServerSocket;importandroid.net.LocalSocket;importandroid.net.LocalSocketAddress;importandroid.os.Bundle;importandroid.os.RemoteException;importandroid.support.design.widget.FloatingActionButton;importandroid.support.design.widget.Snackbar;importandroid.support.v7.app.AppCompatActivity;importandroid.support.v7.widget.Toolbar;importandroid.util.Log;importandroid.view.View;importandroid.view.Menu;importandroid.view.MenuItem;importandroid.widget.Toast;importjava.io.IOException;import staticandroid.widget.Toast.LENGTH_SHORT;public class MainActivity extendsAppCompatActivity {private static final String SOCKET_ADDRESS = "nano_server_socket"; //和HAL层统一地址名称LocalSocket client_socket= null;
LocalSocketAddress addr;
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
client_socket = newLocalSocket(); //创建socket对象
addr= newLocalSocketAddress(SOCKET_ADDRESS,LocalSocketAddress.Namespace.ABSTRACT); //使用虚空间地址try{
client_socket.connect(addr); //请求连接
}catch(IOException e) {
e.printStackTrace();
}findViewById(R.id.sendMsg).setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(View v) {try{//发送数据
String data="hello";
client_socket.getOutputStream().write(data.getBytes());
}catch(IOException e) {
e.printStackTrace();
}
}
});}
@Overrideprotected voidonStop() {super.onStop();try{
client_socket.close();
}catch(IOException e) {
e.printStackTrace();
}
}}
注:Android8.0版本验证app端要用LocalSocket,需要两个条件:
2.需要给platform_app增加selinux权限,修改xxx\sepolicy\platform_app.te,添加如下两条规则:
typeattribute platform_app mlstrustedsubject;
allow platform_app audioserver:unix_stream_socket connectto;
测试结果: