您的位置:首页 > 移动开发 > Android开发

android socket通讯

2014-01-07 16:20 429 查看
项目中要用到进程间通讯,服务端接收应用的请求数据,对串口进行读写操作。考虑到android的socket服务比较实用,并且可以支持多个客户端同时连接。

服务端写成一个服务,在init.rc中启动,示例代码如下:

socket_keyboard.c:

#define LOG_TAG "socket-keyboard"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <utime.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cutils/fs.h>
#include <cutils/sockets.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
#define SOCKET_PATH "spi-keyboard"
//#define ALOGE LOGE

void *thr_fn(void *arg)
{
int s = (int)arg;
char buf[1024];
int count;
char *respone = "this is from service";

ALOGE("new thread: ");
while (1) {
ALOGE("read...");
count = read(s,buf, sizeof(buf));
if (count > 0) {
buf[count] = '\0';
ALOGE("read client:%s", buf);
if (!memcmp(buf, "close", 5))
break;
}
ALOGE("write...");
count = write(s, respone, strlen(respone));
}

return NULL;
}

int main(const int argc, const char *argv[]) {
struct sockaddr addr;
socklen_t alen;
int lsocket, s;
ALOGE("socket keyboard service start");
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
exit(1);
}
if (listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}

ALOGE("socket keyboard service loop......");
for (;;) {
ALOGE("accept...");
alen = sizeof(addr);
s = accept(lsocket, &addr, &alen);
if (s < 0) {
ALOGE("Accept failed: %s\n", strerror(errno));
continue;
} else {
int err;
pthread_t ntid;
err = pthread_create(&ntid, NULL, thr_fn, (void *)s);
}
}
ALOGE("service close");

return 0;
}


客户端可以是c/c++代码,也可以是应用层代码,c代码如下:

#define LOG_TAG "socket-client"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <utime.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cutils/fs.h>
#include <cutils/sockets.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <cutils/multiuser.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/un.h>
#include <errno.h>
#include <private/android_filesystem_config.h>
#define SOCKET_PATH "socket-keyboard"

#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
int main(const int argc, const char *argv[]) {
char buf[1024] = {0};
struct sockaddr_un addr;
struct sockaddr_un *p_addr;
socklen_t alen;
int fd, s, count;
size_t namelen;
char *str = "this is from client";
char *name = "spi-keyboard";

p_addr = &addr;

memset (p_addr, 0, sizeof (*p_addr));

strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
strcat(p_addr->sun_path, name);
p_addr->sun_family = AF_LOCAL;
namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;

ALOGE("socket keyboard client start");
fd = socket(PF_LOCAL, SOCK_STREAM, 0);

if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
ALOGE("connet spi keyboard server err[%s]", strerror(errno));
exit(-1);
}

while (1) {
ALOGE("write");
write(fd, str, strlen(str) + 1);
ALOGE("read");
count = read(fd, buf, sizeof(buf));
if (count) {
ALOGE("client: %s", buf);
}
sleep(1);
}
return 0;
}


客户端的c代码是仿照android socket源代码搬写过来的,源代码路径在system/core/libcutils/socket_local_client.c中。

Android.mk:

LOCAL_PATH := $(call my-dir)
#
# Executable
#

include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
socket_keyboard.c

LOCAL_SHARED_LIBRARIES := \
libcutils

LOCAL_STATIC_LIBRARIES := \
libdiskusage
LOCAL_MODULE := spi-keyboard

LOCAL_MODULE_TAGS := optional

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
socket_client.c

LOCAL_SHARED_LIBRARIES := \
libcutils

LOCAL_STATIC_LIBRARIES := \
libdiskusage
LOCAL_MODULE := socket-client

LOCAL_MODULE_TAGS := optional

include $(BUILD_EXECUTABLE)


把编译出来的执行文件放入/system/bin/目录,接着在init.rc中加入启动服务:

service spi-keyboard /system/bin/spi-keyboard
class main
socket spi-keyboard stream 777 user user


这里权限要更改成777 和 user,否则应用程序没有权限连接该服务。系统启动后在/dev/socket/目录下发现多了一个文件:

adbd                keystore            property_service    spi-keyboard
dnsproxyd           mdns                rild                vold
installd            netd                rild-debug          zygote
这就是新的socket服务spi-keyboard。

执行./socket_client 即可连接上服务端。

应用层代码如下:

package com.example.keyboardsocketclient;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {
LocalSocket mSocket;
InputStream mIn;
OutputStream mOut;
byte buf[] = new byte[1024];
int ret = 0;
String write_str = "this is from client******************************************";
String write_str1 = "close";
ServerSocketThread mthread;
boolean isInterrupted = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mthread = new ServerSocketThread();
mthread.start();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
protected void onDestroy() {
Log.d("spi-keyboard", "onDestroy");
super.onDestroy();
mthread.interrupt();
finish();
}

void connect() throws IOException
{
//创建socket
mSocket = new LocalSocket();
//设置连接地址
LocalSocketAddress address = new LocalSocketAddress("spi-keyboard",
LocalSocketAddress.Namespace.RESERVED);
//建立连接
mSocket.connect(address);
//获取数据输入流 可以读数据
mIn = mSocket.getInputStream();
//获取数据输出流 可以写数据
mOut = mSocket.getOutputStream();
}

private class ServerSocketThread extends Thread {

public void interrupt(){
isInterrupted = true;
super.interrupt();
}
@Override
public void run() {
try {
connect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (!isInterrupted) {//不能用this.isInterrupted()来判断,因为sleep时候,中断线程,只会catch异常,接着下一次循环中isInterrupted()还是为false
try {
Thread.sleep(1000);//括号里面的5000代表5000毫秒,也就是5秒,可以该成你需要的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mOut.write(write_str.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

int count = 0;
while (count == 0) {
try {
count = mIn.available();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

byte[] b = new byte[count];

try {
ret = mIn.read(b);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

if (ret > 0) {
String str = "";
try {
str = new String(b, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e("keyboard client", "" + str);
}
}
}
}
}

运行该apk可以发现,服务端又为这个客户端创建一个线程,这样两个客户端可以同时连接,要注意LocalSocketAddress的第二个参数必须为LocalSocketAddress.Namespace.RESERVED,这个值为1,android专门为路径/dev/socket通讯而留的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: