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域套接字结构*/ struct sockaddr_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 ",strerror(errno)); return; } /*配置服务器信息(socket对象路径)*/ // Check with length +1 for the *initial* ' '. if ((strlen(sName) + 1) > sizeof(server_address.sun_path)) { ALOGE("name too long "); goto EXIT; } /* * Note: The path in this case is *not* supposed to be * ' '-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 "); goto EXIT; } /*配置服务器信息(服务器地址长度)*/ 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 ",strerror(errno)); goto EXIT; }
/*接受客户端请求; 第2个参数用来存储客户端地址; 第3个参数用来存储客户端地址的大小*/
/*建立(返回)一个到客户端的文件描述符,用以对客户端的读写操作*/
client_sockfd = accept (server_sockfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len);
if (client_sockfd == -1) {
ALOGE("accept error : %s
",strerror(errno));
goto EXIT;
}
else if (client_sockfd> FD_SETSIZE){
ALOGE("accept reach max
");
close(client_sockfd);
goto EXIT;
}
/*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
",strerror(errno));
close(client_socketfd);
return;
}
}
EXIT:
close(server_sockfd);
HAL层可具体参考Android源码的 systemtosisrcsocket_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代码:
package com.example.administrator.localsocket; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Bundle; import android.os.RemoteException; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; import java.io.IOException; import static android.widget.Toast.LENGTH_SHORT; public class MainActivity extends AppCompatActivity { private static final String SOCKET_ADDRESS = "nano_server_socket"; //和HAL层统一地址名称 LocalSocket client_socket = null; LocalSocketAddress addr; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); client_socket = new LocalSocket(); //创建socket对象 addr = new LocalSocketAddress(SOCKET_ADDRESS,LocalSocketAddress.Namespace.ABSTRACT); //使用虚空间地址 try { client_socket.connect(addr); //请求连接 } catch (IOException e) { e.printStackTrace(); } findViewById(R.id.sendMsg).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //发送数据 String data="hello"; client_socket.getOutputStream().write(data.getBytes()); } catch (IOException e) { e.printStackTrace(); } } }); }
@Override protected void onStop() { super.onStop(); try { client_socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
注:Android8.0版本验证app端要用LocalSocket,需要两个条件:
1.apk需要签名成platform_app; (方法可参考: https://www.cnblogs.com/blogs-of-lxl/p/9233285.html )
2.需要给platform_app增加selinux权限,修改xxxsepolicyplatform_app.te,添加如下两条规则:
typeattribute platform_app mlstrustedsubject;
allow platform_app audioserver:unix_stream_socket connectto;
测试结果: