Boh*_* Lu 11 java unix sockets java-native-interface android
在android中,有两个类LocalServerSocket和LocalSocket.我认为它们类似于unix socket中的AF_LOCAL(我不确定它是否正确).
我的问题是:是否可以在Java中创建LocalServerSocket并使用普通的unix套接字客户端在本机或其他进程中连接到它?
如果有可能,我应该在本机中设置"sockaddr_un.sun_path"是什么?
我编写了一个示例项目来测试它,我尝试将.sun_path设置为与LocalServerSocket中使用的字符串名称相同,但是它失败了,本机无法连接到Java LocalServerSocket.
我的Java代码:
package test.socket;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class TestSocketActivity extends Activity {
public static String SOCKET_ADDRESS = "my.local.socket.address";
public String TAG = "Socket_Test";
static{System.loadLibrary("testSocket");}
private native void clientSocketThreadNative();
private native void setStopThreadNative();
localServerSocket mLocalServerSocket;
localClientSocket mLocalClientSocket;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLocalServerSocket = new localServerSocket();
mLocalClientSocket = new localClientSocket();
}
/* LocalServerSocket */
public class localServerSocket extends Thread {
int bufferSize = 32;
byte[] buffer;
int bytesRead;
int totalBytesRead;
int posOffset;
LocalServerSocket server;
LocalSocket receiver;
InputStream input;
private volatile boolean stopThread;
public localServerSocket() {
Log.d(TAG, " +++ Begin of localServerSocket() +++ ");
buffer = new byte[bufferSize];
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
try {
server = new LocalServerSocket(SOCKET_ADDRESS);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "The LocalServerSocket created failed !!!");
e.printStackTrace();
}
stopThread = false;
}
public void run() {
Log.d(TAG, " +++ Begin of run() +++ ");
while (!stopThread) {
if (null == server){
Log.d(TAG, "The LocalServerSocket is NULL !!!");
stopThread = true;
break;
}
try {
Log.d(TAG, "LocalServerSocket begins to accept()");
receiver = server.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "LocalServerSocket accept() failed !!!");
e.printStackTrace();
continue;
}
try {
input = receiver.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "getInputStream() failed !!!");
e.printStackTrace();
continue;
}
Log.d(TAG, "The client connect to LocalServerSocket");
while (receiver != null) {
try {
bytesRead = input.read(buffer, posOffset,
(bufferSize - totalBytesRead));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "There is an exception when reading socket");
e.printStackTrace();
break;
}
if (bytesRead >= 0) {
Log.d(TAG, "Receive data from socket, bytesRead = "
+ bytesRead);
posOffset += bytesRead;
totalBytesRead += bytesRead;
}
if (totalBytesRead == bufferSize) {
Log.d(TAG, "The buffer is full !!!");
String str = new String(buffer);
Log.d(TAG, "The context of buffer is : " + str);
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
}
}
Log.d(TAG, "The client socket is NULL !!!");
}
Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
if (receiver != null){
try {
receiver.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (server != null){
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void setStopThread(boolean value){
stopThread = value;
Thread.currentThread().interrupt(); // TODO : Check
}
}
/* Client native socket */
public class localClientSocket extends Thread {
private volatile boolean stopThread;
public localClientSocket(){
Log.d(TAG, " +++ Begin of localClientSocket() +++ ");
stopThread = false;
}
public void run(){
Log.d(TAG, " +++ Begin of run() +++ ");
while(!stopThread){
clientSocketThreadNative();
}
}
public void setStopThread(boolean value){
stopThread = value;
setStopThreadNative();
Thread.currentThread().interrupt(); // TODO : Check
}
}
public void bt_startServerOnClick(View v) {
mLocalServerSocket.start();
}
public void bt_startClientOnClick(View v) {
mLocalClientSocket.start();
}
public void bt_stopOnClick(View v) {
mLocalClientSocket.setStopThread(true);
mLocalServerSocket.setStopThread(true);
}
}
Run Code Online (Sandbox Code Playgroud)
我的原生代码:
#define SOCKET_NAME "my.local.socket.address"
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
(JNIEnv *env, jobject object){
LOGD("In clientSocketThreadNative() : Begin");
stopThread = 1;
int sk, result;
int count = 1;
int err;
char *buffer = malloc(8);
int i;
for(i = 0; i<8; i++){
buffer[i] = (i+1);
}
/*
struct sockaddr_un addr;
bzero((char *)&addr,sizeof(addr);
addr.sun_family = AF_UNIX;
addr.sun_path = SOCKET_NAME;
*/
struct sockaddr_un addr = {
AF_UNIX, SOCKET_NAME
};
LOGD("In clientSocketThreadNative() : Before creating socket");
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
err = errno;
LOGD("%s: Cannot open socket: %s (%d)\n",
__FUNCTION__, strerror(err), err);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err = errno;
LOGD("%s: connect() failed: %s (%d)\n",
__FUNCTION__, strerror(err), err);
close(sk);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");
while(!stopThread){
result = write(sk, buffer, 8);
LOGD("In clientSocketThreadNative() : Total write = %d", result);
count++;
if(4 == count){
sleep(1);
count = 0;
}
}
LOGD("In clientSocketThreadNative() : End");
}
Run Code Online (Sandbox Code Playgroud)
任何建议将不胜感激!!!
Boh*_* Lu 12
以下代码可能不完美,但它的工作原理!谢谢迈克.
Java部分(套接字服务器):
package test.socket;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class TestSocketActivity extends Activity {
public static String SOCKET_ADDRESS = "/test/socket/localServer";
public String TAG = "Socket_Test";
static{System.loadLibrary("testSocket");}
private native void clientSocketThreadNative();
private native void setStopThreadNative();
localSocketServer mLocalSocketServer;
localSocketClient mLocalSocketClient;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLocalSocketServer = new localSocketServer();
mLocalSocketClient = new localSocketClient();
}
/* LocalSocketServer */
public class localSocketServer extends Thread {
int bufferSize = 32;
byte[] buffer;
int bytesRead;
int totalBytesRead;
int posOffset;
LocalServerSocket server;
LocalSocket receiver;
InputStream input;
private volatile boolean stopThread;
public localSocketServer() {
Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
buffer = new byte[bufferSize];
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
try {
server = new LocalServerSocket(SOCKET_ADDRESS);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "The localSocketServer created failed !!!");
e.printStackTrace();
}
LocalSocketAddress localSocketAddress;
localSocketAddress = server.getLocalSocketAddress();
String str = localSocketAddress.getName();
Log.d(TAG, "The LocalSocketAddress = " + str);
stopThread = false;
}
public void run() {
Log.d(TAG, " +++ Begin of run() +++ ");
while (!stopThread) {
if (null == server){
Log.d(TAG, "The localSocketServer is NULL !!!");
stopThread = true;
break;
}
try {
Log.d(TAG, "localSocketServer begins to accept()");
receiver = server.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "localSocketServer accept() failed !!!");
e.printStackTrace();
continue;
}
try {
input = receiver.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "getInputStream() failed !!!");
e.printStackTrace();
continue;
}
Log.d(TAG, "The client connect to LocalServerSocket");
while (receiver != null) {
try {
bytesRead = input.read(buffer, posOffset,
(bufferSize - totalBytesRead));
} catch (IOException e) {
// TODO Auto-generated catch block
Log.d(TAG, "There is an exception when reading socket");
e.printStackTrace();
break;
}
if (bytesRead >= 0) {
Log.d(TAG, "Receive data from socket, bytesRead = "
+ bytesRead);
posOffset += bytesRead;
totalBytesRead += bytesRead;
}
if (totalBytesRead == bufferSize) {
Log.d(TAG, "The buffer is full !!!");
String str = new String(buffer);
Log.d(TAG, "The context of buffer is : " + str);
bytesRead = 0;
totalBytesRead = 0;
posOffset = 0;
}
}
Log.d(TAG, "The client socket is NULL !!!");
}
Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
if (receiver != null){
try {
receiver.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (server != null){
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void setStopThread(boolean value){
stopThread = value;
Thread.currentThread().interrupt(); // TODO : Check
}
}
/* Client native socket */
public class localSocketClient extends Thread {
private volatile boolean stopThread;
public localSocketClient(){
Log.d(TAG, " +++ Begin of localSocketClient() +++ ");
stopThread = false;
}
public void run(){
Log.d(TAG, " +++ Begin of run() +++ ");
while(!stopThread){
clientSocketThreadNative();
}
}
public void setStopThread(boolean value){
stopThread = value;
setStopThreadNative();
Thread.currentThread().interrupt(); // TODO : Check
}
}
public void bt_startServerOnClick(View v) {
mLocalSocketServer.start();
}
public void bt_startClientOnClick(View v) {
mLocalSocketClient.start();
}
public void bt_stopOnClick(View v) {
mLocalSocketClient.setStopThread(true);
mLocalSocketServer.setStopThread(true);
}
}
Run Code Online (Sandbox Code Playgroud)
原生C部分(客户端)
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/un.h>
#include "test_socket_TestSocketActivity.h"
#define LOCAL_SOCKET_SERVER_NAME "/test/socket/localServer"
volatile int stopThread;
#ifndef __JNILOGGER_H_
#define __JNILOGGER_H_
#include <android/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LOG_TAG
#define LOG_TAG "NativeSocket"
#endif
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__)
#define LOGS(...) __android_log_print(ANDROID_LOG_SILENT,LOG_TAG,__VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif /* __JNILOGGER_H_ */
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
(JNIEnv *env, jobject object){
LOGD("In clientSocketThreadNative() : Begin");
stopThread = 0;
int sk, result;
int count = 1;
int err;
char *buffer = malloc(8);
int i;
for(i = 0; i<8; i++){
buffer[i] = (i+1);
}
struct sockaddr_un addr;
socklen_t len;
addr.sun_family = AF_LOCAL;
/* use abstract namespace for socket path */
addr.sun_path[0] = '\0';
strcpy(&addr.sun_path[1], LOCAL_SOCKET_SERVER_NAME );
len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&addr.sun_path[1]);
LOGD("In clientSocketThreadNative() : Before creating socket");
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
err = errno;
LOGD("%s: Cannot open socket: %s (%d)\n",
__FUNCTION__, strerror(err), err);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
if (connect(sk, (struct sockaddr *) &addr, len) < 0) {
err = errno;
LOGD("%s: connect() failed: %s (%d)\n",
__FUNCTION__, strerror(err), err);
close(sk);
errno = err;
return;
}
LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");
while(!stopThread){
result = write(sk, buffer, 8);
LOGD("In clientSocketThreadNative() : Total write = %d", result);
count++;
if(4 == count){
sleep(1);
count = 0;
}
}
LOGD("In clientSocketThreadNative() : End");
}
JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_setStopThreadNative
(JNIEnv *env, jobject object){
stopThread = 1;
}
Run Code Online (Sandbox Code Playgroud)
查看local_socket_client.c
Android 源代码,看起来他们是这样做的:
int socket_make_sockaddr_un(const char *name, int namespaceId,
struct sockaddr_un *p_addr, socklen_t *alen)
{
memset (p_addr, 0, sizeof (*p_addr));
size_t namelen;
switch (namespaceId) {
case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
namelen = strlen(name);
// Test with length +1 for the *initial* '\0'.
if ((namelen + 1) > sizeof(p_addr->sun_path)) {
goto error;
}
/*
* Note: The path in this case is *not* supposed to be
* '\0'-terminated. ("man 7 unix" for the gory details.)
*/
p_addr->sun_path[0] = 0;
memcpy(p_addr->sun_path + 1, name, namelen);
...
p_addr->sun_family = AF_LOCAL;
*alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
Run Code Online (Sandbox Code Playgroud)
看来 memset() 很重要,因为整个sun_path
都是相关的。(不过,看起来你用结构初始化覆盖了该部分。)它不是“0”加上原始名称,它是一个实际的零字节!(它的值都是二进制零,而不是 ascii '0'
)
尝试更密切地关注他们正在做的事情,包括前导“\0”字节和 AF_LOCAL 系列。
如果您更新了代码(无论是否有效),请发布!我对你的结果很感兴趣。你曾经让它发挥作用吗?
如果它不起作用,请找出它errno
是什么,然后调用perror()
将其打印到stderr
,或者调用strerror()
并记录输出。让我们知道您遇到了什么错误。
编辑
我最近在自己的一个项目中解决了这个问题。我发现关键是调用connect()
and时指定正确的长度bind()
。在我上面发布的代码中,它通过使用sun_path
结构中的偏移量加上名称的长度以及前导'\0'
字节加一来计算长度。如果指定任何其他长度,Java 代码可能无法连接到套接字。