NDK Socket编程:面向连接的通信(tcp)
生活随笔
收集整理的這篇文章主要介紹了
NDK Socket编程:面向连接的通信(tcp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
使用posix socket api,java層調用c層。
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.apress.echo"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="19" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><!-- 服務端app --> <!-- <activity --> <!-- android:name=".EchoServerActivity" --> <!-- android:label="@string/title_activity_echo_server" --> <!-- android:launchMode="singleTop" > --> <!-- <intent-filter> --> <!-- <action android:name="android.intent.action.MAIN" /> --><!-- <category android:name="android.intent.category.LAUNCHER" /> --> <!-- </intent-filter> --> <!-- </activity> --><!-- 客戶端app --><activityandroid:name=".EchoClientActivity"android:label="@string/title_activity_echo_client"android:launchMode="singleTop" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.INTERNET" /></manifest>AbstractEchoActivity:
package com.apress.echo;import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ScrollView; import android.widget.TextView;/*** 客戶端和服務端的抽象父類 共同有一個啟動按鈕,顯示日志的TextView,端口設置EditText* */ public abstract class AbstractEchoActivity extends Activity implementsOnClickListener {protected static final int TCP = 1;protected static final int UDP = 2;protected EditText editPort;// Port numberprotected Button btnStart;// server buttonprotected ScrollView scrollLog;//protected TextView tvLog;// log viewprivate final int layoutID;public AbstractEchoActivity(int layoutID) {this.layoutID = layoutID;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(layoutID);editPort = (EditText) findViewById(R.id.port_edit);btnStart = (Button) findViewById(R.id.start_button);scrollLog = (ScrollView) findViewById(R.id.scroll_view);tvLog = (TextView) findViewById(R.id.log_view);btnStart.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v == btnStart) {onStartButtonClicked();} else {Log.v("onClick", "onClick no done.");}}/*** 獲取端口* * @return*/protected Integer getPort() {Integer port;try {port = Integer.valueOf(editPort.getText().toString());} catch (Exception e) {e.printStackTrace();port = null;}return port;}protected void logMessage(final String message) {runOnUiThread(new Runnable() {@Overridepublic void run() {logMessageDirect(message);}});}protected void logMessageDirect(final String message) {tvLog.append(message);tvLog.append("\n");scrollLog.fullScroll(View.FOCUS_DOWN);}protected abstract void onStartButtonClicked();/*** 這個thread抽象出onBackground()方法作為線程的執行方法,在啟動前先設置控件狀態為不可用,同時清空日志。執行完畢后設置控件可用。* */protected abstract class AbstractEchoTask extends Thread {private final Handler handler;public AbstractEchoTask() {handler = new Handler();}protected void onPreExecute() {btnStart.setEnabled(false);// 清空日志tvLog.setText("");}/* * */@Overridepublic synchronized void start() {// 這里start是由主線程來調用的。調用之前先設置控件狀態。onPreExecute();super.start();}@Overridepublic void run() {// run是在新線程中運行的onBackground();// 用handler來修改控件handler.post(new Runnable() {@Overridepublic void run() {onPostExecute();}});}/*** 線程的執行體*/protected abstract void onBackground();/*** */protected void onPostExecute() {btnStart.setEnabled(true);}}static {System.loadLibrary("Echo");}}客戶端app
EchoClientActivity:
服務端app
EchoServerActivity:
package com.apress.echo;public class EchoServerActivity extends AbstractEchoActivity {public EchoServerActivity() {super(R.layout.activity_echo_server);}@Overrideprotected void onStartButtonClicked() {Integer port = getPort();if (port != null) {new ServerTask(port, TCP).start();} else {logMessage("port error");}}/*** 啟動tcp服務* * @param port* @throws Exception*/private native void nativeStartTcpServer(int port) throws Exception;/*** 啟動udp服務* * @param port* @throws Exception*/private native void nativeStartUdpServer(int port) throws Exception;private class ServerTask extends AbstractEchoTask {private final int port;private final int protocol;/*** @param port端口* @param protocol* 使用的協議*/public ServerTask(int port, int protocol) {this.port = port;this.protocol = protocol;}@Overrideprotected void onBackground() {logMessage("Starting server.");logMessage("server ip:" + Commons.getIpAddress());try {if (protocol == TCP) {nativeStartTcpServer(port);} else if (protocol == UDP) {nativeStartUdpServer(port);} else {logMessage("protocol error.");}} catch (Exception e) {logMessage(e.getMessage());}logMessage("Server terminated.");}} }ndk代碼
com_apress_echo_EchoClientActivity.h:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_apress_echo_EchoClientActivity */#ifndef _Included_com_apress_echo_EchoClientActivity #define _Included_com_apress_echo_EchoClientActivity #ifdef __cplusplus extern "C" { #endif /** Class: com_apress_echo_EchoClientActivity* Method: nativeStartTcpClient* Signature: (Ljava/lang/String;ILjava/lang/String;)V*/ JNIEXPORT void JNICALL Java_com_apress_echo_EchoClientActivity_nativeStartTcpClient(JNIEnv *, jobject, jstring, jint, jstring);#ifdef __cplusplus } #endif #endifcom_apress_echo_EchoServerActivity.h:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_apress_echo_EchoServerActivity */#ifndef _Included_com_apress_echo_EchoServerActivity #define _Included_com_apress_echo_EchoServerActivity #ifdef __cplusplus extern "C" { #endif #undef com_apress_echo_EchoServerActivity_TCP #define com_apress_echo_EchoServerActivity_TCP 1L #undef com_apress_echo_EchoServerActivity_UDP #define com_apress_echo_EchoServerActivity_UDP 2L /** Class: com_apress_echo_EchoServerActivity* Method: nativeStartTcpServer* Signature: (I)V*/ JNIEXPORT void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartTcpServer(JNIEnv *, jobject, jint);/** Class: com_apress_echo_EchoServerActivity* Method: nativeStartUdpServer* Signature: (I)V*/ JNIEXPORT void JNICALL Java_com_apress_echo_EchoServerActivity_nativeStartUdpServer(JNIEnv *, jobject, jint);#ifdef __cplusplus } #endif #endif實現socket函數的頭文件
SocketUtils.h:
#include <stdio.h> #include <stdarg.h> //errno #include <errno.h> #include <string.h>#include <sys/types.h> #include <sys/socket.h>//sockaddr_un #include <sys/un.h>//htons,sockaddr_in #include <netinet/in.h> //inet_ntop #include <arpa/inet.h> //close,unlink #include <unistd.h> //offsetof #include <stddef.h>#ifndef __SOCKET_UTILS__ #define __SOCKET_UTILS_//MAX log message length #define MAX_LOG_MESSAGE_LENGTH 256 //MAX data buffer size #define MAX_BUFFER_SIZE 80//打印日志到java環境中 static void LogMessage(JNIEnv* env, jobject obj, const char* format, ...) {//cache log method IDstatic jmethodID methodID = NULL;if (methodID == NULL) {jclass clazz = env->GetObjectClass(obj);methodID = env->GetMethodID(clazz, "logMessage","(Ljava/lang/String;)V");env->DeleteLocalRef(clazz);}if (methodID != NULL) {char buffer[MAX_BUFFER_SIZE];//將可變參數輸出到字符數組中va_list ap;va_start(ap, format);vsnprintf(buffer, MAX_LOG_MESSAGE_LENGTH, format, ap);va_end(ap);//轉換成java字符串jstring message = env->NewStringUTF(buffer);if (message != NULL) {env->CallVoidMethod(obj, methodID, message);env->DeleteLocalRef(message);}} }//通過異常類和異常信息拋出異常 static void ThrowException(JNIEnv* env, const char* className,const char* message) {jclass clazz = env->FindClass(className);if (clazz != NULL) {env->ThrowNew(clazz, message);env->DeleteLocalRef(clazz);} }//通過異常類和錯誤號拋出異常 static void ThrowErrnoException(JNIEnv* env, const char* className,int errnum) {char buffer[MAX_LOG_MESSAGE_LENGTH];//通過錯誤號獲得錯誤消息if (-1 == strerror_r(errnum, buffer, MAX_LOG_MESSAGE_LENGTH)) {strerror_r(errno, buffer, MAX_LOG_MESSAGE_LENGTH);}ThrowException(env, className, buffer); }//sock用到的一些公用方法 //創建一個socket:socket() static int NewTcpSocket(JNIEnv* env, jobject obj) {LogMessage(env, obj, "Constructing a new TCP socket...");int tcpSocket = socket(PF_INET, SOCK_STREAM, 0);if (-1 == tcpSocket) {ThrowErrnoException(env, "java/io/IOException", errno);}return tcpSocket; }//綁定 bind() static void BindSocketToPort(JNIEnv* env, jobject obj, int sd,unsigned short port) {struct sockaddr_in address;//清空結構體memset(&address, 0, sizeof(address));address.sin_family = PF_INET;//Bind to all addressaddress.sin_addr.s_addr = htonl(INADDR_ANY);//Convert port to network byte orderaddress.sin_port = htons(port);//Bind socketLogMessage(env, obj, "Binding to port %hu.", port);//sockaddr方便函數傳遞, sockaddr_in方便用戶設定, 所以需要的時候在這2者之間進行轉換if (-1 == bind(sd, (struct sockaddr*) &address, sizeof(address))) {ThrowErrnoException(env, "java/io/IOException", errno);}} //返回當前socket綁定的端口 static unsigned short GetSocketPort(JNIEnv* env, jobject obj, int sd) {unsigned short port = 0;struct sockaddr_in address;socklen_t addressLength = sizeof(address);if (-1 == getsockname(sd, (struct sockaddr*) &address, &addressLength)) {ThrowErrnoException(env, "java/io/IOException", errno);} else {port = ntohs(address.sin_port);LogMessage(env, obj, "Binding to the random port %hu.", port);}return port; }//監聽 listen() static void ListenOnSocket(JNIEnv*env, jobject obj, int sd, int backlog) {LogMessage(env, obj,"Listening on socket with a baklog of %d pending connections.",backlog);//listen()用來等待參數s 的socket 連線. 參數backlog 指定同時能處理的最大連接要求,//如果連接數目達此上限則client 端將收到ECONNREFUSED 的錯誤.//Listen()并未開始接收連線, 只是設置socket 為listen 模式, 真正接收client 端連線的是accept().//通常listen()會在socket(), bind()之后調用, 接著才調用accept().if (-1 == listen(sd, backlog)) {ThrowErrnoException(env, "java/io/IOException", errno);}}//根據地址打印IP和端口 static void LogAddress(JNIEnv* env, jobject obj, const char* message,const struct sockaddr_in* address) {char ip[INET_ADDRSTRLEN];if (NULL == inet_ntop(PF_INET, &(address->sin_addr), ip, INET_ADDRSTRLEN)) {ThrowErrnoException(env, "java/io/IOException", errno);} else {unsigned short port = ntohs(address->sin_port);LogMessage(env, obj, "%s %s:%hu", message, ip, port);} }//accept() static int AcceptOnSocket(JNIEnv* env, jobject obj, int sd) {struct sockaddr_in address;socklen_t addressLength = sizeof(address);LogMessage(env, obj, "Waiting for a client connection...");int clientSocket = accept(sd, (struct sockaddr*) &address, &addressLength);if (-1 == clientSocket) {ThrowErrnoException(env, "java/io/IOException", errno);} else {LogAddress(env, obj, "Client connection from ", &address);}return clientSocket; }//接收 recv() static ssize_t ReceiveFromSocket(JNIEnv* env, jobject obj, int sd, char* buffer,size_t bufferSize) {LogMessage(env, obj, "Receiving from the socket... ");ssize_t recvSize = recv(sd, buffer, bufferSize - 1, 0);if (-1 == recvSize) {ThrowErrnoException(env, "java/io/IOException", errno);} else {//字符串截斷buffer[recvSize] = NULL;if (recvSize > 0) {//接收成功,打印LogMessage(env, obj, "Received %d bytes:%s", bufferSize, buffer);} else {LogMessage(env, obj, "Client disconnected.");}}return recvSize; }//發送消息:send() static ssize_t SendToSocket(JNIEnv *env, jobject obj, int sd,const char* buffer, size_t bufferSize) {LogMessage(env, obj, "Sending to the socket... ");ssize_t sentSize = send(sd, buffer, bufferSize, 0);if (-1 == sentSize) {ThrowErrnoException(env, "java/io/IOException", errno);} else {if (sentSize > 0) {LogMessage(env, obj, "Send %d bytes: %s", sentSize, buffer);} else {LogMessage(env, obj, "Client disconnected.");}}return sentSize; }//鏈接到服務器 connect() static void ConnectToAddress(JNIEnv*env, jobject obj, int sd, const char*ip,unsigned short port) {LogMessage(env, obj, "Connecting to %s:%hu...", ip, port);struct sockaddr_in address;memset(&address, 0, sizeof(address));address.sin_family = PF_INET;//轉換ipif (0 == inet_aton(ip, &(address.sin_addr))) {ThrowErrnoException(env, "java/io/IOException", errno);} else {address.sin_port = htons(port);}if (-1 == connect(sd, (const sockaddr*) &address, sizeof(address))) {ThrowErrnoException(env, "java/io/IOException", errno);} else {LogMessage(env, obj, "Connected.");}}//----------------udp//創建udp socket static int NewUdpSocket(JNIEnv* env, jobject obj) {LogMessage(env, obj, "Constructing a new UDP socket...");int udpSocket = socket(PF_INET, SOCK_DGRAM, 0);if (-1 == udpSocket) {ThrowErrnoException(env, "java/io/IOException", errno);}return udpSocket; }#endif __SOCKET_UTILS_實現代碼:
Echo.cpp:
分別編譯客戶端和服務端,安裝到兩臺不同的手機上。
運行結果:
代碼下載
http://download.csdn.net/detail/hai836045106/8062933
總結
以上是生活随笔為你收集整理的NDK Socket编程:面向连接的通信(tcp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用代理下载android系统源码和SD
- 下一篇: android判断和创建快捷方式(4.0