Android Java层和Native层通信实战大荟萃之原生socket实现通信
Android Java层和Naitve层通信之实战大荟萃目录:
Android Java层和Native层通信实战大荟萃之MessageQueue实现通信
Android Java层和Native层通信实战大荟萃之JNI实现通信
Android Java层和Native层通信实战大荟萃之LocalSocket实现通信
Android Java层和Native层通信实战大荟萃之原生socket实现通信
引言
本篇是Android Java层和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++的代码库的。当然这个可可以扩展开来,在不同的机器上部署从而通过局域网或者广域网来进行通信。前面的代码只是简单的演示程序,具体的业务逻辑可以根据实际情况进行扩展,这篇文章只是一个抛砖引玉的作用。