Android Framework层和Native层通过原生socket实现通信

  Android Framework层和Native层通过原生socket实现通信

  本篇是Android Framework层和Native层系列的第一篇,先从最简单Socket通信来作为开篇。聊到Android Framework和Native之间的通信读者也许可以列举很多,为什么要从最简单的原生socket通信为例来最开始说明,这个是因为不可能一开始就上最难的,得循序渐进一步步进行,所以先从最简单的原生Socket开始是最好不过的了。在这里就不对Socket基础知识进行讲解了,直接开撸代码。



Native层C/C++服务端实现

   有了开篇的介绍,大家应该知道得开始Socket通信经典的C/S模式了,在这里我们将C/C++端实现的Native程序作为服务端,Android端作为客户端。下面直接上服务端代码,如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <android/log.h>
#include <jni.h>
#include <assert.h>

#define PORT 8888               //服务器端监听端口号,这个端口号可以自行定义
#define MAX_BUFFER 1024         //数据缓冲区最大值

#define LOGE(TAG,...) if(1) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);

#define TAG "Service"
int main()
{
    struct sockaddr_in server_addr, client_addr;
    int server_sockfd, client_sockfd;
    int size, write_size;
    char buffer[MAX_BUFFER];

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)    //创建Socket
    {
        perror("Socket Created Failed!\n");
        exit(1);
    }
    printf("Socket Create Success!\n");
	LOGE(TAG,"Socket Create Success!\n");

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);
    bzero(&(server_addr.sin_zero), 8);

    int opt = 1;
    int res = setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));    //设置地址复用
    if (res < 0)
    {
        perror("Server reuse address failed!\n");
        exit(1);
    }

    if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)  //绑定本地地址
    {
        perror("Socket Bind Failed!\n");
        exit(1);
    }
    printf("Socket Bind Success!\n");
	LOGE(TAG,"Socket Bind Success!\n");

    if (listen(server_sockfd, 5) == -1)                 //监听
    {
        perror("Listened Failed!\n");
        exit(1);
    }
    printf("Listening ....\n");
	LOGE(TAG,"Listening ....\n");

    socklen_t len = sizeof(client_addr);

    printf("waiting connection...\n");
    if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len)) == -1)  //等待客户端连接
    {
        perror("Accepted Failed!\n");
        exit(1);
    }

    printf("connection established!\n");
	LOGE(TAG,"connection established!\n");
    printf("waiting message...\n");
	LOGE(TAG,"waiting message...\n");
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));                             //清空数据缓冲区

        if ((size = read(client_sockfd, buffer, MAX_BUFFER)) == -1)    //读取客户端的数据
        {
            perror("Recv Failed!\n");
            exit(1);
        }

        if (size != 0)                                               
        {
            buffer[size] = '\0';
            printf("Recv msg from client: %s\n", buffer);
			LOGE(TAG,"Recv msg from client: %s\n", buffer);
            if ((write_size = write(client_sockfd, buffer, MAX_BUFFER)) > 0)   //把收到的数据回发给客户端
            {
                printf("Sent msg to client successfully!\n");
				LOGE(TAG,"Sent msg to client successfully!\n");
            }

        }
    }

    close(client_sockfd);   //关闭Socket
    close(server_sockfd);

    return 0;
}

有了具体的实现代码,还得有相关的编译脚本,编译脚本Android.mk的实现如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libcutils liblog  libutils libicuuc
LOCAL_LDLIBS	:= -lm -llog 

LOCAL_MODULE:= Service
LOCAL_SRC_FILES:= Server.c
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)

在NDK或者源码环境下编译,将生成的可执行文件Service推入终端执行,具体步骤如下:

λ adb ppush  E:\workspace\Android2Native\obj\local\armeabi\Service    /system/bin
2591 KB/s (42456 bytes in 0.016s)


λ adb shell
xxx:/ # cd system
xxx:/system # cd bin
xxx:/system/bin # ./Service
Socket Create Success!
Socket Bind Success!
Listening ....
waiting connection...


Android客户端的实现

   在前面的章节里面,我们Linux C/C++服务端已经愉快的跑了起来,正在等待着客户端的连接,我们的Android客户端要来连接了,具体核心代码如下:

package com.xxx.android2native;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class TcpClientManager {

    private static TcpClientManager mTcpClientConnector;
    private Socket mClient;
    private ConnectListener mListener;
    private Thread mConnectThread;
    private static final int HANDMESSAGE = 0;

    public interface ConnectListener {
        void onReceiveData(String data);
    }

    public void setOnConnectListener(ConnectListener listener) {
        this.mListener = listener;
    }

    public static TcpClientManager getInstance() {
        if (mTcpClientConnector == null)
            mTcpClientConnector = new TcpClientManager();
        return mTcpClientConnector;
    }

    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case HANDMESSAGE:
                if (mListener != null) {
                    mListener.onReceiveData(msg.getData().getString("data"));
                }
                break;
            }
        }
    };

    public void createConnect(final String mSerIP, final int mSerPort) {
        if (mConnectThread == null) {
            mConnectThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    connect(mSerIP, mSerPort);
                }
            });
            mConnectThread.start();
        }
    }

    /**
     * 与服务端进行连接
     * 
     * @throws IOException
     */
    private void connect(String mSerIP, int mSerPort) {
        if (mClient == null) {
            try {
                mClient = new Socket(mSerIP, mSerPort);
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        if (mClient.isConnected()) {
            Message message = Message.obtain();
            message.what = HANDMESSAGE;
            Bundle bundle = new Bundle();
            bundle.putString("data", "Connect Server success\n");
            message.setData(bundle);
            mHandler.sendMessage(message);
        }

    }

    /**
     * 发送数据
     * 
     * @param data
     *            需要发送的内容
     */
    public void send(String data) throws IOException {
        OutputStream outputStream = mClient.getOutputStream();
        outputStream.write(data.getBytes());

        InputStream inputStream;
        try {
            inputStream = mClient.getInputStream();
            if (inputStream == null) {
                Log.e("TAG", "inputStream error");
                return;
            }
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(buffer)) != -1) {
                String recedata = new String(buffer, 0, len);
                Message message = Message.obtain();
                message.what = HANDMESSAGE;
                Bundle bundle = new Bundle();
                bundle.putString("data", recedata);
                message.setData(bundle);
                mHandler.sendMessage(message);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 断开连接
     * 
     * @throws IOException
     */
    public void disconnect() throws IOException {
        if (mClient != null) {
            mClient.close();
            mClient = null;
        }
    }
}


效果演示

   在前面的章节里面,Android客户端和C/C++的核心代码已经提供了,下面演示一下最终的实际效果:
(1) 服务端

xxx:/system/bin # ./Service
Socket Create Success!
Socket Bind Success!
Listening ....
waiting connection...
connection established!
waiting message...
Recv msg from client: 123456
Sent msg to client successfully!

(2) Android客户端
在这里插入图片描述



总结

   如上的Android客户端和Linux C/C++的服务端我是在同一台Android终端设备上面部署的,且使用的127.0.0.1的本地IP进行的通信测试的。记得我刚参加工作的时候从事的是机顶盒的开发,当时对Jni全公司的人都不是非常的了解,但是由于一些解码的库都是使用C/C++实现的,当时就是通过这种本地网络端口进行通信从而实现调用C/C++的代码库的。当然这个可可以扩展开来,在不同的机器上部署从而通过局域网或者广域网来进行通信。前面的代码只是简单的演示程序,具体的业务逻辑可以根据实际情况进行扩展,这篇文章只是一个抛砖引玉的作用。

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/103064321