android socket通讯

项目中要用到进程间通讯,服务端接收应用的请求数据,对串口进行读写操作。考虑到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通讯而留的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值