欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-02 10:30:00
      Android和C/C++通过原生socket实现通信  本篇是Android和C/C++通信系列的第一篇,先从最简单Socket通信来作为开篇。聊到Android和C/C++之间的通信读者也许可以列举很多,为什么要从最简单的原生socket通信为例来最开始说明,这个是因为不可能一开始就上最难的,得循序渐进一步步进行,所以先从最简单的原生Socket开始是最好不过的了。在这里就不对S......

  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 Framework层和Native层通过原生socket实现通信



总结

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

本文地址:https://blog.csdn.net/tkwxty/article/details/103064321