2017-5-10
需求:
arm与stm32在android平台中通信,串口由stm操作,由arm发送指令给stm,并接受stm的数据。
为什么要使用UnixDomainSocket:
Unix Socket是一种Socket方式实现进程间通信(IPC)的功能,与普通的网络socket相比,不需要进行复杂的数据打包拆包,校验和计算验证,不需要走网络协议栈,而且安全可靠(官方说明)。
方法:
socket的其中一种AF_UNIX或AF_LOCAL的类型,成为unix domain socket,是为了进行本地通信,也就是为了实现IPC,所以构造函数不需要IP和端口,取而代之的是文件路径。
c端
http://man7.org/linux/man-pages/man7/unix.7.html
socket的构造函数如下:
int socket(int domain, int type, int protocol);
而unix socket的构造
#include <sys/socket.h>
#include <sys/un.h>
unix_socket = socket(AF_UNIX, type, 0);
error = socketpair(AF_UNIX, type, 0, int *sv);
抄一段使用unix socket client的c代码
#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
/* Create a client endpoint and connect to a server. Returns fd if all OK, <0 on error. */
int unix_socket_conn(const char *servername)
{
int fd;
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) /* create a UNIX domain stream socket */
{
return(-1);
}
int len, rval;
struct sockaddr_un un;
memset(&un, 0, sizeof(un)); /* fill socket address structure with our address */
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "scktmp%05d", getpid());
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
unlink(un.sun_path); /* in case it already exists */
if (bind(fd, (struct sockaddr *)&un, len) < 0)
{
rval= -2;
}
else
{
/* fill socket address structure with server's address */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, servername);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);
if (connect(fd, (struct sockaddr *)&un, len) < 0)
{
rval= -4;
}
else
{
return (fd);
}
}
int err;
err = errno;
close(fd);
errno = err;
return rval;
}
void unix_socket_close(int fd)
{
close(fd);
}
int main(void)
{
srand((int)time(0));
int connfd;
connfd = unix_socket_conn("foo.sock");
if(connfd<0)
{
printf("Error[%d] when connecting...",errno);
return 0;
}
printf("Begin to recv/send...\n");
int i,n,size;
char rvbuf[4096];
for(i=0;i<10;i++)
{
/*
//=========接收=====================
size = recv(connfd, rvbuf, 800, 0); //MSG_DONTWAIT
if(size>=0)
{
printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
}
if(size==-1)
{
printf("Error[%d] when recieving Data.\n",errno);
break;
}
if(size < 800) break;
*/
//=========发送======================
memset(rvbuf,'a',2048);
rvbuf[2047]='b';
size = send(connfd, rvbuf, 2048, 0);
if(size>=0)
{
printf("Data[%d] Sended:%c.\n",size,rvbuf[0]);
}
if(size==-1)
{
printf("Error[%d] when Sending Data:%s.\n",errno,strerror(errno));
break;
}
sleep(1);
}
unix_socket_close(connfd);
printf("Client exited.\n");
}
要运行在Android平台上的话,要生成可执行文件,下了cygwin和mingw都没搞定。查了查发现ndk完全可以解决:就只需要在Android.mk最后的BUILD模式改成BUILD_EXECUTABLE就好,生成so文件的话模式是BUILD_SHARED_LIBRARY。
touch一个foo.sock文件,然后先运行Server端,再运行Client则可以发送成功!
java端
https://github.com/mcfunley/juds
https://github.com/jnr/jnr-unixsocket
https://github.com/kohlschutter/junixsocket
java需要使用第三方库才能使用unix socket,这里我们使用的是第一个mcfunley/juds,因为它star竟高出第二名15个!!!嗯。
unix socket分两种类型,一种类似TCP,叫做STREAM类型,一种类似UDP,叫做DGRAM类型。按自己的理解调用一下:
try {
boolean result = new File("/mnt/sdcard/uds").createNewFile();
Log.d(TAG, "create file result=" + result);
final UnixDomainSocketClient streamClient = new UnixDomainSocketClient("/mnt/sdcard/uds", JUDS.SOCK_STREAM);
final UnixDomainSocketServer streamServer = new UnixDomainSocketServer("/mnt/sdcard/uds", JUDS.SOCK_STREAM);
new Thread(){
@Override
public void run() {
byte[] buf = new byte[128];
try {
int count = streamServer.getInputStream().read(buf);
Log.d(TAG, "size of data=" + count);
for(byte b : buf){
Log.d(TAG, "byte=" + b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
new Thread(){
@Override
public void run() {
try {
streamClient.getOutputStream().write("Hello stm".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
2017-5-19
java端与c端的通信报出java.io.IOException: Unable to write to Unix domain socket
问题有待解决。