Android利用JNI通过Socket与服务器进行通信

简介: 终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~不多说,先上代码。记得点击star哦,代码地址是:SocketDemo上一篇文章写了如何通过Java层实现Socket和服务器的Socket进行通信,这一篇继续深究,写个如何通过native层实现socket和服务器进行通信。

终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~

不多说,先上代码。记得点击star哦,代码地址是:SocketDemo

上一篇文章写了如何通过Java层实现Socket和服务器的Socket进行通信,这一篇继续深究,写个如何通过native层实现socket和服务器进行通信。服务器端代码和前一篇博客 代码一致,主要看下Android端的代码。首先看下Main2Activity的代码:

Main2Activity.java

package com.zqc.socketdemo;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class Main2Activity extends Activity implements View.OnClickListener {

    private TextView tv;
    private Button bt;
    Handler handler;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tv = (TextView) findViewById(R.id.textView);
        bt = (Button) findViewById(R.id.button);
        bt.setOnClickListener(this);
        mContext = this;
        handler = new Handler(Looper.getMainLooper());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(){//网络连接需要在线程中实现
                    @Override
                    public void run() {
                        SocketJNI.connect(mContext, "10.18.73.62", 6868);//和服务器进行socket通信
                    }
                }.start();
                break;
        }
    }

}
对应的xml布局是

activity_main2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zqc.socketdemo.Main2Activity">

    <TextView
        android:text="TextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button"
        android:layout_alignStart="@+id/button"
        android:layout_marginTop="69dp"
        android:id="@+id/textView" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="11dp"
        android:id="@+id/button"
        android:layout_marginStart="27dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>
其中SocketJNI.java的代码很简单,代码如下:

package com.zqc.socketdemo;

import android.content.Context;

/**
 * Created by zhangqianchu on 2017/8/2.
 */

public class SocketJNI {
    static {
        System.loadLibrary("mysocket");
    }
    public native static void connect(Context mContext, String ip, int port);//这里定义了一个jni方法,在native层实现socket连接
}
在SocketJNI.java类中定义了一个jni方法,即提供一个Java层的调用,具体代码在native层实现。下面看看native层的代码

#include <jni.h>
#include <string.h>
#include "socketutil.h"


  void showToast(JNIEnv *env, jobject context, jstring str) {
    jclass tclss = (*env)->FindClass(env,"android/widget/Toast");  
     jmethodID mid = (*env)->GetStaticMethodID(env,tclss,"makeText","(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");  
     jobject job = (*env)->CallStaticObjectMethod(env,tclss,mid,context,str);  
     jmethodID showId = (*env)->GetMethodID(env,tclss,"show","()V");  
     (*env)->CallVoidMethod(env,job,showId,context,str);  
  }

 JNIEXPORT void JNICALL Java_com_zqc_socketdemo_SocketJNI_connect  
   (JNIEnv *env, jclass jcz, jobject context, jstring addr, jint port) {   
    jstring msg = (*env)->NewStringUTF(env,"start");
    //showToast(env,context,msg);//show的时候会出现暂时卡顿,原因不明
    LOGI("connect socket.");  
    const char* response =  connectRemote("10.18.73.62", 6868); //response就是服务端传给客户端的内容
    release();  
    LOGI("connect socket end.");  
    //msg  = (*env)->NewStringUTF(env,response);
    //showToast(env,context,msg);/
  }  
这里将socket的具体实现放到socketutil.h里面进行实现,避免代码耦合。

socketutil.h

#ifndef SOCKET_UTIL
#define SOCKET_UTIL
#include <jni.h>
#include <android/log.h>  

#define  LOG_TAG    "mysocket"  
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)  

void release();
const char* connectRemote(const char*,const int);
#endif
socketutil.c
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <android/log.h>
#include "socketutil.h"
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <stdlib.h>  
#include <android/log.h>  

#define MAXSIZE 4096  
struct sockaddr_in sock_add;  
struct sockaddr_in server_addr;  
int sockfd = 0;  
  
void release(){  
   close(sockfd);  
}  
  
const char* connectRemote(const char* addr,const int port) { 
     sockfd = socket(AF_INET, SOCK_STREAM, 0);    //ipv4,TCP数据连接  
     LOGI("step1");  
     if (sockfd < 0) {  
        return "socket error";  
     }  
    //服务器地址 
     bzero(&server_addr,sizeof(server_addr));  
     server_addr.sin_family = AF_INET;  
     server_addr.sin_port = htons(port);  
     LOGI("step2");  
     if( inet_pton(AF_INET, addr, &server_addr.sin_addr) < 0){    //设置ip地址  
       LOGI("address error");
       return "address error";  
     }  
     socklen_t server_addr_length = sizeof(server_addr);  
     int connfd = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); //连接到服务器  
     LOGI("step3");  
     if (connfd < 0) {  
        return "connect error";  
     }  
     //发送数据  
     char *msg = "i send a data to server. \n";  
     int sdf = send(sockfd, msg , strlen(msg), 0); //发送一行数据到服务器  
     LOGI("step4.");  
     char buff[4096];  
     int n = recv(sockfd, buff, MAXSIZE ,0);  //这地方应该需要循环获取数据,目前服务器只响应了一条很短的字符串。  
     buff[n] = 0;  
     LOGI("step5.");  
     return buff;  
}  
这里的socket连接使用的是最基本的Socket连接方法,首先设置协议(IPV4协议),然后设置ip地址和port端口号,之后便尝试连接服务器,调用的连接方法是sys/socket.h库中的函数,连接成功后会返回一个int值,若该值小于0则表示连接出错。
上述代码编写好之后,再编写个Android.mk,然后利用ndk-build命令将c代码编译为so库,放入到工程的libs目录下。其中Android.mk代码如下:

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION :=cpp #针对C++的支持,标记c++文件的扩展名名称
LOCAL_MODULE := mysocket
LOCAL_SRC_FILES := socketutil.c \
				   socketjni.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
为了少编译些版本,也可以加入一个Application.mk,代码如下:

Application.mk

#APP_MODULES := mysocket //so库的名称,在Android.mk里面设置了
#APP_STL:=stlport_static
APP_ABI := armeabi #编译的版本有哪些
#APP_OPIM :=debug
利用ndk-build命令进行编译,ndk-build命令会直接将so库放入到libs目录下,然后运行工程,点击里面的Button按钮,即可查看连接结果。注意,手机要和服务端在同一个局域网里面,而且在运行的时候要修改下代码里面的ip地址。

运行后输出的Log如下:


上面的step1到step5表示运行到了哪一步,方便查看具体是在哪一步出错了。

服务端输出如下:










目录
相关文章
|
3月前
|
网络协议 安全 网络安全
网络编程:基于socket的TCP/IP通信。
网络编程:基于socket的TCP/IP通信。
247 0
|
1月前
|
Ubuntu Linux Android开发
termux+anlinux+Rvnc viewer来使安卓手机(平板)变成linux服务器
本文介绍了如何在Android设备上安装Termux和AnLinux,并通过这些工具运行Ubuntu系统和桌面环境。
134 2
termux+anlinux+Rvnc viewer来使安卓手机(平板)变成linux服务器
|
1月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
1月前
|
存储 监控 NoSQL
Redis的实现二: c、c++的网络通信编程技术,让服务器处理多个client
本文讨论了在C/C++中实现服务器处理多个客户端的技术,重点介绍了事件循环和非阻塞IO的概念,以及如何在Linux上使用epoll来高效地监控和管理多个文件描述符。
28 0
|
2月前
|
Java Android开发 数据安全/隐私保护
Android中多进程通信有几种方式?需要注意哪些问题?
本文介绍了Android中的多进程通信(IPC),探讨了IPC的重要性及其实现方式,如Intent、Binder、AIDL等,并通过一个使用Binder机制的示例详细说明了其实现过程。
334 4
|
2月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【9月更文挑战第14天】网络协议是机器间交流的约定格式,确保信息准确传达。主要模型有OSI七层与TCP/IP模型,通过分层简化复杂网络环境。IP地址全局定位设备,MAC地址则在本地网络中定位。网络分层后,数据包层层封装,经由不同层次协议处理,最终通过Socket系统调用在应用层解析和响应。
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
120 0
|
3月前
|
Android开发
Android项目架构设计问题之C与B通信如何解决
Android项目架构设计问题之C与B通信如何解决
18 0
|
3月前
|
移动开发 前端开发 weex
Android项目架构设计问题之模块化后调用式通信如何解决
Android项目架构设计问题之模块化后调用式通信如何解决
17 0
|
3月前
|
网络协议
socket编程(2) -- TCP通信
socket编程(2) -- TCP通信
43 0
下一篇
无影云桌面