├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── speakin │ │ └── recorder │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── speakin │ │ │ └── recorder │ │ │ ├── RecorderApp.java │ │ │ ├── module │ │ │ └── control │ │ │ │ ├── ControlConstants.java │ │ │ │ ├── MasterControlManager.java │ │ │ │ ├── SlaveControlManager.java │ │ │ │ ├── search │ │ │ │ ├── DeviceBroadcastReceiver.java │ │ │ │ ├── DeviceBroadcastSender.java │ │ │ │ ├── DeviceReceiver.kt │ │ │ │ ├── MasterSearchManager.java │ │ │ │ └── SlaveSearchManager.java │ │ │ │ ├── socket │ │ │ │ ├── CommonUtil.java │ │ │ │ ├── SocketClient.java │ │ │ │ ├── SocketClientManager.java │ │ │ │ ├── SocketServerManager.java │ │ │ │ ├── data │ │ │ │ │ ├── MsgEntity.java │ │ │ │ │ ├── MsgQueueManager.java │ │ │ │ │ ├── TextMsgEntity.java │ │ │ │ │ └── VoiceMsgEntity.java │ │ │ │ └── handle │ │ │ │ │ ├── FileHandlerSocket.java │ │ │ │ │ ├── MsgParam.java │ │ │ │ │ ├── MsgReceiveHandler.java │ │ │ │ │ ├── MsgRequest.java │ │ │ │ │ ├── MsgSendHandler.java │ │ │ │ │ ├── RequestQueueManager.java │ │ │ │ │ └── TextHandlerSocket.java │ │ │ │ └── websocket │ │ │ │ ├── MasterWebSocketManager.java │ │ │ │ └── SlaveWebSocketManager.java │ │ │ ├── ui │ │ │ └── MainActivity.java │ │ │ └── utils │ │ │ ├── DeviceUUID.java │ │ │ ├── DevicesInfo.java │ │ │ ├── IpUtil.java │ │ │ └── NetHelper.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── speakin │ └── recorder │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #手机局域网内(包括热点)组网通讯实现 2 | 3 | 4 | 最近在做一个应用,需要多个手机一起组网,一个充当主机,其他多个手机充当从机。主机跟每个从机之间可以相互通信,发送消息和文件。这不就是个CS(client-server)模式吗,只不过server不是在云端,只是在同一个局域网的手机上罢了。 5 | 6 | 这里面有两个关键的地方,一个是如何发现同一个局域网内的手机客户端,包括自建的热点,一个是知道各个手机的Ip地址后,用什么方式来建立通信。这里说一下热点,手机开启热点之后,别的手机连上这个特点,其实也是一个小型局域网,只不过这个局域网稍微有点不一样(Ip地址),其他的都一样。 7 | 8 | 局域网发现 9 | 局域网发现主要难点在于如何得到对方Ip地址。我们的做法是发送UDP包。UDP协议是无面向连接的,也就是不需要像TCP协议那样提前建立连接。所以我们向局域网内某一个特定的端口广播UDP包,接收到这个包的人,发送回包,那么我们就知道彼此的存在和地址。 10 | 11 | 那么关键地方是 12 | 13 | 有人一直在监听一个端口X,接收UDP包 14 | 有人在一直往这个端口X发送UDP包 15 | 监听端的人收到了UDP包后,就知道了发送者的Ip地址。但这时发送者是不知道接收者的,因为这是UDP包,无连接的状态的。那怎么做才让发送者也知道了。其实只要发过来再做一次就行。发送者同时也是监听者,这就行了。不过发送者A监听的是另一个端口Y,接收者B接收到包后,发送一个Y端口的包给A就行了。 16 | 17 | 总结起来: 18 | 19 | 主机监听端口X,一直发送Y端口的广播UDP包 20 | 从机监听端口Y,接收到UDP包后,往X端口发送UDP包。 21 | 这样就能双方知道对方的Ip地址了。发送的数据包也可以很讲究,例如发送json数据,可以自己定义一些字段,当接收者收到之后,解析数据,如果不符合规则的数据包直接丢弃掉。这就可以支撑一些业务逻辑了。 22 | 23 | 广播地址 24 | 25 | 广播地址是什么呢,一般都是用255.255.255.255,也有不用广播用组播地址例如192.168.43.255.这个组播地址就是热点的时候采用的地址,因为开启热点之后,那个手机的IP地址是固定的,是192.168.43.1其他端的手机都是这个地址后面的192.168.43.X。如果广播发送不了或者被禁掉了?一般来说还有一种方法,就是子网掩码里面的所有Ip地址遍历发一遍,这也是办法之一。 26 | 27 | 一对多的通信 28 | 当知道对方的Ip地址后,一切都好办了。端对端的通信有挺多方法,一对多的通信也不少。如果要同时考虑在Android和iOS都好实现的话,其实可选的不是很多。 29 | 30 | 最保守的方法就是直接采用TCP协议建立sock来通信。在iOS端和Android端都有很好的socket库,不用担心能不能实现和兼容性问题。这个方案挺好但是缺点也明显,因为sock通信只定义了最基础的,消息格式要完全自己定制和实现,还多心跳之类的,这里的工作量还不少。也许还有少的框架实现了这些,但是要同时在iOS端和安卓端都有实现,我还真不知道有哪些。而且我的情景里最重要的是有一个主机在里面,所有的从机都跟主机通信,所以必须说要有一个server。 31 | 32 | 我后来采用的的WebSocket来实现。WebSocket在Android上有很多库,不过大多数都是client端的,包括了server端的有AndroidAsync,Java-Websocket。我采用的是AndroidAysnc。而iOS端可选择的就更少了。我在github上找了很多库,都是只支持client的,有支持server的都是很久很老的项目,找到一个采用swift写的叫Telegraph库,试用了起来,这个库实现得不是很好,有挺多问题,不尽如意。最终现在iOS端只有采用了Facebook的SocketRocket来作为client端,server端暂时没有好的实现。**后来我找到了一个还算比较可靠的包含server的iOS的WebSocket库:PocketSocket。感兴趣的可以看一下** 33 | 34 | 采用WebSocket来实现双向通信有不少好处,例如它直接支持发送字符串消息,二进制数据(文件),还有连接上和断开都有相应的事件,还有ping-pong这种心跳机制,我觉得挺好的。iOS端的WebSocket server暂时没有找到可靠的第三方实现,比较遗憾。 35 | 36 | 我自己做了demo工程,上传到github。 37 | 38 | * [Android局域网发现和建立一对多通信](https://github.com/szuwest/Recorder) 39 | * [iOS局域网发现和建立一对多通信](https://github.com/szuwest/SpeakinRecorder) 40 | 41 | 42 | 如果对你有用或许你可以打赏请我喝杯咖啡☕️ 43 | ![打赏](https://raw.githubusercontent.com/szuwest/szuwest.github.io/master/images/2018-02-21%20133111.jpg) 44 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 26 6 | buildToolsVersion "26.0.1" 7 | defaultConfig { 8 | applicationId "com.speakin.recorder" 9 | minSdkVersion 14 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'com.android.support:appcompat-v7:26.+' 29 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 30 | testCompile 'junit:junit:4.12' 31 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 32 | compile 'com.nononsenseapps:filepicker:4.1.0' 33 | compile 'com.koushikdutta.async:androidasync:2.+' 34 | compile 'org.greenrobot:eventbus:3.0.0' 35 | } 36 | repositories { 37 | mavenCentral() 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/west/Documents/software/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/speakin/recorder/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.speakin.recorder", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/RecorderApp.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Copyright 2017 SpeakIn.Inc 7 | * Created by west on 2017/10/25. 8 | */ 9 | 10 | public class RecorderApp extends Application { 11 | 12 | public static RecorderApp app = null; 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | app = this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/ControlConstants.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control; 2 | 3 | import android.os.Environment; 4 | 5 | /** 6 | * Copyright 2017 SpeakIn.Inc 7 | * Created by west on 2017/10/17. 8 | */ 9 | 10 | public class ControlConstants { 11 | 12 | public static final String TEAMID = "Speakin"; 13 | public static final String TASKID = "Test"; 14 | public static final int SLAVECOUNT = 3; 15 | 16 | public static final int SERVER_SOCKET_PORT = 8080; 17 | public static final String PROTOCOL = "speakinchat";// 18 | 19 | public static final int SLAVE_LISTEN_PORT = 8883; 20 | public static final int MASTER_LISTEN_PORT = 8882; 21 | 22 | public static final String ROOT = Environment.getExternalStorageDirectory().getAbsolutePath()+"/SpeakInRecorder/"; 23 | public static final String RECEIVE_DIR = ROOT + "receive/"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/MasterControlManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import com.koushikdutta.async.AsyncNetworkSocket; 8 | import com.koushikdutta.async.http.WebSocket; 9 | import com.speakin.recorder.module.control.search.MasterSearchManager; 10 | import com.speakin.recorder.module.control.websocket.MasterWebSocketManager; 11 | 12 | import org.json.JSONObject; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * Copyright 2017 SpeakIn.Inc 18 | * Created by west on 2017/10/9. 19 | */ 20 | 21 | public class MasterControlManager implements MasterSearchManager.MasterSearchManagerCallback, MasterWebSocketManager.MasterSocketManagerCallback{ 22 | 23 | public interface MasterControlManagerCallback { 24 | void onServerError(Exception ex); 25 | void onClientConnected(String clientSocket); 26 | void onClientDisconnect(String clientSocket); 27 | void onMessageReceive(String clientSocket, String message); 28 | void onReceiveFile(String clientSocket, String filePath); 29 | } 30 | 31 | private static final String TAG = MasterControlManager.class.getSimpleName(); 32 | private MasterSearchManager searchManager; 33 | private MasterWebSocketManager socketManager; 34 | private Handler handler = new Handler(Looper.getMainLooper()); 35 | private MasterControlManagerCallback controlManagerCallback; 36 | private boolean isRunning = false; 37 | 38 | public void setControlManagerCallback(MasterControlManagerCallback controlManagerCallback) { 39 | this.controlManagerCallback = controlManagerCallback; 40 | } 41 | 42 | public void start() { 43 | start(ControlConstants.SLAVECOUNT, ControlConstants.TEAMID, ControlConstants.TASKID); 44 | } 45 | 46 | public void stop() { 47 | stopSearch(); 48 | stopMaster(); 49 | isRunning = false; 50 | } 51 | 52 | public void start(int slaveCount, String teamId, String taskId) { 53 | if (isRunning) { 54 | Log.d(TAG, "is running"); 55 | return; 56 | } 57 | searchManager = new MasterSearchManager(slaveCount, teamId, taskId); 58 | searchManager.setSearchCallback(this); 59 | searchManager.start(); 60 | socketManager = new MasterWebSocketManager(slaveCount); 61 | socketManager.setMasterSocketManager(this); 62 | socketManager.start(); 63 | 64 | } 65 | 66 | public void send(String message) { 67 | if (socketManager != null) { 68 | socketManager.sendMessage(message); 69 | } 70 | } 71 | 72 | private void stopMaster() { 73 | if (socketManager != null) { 74 | socketManager.stop(); 75 | socketManager.setMasterSocketManager(null); 76 | } 77 | socketManager = null; 78 | } 79 | 80 | private void stopSearch() { 81 | if (searchManager != null) { 82 | searchManager.stop(); 83 | searchManager.setSearchCallback(null); 84 | } 85 | searchManager = null; 86 | } 87 | 88 | @Override 89 | public void onFoundNewSlave(String slaveIp, JSONObject slaveInfo) { 90 | Log.d(TAG, "found slave " + slaveIp + " info=" + slaveInfo); 91 | } 92 | 93 | @Override 94 | public void onFoundSlaves(List slaveIpList) { 95 | Log.d(TAG, "slave count = " + slaveIpList.size()); 96 | String msg = "发现从机:\n"; 97 | for (String ip : slaveIpList) { 98 | msg += "IP地址:" + ip + "\n"; 99 | } 100 | Log.d(TAG, msg); 101 | 102 | handler.post(new Runnable() { 103 | @Override 104 | public void run() { 105 | stopSearch(); 106 | } 107 | }); 108 | } 109 | 110 | @Override 111 | public void onServerError(final Exception ex) { 112 | Log.d(TAG, "start server error " + ex.getLocalizedMessage()); 113 | handler.post(new Runnable() { 114 | @Override 115 | public void run() { 116 | if (controlManagerCallback != null) { 117 | controlManagerCallback.onServerError(ex); 118 | } 119 | } 120 | }); 121 | 122 | isRunning = false; 123 | } 124 | 125 | @Override 126 | public void onClientConnected(WebSocket clientSocket) { 127 | final AsyncNetworkSocket workSocket = (AsyncNetworkSocket) clientSocket.getSocket(); 128 | Log.d(TAG, "client connected: " + workSocket.getRemoteAddress().getHostName()); 129 | handler.post(new Runnable() { 130 | @Override 131 | public void run() { 132 | if (controlManagerCallback != null) { 133 | controlManagerCallback.onClientConnected(workSocket.getRemoteAddress().getHostName()); 134 | } 135 | if (socketManager.getClientCount() == ControlConstants.SLAVECOUNT) { 136 | stopSearch(); 137 | } 138 | } 139 | }); 140 | } 141 | 142 | @Override 143 | public void onClientDisconnect(WebSocket clientSocket) { 144 | final AsyncNetworkSocket workSocket = (AsyncNetworkSocket) clientSocket.getSocket(); 145 | Log.d(TAG, "client onClientDisconnect: " + workSocket.getRemoteAddress().getHostName()); 146 | handler.post(new Runnable() { 147 | @Override 148 | public void run() { 149 | if (controlManagerCallback != null) 150 | controlManagerCallback.onClientDisconnect(workSocket.getRemoteAddress().getHostName()); 151 | } 152 | }); 153 | 154 | } 155 | 156 | @Override 157 | public void onMessageReceive(WebSocket clientSocket, final String message) { 158 | final AsyncNetworkSocket workSocket = (AsyncNetworkSocket) clientSocket.getSocket(); 159 | Log.d(TAG, "receive: " + message + "from: " + workSocket.getRemoteAddress().getHostName()); 160 | handler.post(new Runnable() { 161 | @Override 162 | public void run() { 163 | if (controlManagerCallback != null) 164 | controlManagerCallback.onMessageReceive(workSocket.getRemoteAddress().getHostName(), message); 165 | } 166 | }); 167 | } 168 | 169 | @Override 170 | public void onFileReceive(WebSocket clientSocket, final String filePath) { 171 | final AsyncNetworkSocket workSocket = (AsyncNetworkSocket) clientSocket.getSocket(); 172 | Log.d(TAG, "receive: " + filePath + "from: " + workSocket.getRemoteAddress().getHostName()); 173 | handler.post(new Runnable() { 174 | @Override 175 | public void run() { 176 | if (controlManagerCallback != null) 177 | controlManagerCallback.onReceiveFile(workSocket.getRemoteAddress().getHostName(), filePath); 178 | } 179 | }); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/SlaveControlManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import com.koushikdutta.async.http.WebSocket; 8 | import com.speakin.recorder.module.control.search.SlaveSearchManager; 9 | import com.speakin.recorder.module.control.websocket.SlaveWebSocketManager; 10 | 11 | import org.json.JSONObject; 12 | 13 | /** 14 | * Copyright 2017 SpeakIn.Inc 15 | * Created by west on 2017/10/9. 16 | */ 17 | 18 | public class SlaveControlManager implements SlaveSearchManager.SlaveSearchManagerCallback, SlaveWebSocketManager.SlaveSocketManagerCallback{ 19 | private static final String TAG = SlaveControlManager.class.getSimpleName(); 20 | 21 | public interface SlaveControlManagerCallback { 22 | void onFoundMaster(String masterIp, JSONObject masterInfo); 23 | void onConnectedMaster(String masterIp, Exception ex); 24 | void onDisconnectMaster(String masterIp, Exception ex); 25 | void onReceiveMessage(String message); 26 | } 27 | 28 | private SlaveSearchManager searchManager; 29 | private String masterIp; 30 | private SlaveWebSocketManager socketManager; 31 | private Handler handler = new Handler(Looper.getMainLooper()); 32 | private SlaveControlManagerCallback controlManagerCallback; 33 | private boolean isConnected = false; 34 | 35 | public void setControlManagerCallback(SlaveControlManagerCallback controlManagerCallback) { 36 | this.controlManagerCallback = controlManagerCallback; 37 | } 38 | 39 | public void start() { 40 | startSearchMaster(ControlConstants.TEAMID, ControlConstants.TASKID); 41 | } 42 | 43 | public void stop() { 44 | stopSearch(); 45 | stopConnect(); 46 | isConnected = false; 47 | } 48 | 49 | public boolean send(String message) { 50 | if (socketManager != null) { 51 | return socketManager.sendMessage(message); 52 | } 53 | Log.d(TAG, "not connected"); 54 | return false; 55 | } 56 | 57 | public void sendFile(String filePath) { 58 | if (socketManager != null) { 59 | socketManager.sendFile(filePath); 60 | } 61 | } 62 | 63 | public void startSearchMaster(String teamId, String taskId) { 64 | if (isConnected) { 65 | Log.d(TAG, "isConnected"); 66 | return; 67 | } 68 | stop(); 69 | isConnected = true; 70 | searchManager = new SlaveSearchManager(teamId, taskId); 71 | searchManager.setSlaveSearchCallback(this); 72 | searchManager.start(); 73 | } 74 | 75 | private void stopSearch() { 76 | if (searchManager != null) { 77 | searchManager.stop(); 78 | searchManager.setSlaveSearchCallback(null); 79 | } 80 | searchManager = null; 81 | } 82 | 83 | private void startConnectMaster() { 84 | socketManager = new SlaveWebSocketManager(masterIp, ControlConstants.SERVER_SOCKET_PORT); 85 | socketManager.setSlaveSocketManagerCallback(this); 86 | socketManager.startConnect(); 87 | } 88 | 89 | private void stopConnect() { 90 | if (socketManager != null) { 91 | socketManager.stopConnect(); 92 | socketManager.setSlaveSocketManagerCallback(null); 93 | } 94 | socketManager = null; 95 | } 96 | 97 | 98 | 99 | @Override 100 | public void onFoundMaster(final String masterIp, final JSONObject masterInfo) { 101 | Log.d(TAG, "found master" + masterIp); 102 | this.masterIp = masterIp; 103 | 104 | handler.post(new Runnable() { 105 | @Override 106 | public void run() { 107 | if (controlManagerCallback != null) { 108 | controlManagerCallback.onFoundMaster(masterIp, masterInfo); 109 | } 110 | stopSearch(); 111 | startConnectMaster(); 112 | } 113 | }); 114 | } 115 | 116 | 117 | @Override 118 | public void onMasterConnected(WebSocket socket, final Exception ex) { 119 | if (socket != null) { 120 | Log.d(TAG, "master connected"); 121 | isConnected = true; 122 | } else { 123 | Log.d(TAG, "connect to master fail error = " + ex.getLocalizedMessage()); 124 | isConnected = false; 125 | } 126 | handler.post(new Runnable() { 127 | @Override 128 | public void run() { 129 | if (controlManagerCallback != null) { 130 | controlManagerCallback.onConnectedMaster(SlaveControlManager.this.masterIp, ex); 131 | } 132 | } 133 | }); 134 | 135 | } 136 | 137 | @Override 138 | public void onMasterDisconnect(WebSocket socket, final Exception ex) { 139 | isConnected = false; 140 | Log.d(TAG, "master disconnected"); 141 | handler.post(new Runnable() { 142 | @Override 143 | public void run() { 144 | if (controlManagerCallback != null) { 145 | controlManagerCallback.onDisconnectMaster(SlaveControlManager.this.masterIp, ex); 146 | } 147 | } 148 | }); 149 | 150 | } 151 | 152 | @Override 153 | public void onReceiveMessage(final String message) { 154 | Log.d(TAG, "receive: " + message); 155 | handler.post(new Runnable() { 156 | @Override 157 | public void run() { 158 | if (controlManagerCallback != null) { 159 | controlManagerCallback.onReceiveMessage(message); 160 | } 161 | } 162 | }); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/search/DeviceBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.search; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import com.speakin.recorder.module.control.ControlConstants; 7 | import com.speakin.recorder.utils.IpUtil; 8 | 9 | import java.io.IOException; 10 | import java.net.DatagramPacket; 11 | import java.net.DatagramSocket; 12 | import java.util.Arrays; 13 | 14 | /** 15 | * Copyright 2017 SpeakIn.Inc 16 | * Created by west on 2017/9/26. 17 | */ 18 | 19 | public class DeviceBroadcastReceiver { 20 | 21 | 22 | private static final int BUFFER_LEN = 1024*4; 23 | private volatile boolean needListen = true; 24 | 25 | public interface BroadcastReceiverCallback { 26 | void onError(String errMsg); 27 | void onReceive(String senderIp, String message); 28 | } 29 | 30 | private int port = 0; 31 | private Handler handler; 32 | private DatagramSocket server = null; 33 | 34 | public DeviceBroadcastReceiver(boolean isSlave) { 35 | if (!isSlave) { 36 | port = ControlConstants.MASTER_LISTEN_PORT; 37 | } else { 38 | port = ControlConstants.SLAVE_LISTEN_PORT; 39 | } 40 | handler = new Handler(Looper.getMainLooper()); 41 | needListen = true; 42 | } 43 | 44 | private BroadcastReceiverCallback callback; 45 | 46 | public void setBroadcastReceiveCallback(BroadcastReceiverCallback callback) { 47 | this.callback = callback; 48 | } 49 | 50 | private void startReceive() throws IOException { 51 | final DatagramPacket receive = new DatagramPacket(new byte[BUFFER_LEN], BUFFER_LEN); 52 | 53 | server = new DatagramSocket(port); 54 | 55 | System.out.println("---------------------------------"); 56 | System.out.println("start listen ......"); 57 | System.out.println("---------------------------------"); 58 | 59 | while (needListen) { 60 | server.receive(receive); 61 | byte[] recvByte = Arrays.copyOfRange(receive.getData(), 0, receive.getLength()); 62 | final String receiveMsg = new String(recvByte); 63 | System.out.println("receive msg:" + receiveMsg); 64 | 65 | final String senderIp = receive.getAddress().getHostAddress(); 66 | String localIP = IpUtil.getHostIP(); 67 | if (senderIp.equals(localIP)) { 68 | System.out.println("myself,ignore"); 69 | continue; 70 | } 71 | System.out.println("hostIp" + receive.getAddress().toString()); 72 | System.out.println("port" + receive.getPort()); 73 | 74 | handler.post(new Runnable() { 75 | @Override 76 | public void run() { 77 | if (callback != null) { 78 | callback.onReceive(senderIp, receiveMsg); 79 | } 80 | } 81 | }); 82 | } 83 | server.disconnect(); 84 | server.close(); 85 | System.out.println("end listen ......"); 86 | } 87 | 88 | public void startBroadcastReceive() { 89 | 90 | if (server != null) { 91 | server.close(); 92 | server = null; 93 | } 94 | new Thread(new Runnable() { 95 | @Override 96 | public void run() { 97 | try { 98 | startReceive(); 99 | } catch (final IOException e) { 100 | e.printStackTrace(); 101 | handler.post(new Runnable() { 102 | @Override 103 | public void run() { 104 | if (callback != null) { 105 | callback.onError(e.getLocalizedMessage()); 106 | } 107 | } 108 | }); 109 | if (server != null) { 110 | server.close(); 111 | server = null; 112 | } 113 | } 114 | } 115 | }).start(); 116 | } 117 | 118 | public void stopReceive() { 119 | needListen = false; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/search/DeviceBroadcastSender.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.search; 2 | 3 | import android.content.Context; 4 | import android.net.DhcpInfo; 5 | import android.net.wifi.WifiManager; 6 | 7 | import com.speakin.recorder.RecorderApp; 8 | import com.speakin.recorder.module.control.ControlConstants; 9 | import com.speakin.recorder.utils.IpUtil; 10 | import com.speakin.recorder.utils.NetHelper; 11 | 12 | import java.io.IOException; 13 | import java.net.DatagramPacket; 14 | import java.net.DatagramSocket; 15 | import java.net.InetAddress; 16 | import java.net.UnknownHostException; 17 | 18 | /** 19 | * Copyright 2017 SpeakIn.Inc 20 | * Created by west on 2017/9/26. 21 | */ 22 | 23 | public class DeviceBroadcastSender { 24 | 25 | private static final String BROADCAST_IP = "255.255.255.255"; 26 | // private static final String MSG_TO_SEND = "{\"from\":\"speakin\",\"type\":\"slave\",\"port\":9001}"; 27 | // private static final String MSG_TO_SEND2 = "{\"from\":\"speakin\",\"type\":\"master\"},\"port\":9002"; 28 | 29 | 30 | private int port = 0; 31 | private boolean isSlave = true; 32 | 33 | public DeviceBroadcastSender(boolean isSlave) { 34 | this.isSlave = isSlave; 35 | if (isSlave) port = ControlConstants.MASTER_LISTEN_PORT; 36 | else port = ControlConstants.SLAVE_LISTEN_PORT; 37 | } 38 | 39 | // private String getMsgToSend() { 40 | // if (isSlave) return MSG_TO_SEND; 41 | // else return MSG_TO_SEND2; 42 | // } 43 | 44 | private void sendBroadcast(String message) throws IOException { 45 | 46 | String ipAddr = BROADCAST_IP; 47 | if (IpUtil.isWifiApEnabled(RecorderApp.app)) { 48 | System.out.println("已开启热点"); 49 | ipAddr = "192.168.43.255"; 50 | } 51 | 52 | byte[] msg = new String(message).getBytes(); 53 | /* 54 | * 在Java UDP中单播与广播的代码是相同的,要实现具有广播功能的程序只需要使用广播地址即可, 例如:这里使用了本地的广播地址 55 | */ 56 | InetAddress inetAddr = InetAddress.getByName(ipAddr); 57 | DatagramSocket client = new DatagramSocket(); 58 | 59 | DatagramPacket sendPack = new DatagramPacket(msg, msg.length, inetAddr, port); 60 | 61 | client.send(sendPack); 62 | System.out.println("send msg complete"); 63 | client.close(); 64 | } 65 | 66 | 67 | public void sendBroadcastData(final String message) { 68 | new Thread(new Runnable() { 69 | @Override 70 | public void run() { 71 | try { 72 | sendBroadcast(message); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | }).start(); 78 | } 79 | 80 | public static InetAddress getBroadcastAddress(Context context) throws UnknownHostException { 81 | WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); 82 | DhcpInfo dhcp = wifi.getDhcpInfo(); 83 | if(dhcp==null) { 84 | return InetAddress.getByName("255.255.255.255"); 85 | } 86 | int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask; 87 | byte[] quads = new byte[4]; 88 | for (int k = 0; k < 4; k++) 89 | quads[k] = (byte) ((broadcast >> k * 8) & 0xFF); 90 | return InetAddress.getByAddress(quads); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/search/DeviceReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.search 2 | 3 | /** 4 | * Copyright 2017 SpeakIn.Inc 5 | * Created by west on 2017/9/26. 6 | */ 7 | 8 | class DeviceReceiver { 9 | 10 | fun startReceiver() { 11 | print("log") 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/search/MasterSearchManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.search; 2 | 3 | import android.os.Build; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | import com.speakin.recorder.module.control.ControlConstants; 10 | 11 | import org.json.JSONException; 12 | import org.json.JSONObject; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Copyright 2017 SpeakIn.Inc 19 | * Created by west on 2017/9/27. 20 | */ 21 | 22 | public class MasterSearchManager implements DeviceBroadcastReceiver.BroadcastReceiverCallback { 23 | 24 | private static final String TAG = "MasterSearchManager"; 25 | private DeviceBroadcastSender broadcastSender; 26 | private DeviceBroadcastReceiver broadcastReceiver; 27 | private static final int BROADCAST_INTERVAL = 5*1000; 28 | 29 | public interface MasterSearchManagerCallback { 30 | void onFoundNewSlave(String slaveIp, JSONObject slaveInfo); 31 | void onFoundSlaves(List slaveIpList); 32 | } 33 | 34 | private int minDeviceCount = 1; 35 | private Handler handler; 36 | private volatile boolean stop = false; 37 | private List ipList; 38 | private String teamId; 39 | private String taskId; 40 | 41 | public MasterSearchManager(int minDeviceCount, String teamId, String taskId) { 42 | ipList = new ArrayList<>(5); 43 | handler = new Handler(Looper.getMainLooper()); 44 | this.teamId = teamId; 45 | this.taskId = taskId; 46 | this.minDeviceCount = minDeviceCount; 47 | } 48 | 49 | private MasterSearchManagerCallback callback; 50 | 51 | public void setSearchCallback(MasterSearchManagerCallback callback) { 52 | this.callback = callback; 53 | } 54 | 55 | private String getSendMsg() { 56 | JSONObject jsonObject = new JSONObject(); 57 | try { 58 | jsonObject.put("type", "master"); 59 | jsonObject.put("teamId", this.teamId); 60 | jsonObject.put("port", ControlConstants.SERVER_SOCKET_PORT); 61 | jsonObject.put("taskId", this.taskId); 62 | jsonObject.put("deviceName", Build.MODEL); 63 | } catch (JSONException e) { 64 | e.printStackTrace(); 65 | } 66 | return jsonObject.toString(); 67 | } 68 | 69 | private Runnable broadcastRunnable = new Runnable() { 70 | @Override 71 | public void run() { 72 | if (stop) return; 73 | if (broadcastSender != null) { 74 | broadcastSender.sendBroadcastData(getSendMsg()); 75 | handler.postDelayed(this, BROADCAST_INTERVAL); 76 | } 77 | } 78 | }; 79 | 80 | public void start() { 81 | stop = false; 82 | broadcastReceiver = new DeviceBroadcastReceiver(false); 83 | broadcastReceiver.setBroadcastReceiveCallback(this); 84 | broadcastSender = new DeviceBroadcastSender(false); 85 | broadcastReceiver.startBroadcastReceive(); 86 | broadcastSender.sendBroadcastData(getSendMsg()); 87 | handler.postDelayed(broadcastRunnable, BROADCAST_INTERVAL); 88 | } 89 | 90 | public void stop() { 91 | Log.d(TAG, "stop search"); 92 | stop = true; 93 | broadcastReceiver.stopReceive(); 94 | broadcastReceiver.setBroadcastReceiveCallback(null); 95 | broadcastReceiver = null; 96 | 97 | broadcastSender = null; 98 | handler.removeCallbacks(broadcastRunnable); 99 | } 100 | 101 | @Override 102 | public void onError(String errMsg) { 103 | Log.d(TAG, "errMsg =" + errMsg); 104 | } 105 | 106 | @Override 107 | public void onReceive(String senderIp, String message) { 108 | Log.d(TAG, "message:" + message); 109 | try { 110 | JSONObject jsonObj = new JSONObject(message); 111 | String type = jsonObj.optString("type"); 112 | if (type != null && type.equals("slave")) { 113 | String team = jsonObj.optString("teamId"); 114 | String task = jsonObj.optString("taskId"); 115 | if (!TextUtils.isEmpty(team) && task.equals(teamId) && !TextUtils.isEmpty(task) && task.equals(taskId)) { 116 | if (!ipList.contains(senderIp)) { 117 | ipList.add(senderIp); 118 | if (callback != null) callback.onFoundNewSlave(senderIp, jsonObj); 119 | } 120 | if (ipList.size() == this.minDeviceCount) { 121 | if (callback != null) callback.onFoundSlaves(ipList); 122 | } 123 | } 124 | } 125 | } catch (JSONException e) { 126 | e.printStackTrace(); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/search/SlaveSearchManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.search; 2 | 3 | import android.os.Build; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | /** 11 | * Copyright 2017 SpeakIn.Inc 12 | * Created by west on 2017/9/27. 13 | */ 14 | 15 | public class SlaveSearchManager implements DeviceBroadcastReceiver.BroadcastReceiverCallback{ 16 | 17 | private static final String TAG = "SlaveSearchManager"; 18 | private DeviceBroadcastSender broadcastSender; 19 | private DeviceBroadcastReceiver broadcastReceiver; 20 | 21 | public interface SlaveSearchManagerCallback { 22 | void onFoundMaster(String masterIp, JSONObject masterInfo); 23 | } 24 | 25 | private SlaveSearchManagerCallback callback; 26 | 27 | public void setSlaveSearchCallback(SlaveSearchManagerCallback callback) { 28 | this.callback = callback; 29 | } 30 | 31 | private String teamId; 32 | private String taskId; 33 | 34 | public SlaveSearchManager(String teamId, String taskId) { 35 | this.teamId = teamId; 36 | this.taskId = taskId; 37 | } 38 | 39 | private String getSendMsg() { 40 | JSONObject jsonObject = new JSONObject(); 41 | try { 42 | jsonObject.put("type", "slave"); 43 | jsonObject.put("teamId", this.teamId); 44 | jsonObject.put("taskId", this.taskId); 45 | jsonObject.put("deviceName", Build.MODEL); 46 | } catch (JSONException e) { 47 | e.printStackTrace(); 48 | } 49 | return jsonObject.toString(); 50 | } 51 | 52 | public void start() { 53 | broadcastReceiver = new DeviceBroadcastReceiver(true); 54 | broadcastReceiver.setBroadcastReceiveCallback(this); 55 | 56 | broadcastSender = new DeviceBroadcastSender(true); 57 | broadcastReceiver.startBroadcastReceive(); 58 | } 59 | 60 | public void stop() { 61 | broadcastReceiver.stopReceive(); 62 | broadcastReceiver.setBroadcastReceiveCallback(null); 63 | broadcastReceiver = null; 64 | broadcastSender = null; 65 | } 66 | 67 | @Override 68 | public void onError(String errMsg) { 69 | Log.d(TAG, "errMsg=" + errMsg); 70 | } 71 | 72 | @Override 73 | public void onReceive(String senderIp, String message) { 74 | Log.d(TAG, "message:" + message); 75 | try { 76 | JSONObject jsonObj = new JSONObject(message); 77 | String type = jsonObj.optString("type"); 78 | if (type != null && type.equals("master")) { 79 | String team = jsonObj.optString("teamId"); 80 | String task = jsonObj.optString("taskId"); 81 | broadcastSender.sendBroadcastData(getSendMsg()); 82 | if (!TextUtils.isEmpty(team) && team.equals(teamId) && !TextUtils.isEmpty(task) && task.equals(taskId)) { 83 | if (callback != null) callback.onFoundMaster(senderIp, jsonObj); 84 | } 85 | } 86 | } catch (JSONException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/CommonUtil.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket; 2 | 3 | import android.os.Environment; 4 | 5 | import java.io.File; 6 | import java.util.Calendar; 7 | 8 | /** 9 | * @author keshuangjie 10 | * @date 2014-12-1 下午7:41:28 11 | * @package com.jimmy.im.client.util 12 | * @version 1.0 常用工具类 13 | */ 14 | public class CommonUtil { 15 | 16 | public static final String PATH_ROOT = "SpeakInRecorder"; 17 | public static final String PATH_VOICE = "voice"; 18 | public static final String PATH_SEPARATOR = File.separator; 19 | public static final String FILE_SUFFIX = ".wav"; 20 | 21 | public static String getAmrFilePath(String name) { 22 | String path = ""; 23 | if (isSdcardMounted()) { 24 | path = Environment.getExternalStorageDirectory().getAbsolutePath() 25 | + PATH_SEPARATOR + PATH_ROOT 26 | + PATH_SEPARATOR + PATH_VOICE 27 | + PATH_SEPARATOR + name 28 | + FILE_SUFFIX; 29 | } 30 | return path; 31 | } 32 | 33 | /** 34 | * 获取.amr文件名 35 | * @param path 36 | * @return 37 | */ 38 | public static String getAmrFileName(String path){ 39 | return getFileName(path, FILE_SUFFIX); 40 | } 41 | 42 | /** 43 | * 获取文件名 44 | * @param path 45 | * @return 46 | */ 47 | public static String getFileName(String path, String suffix){ 48 | String name = ""; 49 | int index = path.lastIndexOf(CommonUtil.PATH_SEPARATOR); 50 | name = path.substring(index+1).replace(suffix, ""); 51 | return name; 52 | } 53 | 54 | /** 55 | * 创建目录(不存在则创建) 56 | * @param dir 57 | * @return 58 | */ 59 | public static boolean CreateDir(String dir) { 60 | boolean isSuccess = false; 61 | File file = new File(dir); 62 | File parentFile = file.getParentFile(); 63 | if (parentFile != null) { 64 | if(!parentFile.exists()){ 65 | isSuccess = file.getParentFile().mkdirs(); 66 | }else{ 67 | isSuccess = true; 68 | } 69 | } 70 | return isSuccess; 71 | } 72 | 73 | public static boolean isSdcardMounted() { 74 | return Environment.getExternalStorageState().equals( 75 | Environment.MEDIA_MOUNTED); 76 | } 77 | 78 | 79 | /** 80 | * 获取当前时间 81 | * 82 | * @return 83 | */ 84 | public static String getDate() { 85 | Calendar c = Calendar.getInstance(); 86 | 87 | String year = String.valueOf(c.get(Calendar.YEAR)); 88 | String month = String.valueOf(c.get(Calendar.MONTH)); 89 | String day = String.valueOf(c.get(Calendar.DAY_OF_MONTH) + 1); 90 | String hour = String.valueOf(c.get(Calendar.HOUR_OF_DAY)); 91 | String mins = String.valueOf(c.get(Calendar.MINUTE)); 92 | 93 | StringBuffer sbBuffer = new StringBuffer(); 94 | sbBuffer.append(year + "-" + month + "-" + day + " " + hour + ":" 95 | + mins); 96 | 97 | return sbBuffer.toString(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/SocketClient.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket; 2 | 3 | import com.speakin.recorder.module.control.socket.handle.MsgReceiveHandler; 4 | import com.speakin.recorder.module.control.socket.handle.MsgSendHandler; 5 | 6 | import java.net.Socket; 7 | 8 | /** 9 | * Copyright 2017 SpeakIn.Inc 10 | * Created by west on 2017/10/24. 11 | */ 12 | 13 | public class SocketClient { 14 | 15 | private Socket socket; 16 | private MsgReceiveHandler receiveHandler; 17 | private MsgSendHandler sendHandler; 18 | 19 | public SocketClient(Socket socket) { 20 | this.socket = socket; 21 | } 22 | 23 | public void start() { 24 | receiveHandler = new MsgReceiveHandler(socket); 25 | receiveHandler.start(); 26 | sendHandler = new MsgSendHandler(socket); 27 | sendHandler.start(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/SocketClientManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | 8 | import com.speakin.recorder.module.control.socket.handle.MsgReceiveHandler; 9 | import com.speakin.recorder.module.control.socket.handle.MsgSendHandler; 10 | 11 | /** 12 | * @author keshuangjie 13 | * @date 2014-12-1 下午7:40:29 14 | * @package com.jimmy.im.client.socket 15 | * @version 1.0 16 | * 连接管理类 17 | */ 18 | public class SocketClientManager { 19 | 20 | private static final SocketClientManager sINSTANCE = new SocketClientManager(); 21 | 22 | private SocketClientManager() {} 23 | 24 | public static SocketClientManager getInstance() { 25 | return sINSTANCE; 26 | } 27 | 28 | private String serverIP; 29 | int serverPort; 30 | 31 | /** 32 | * 启动一个socket长连接 33 | */ 34 | public void startSocket(String serverIp, int serverPort){ 35 | this.serverIP = serverIp; 36 | this.serverPort = serverPort; 37 | new Launcher().start(); 38 | } 39 | 40 | // @Deprecated 41 | // public void startTextHandlerSocket(){ 42 | // new TextHandlerSocket().start(); 43 | // } 44 | // 45 | // @Deprecated 46 | // public void startFileSendSocket() { 47 | // new FileHandlerSocket(FileHandlerSocket.TYPE_SEND).start(); 48 | // } 49 | // 50 | // @Deprecated 51 | // public void startFileReceivelerSocket() { 52 | // new FileHandlerSocket(FileHandlerSocket.TYPE_RECEIVE).start(); 53 | // } 54 | 55 | private class Launcher extends Thread{ 56 | 57 | @Override 58 | public void run() { 59 | try { 60 | Socket socket = new Socket(); 61 | socket.connect(new InetSocketAddress(serverIP, serverPort), 5000); 62 | socket.setKeepAlive(true); 63 | 64 | new MsgReceiveHandler(socket).start(); 65 | new MsgSendHandler(socket).start(); 66 | 67 | } catch (SocketException e) { 68 | e.printStackTrace(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/SocketServerManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | 7 | import android.util.Log; 8 | 9 | import com.speakin.recorder.module.control.socket.handle.MsgReceiveHandler; 10 | import com.speakin.recorder.module.control.socket.handle.MsgSendHandler; 11 | 12 | 13 | /** 14 | * @author keshuangjie 15 | * @date 2014-12-1 下午7:46:04 16 | * @package com.jimmy.im.server.socket 17 | * @version 1.0 18 | * socket连接处理 19 | */ 20 | public class SocketServerManager implements Runnable { 21 | private static final String TAG = SocketServerManager.class.getSimpleName(); 22 | 23 | public static final int CONNET_PORT = 5013; 24 | 25 | ServerSocket serverSocket; 26 | 27 | private static final SocketServerManager sINSTANCE = new SocketServerManager(); 28 | 29 | private SocketServerManager() { 30 | }; 31 | 32 | public static SocketServerManager getInstance() { 33 | return sINSTANCE; 34 | } 35 | 36 | public void startConnect() { 37 | new Thread(this).start(); 38 | } 39 | 40 | public void run() { 41 | try { 42 | Log.i(TAG, "1.创建ServerSocket"); 43 | serverSocket = new ServerSocket(CONNET_PORT); 44 | 45 | int num = 0;//socket connect num 46 | 47 | while (true) { 48 | Socket client = serverSocket.accept(); 49 | num++; 50 | log("run() -> craete client success " + num); 51 | 52 | new MsgReceiveHandler(client).start(); 53 | new MsgSendHandler(client).start(); 54 | 55 | } 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | }finally{ 59 | close(); 60 | } 61 | } 62 | 63 | public void close() { 64 | if (serverSocket != null) { 65 | try { 66 | serverSocket.close(); 67 | } catch (IOException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | } 72 | 73 | private void log(String message) { 74 | Log.i(TAG, message); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/data/MsgEntity.java: -------------------------------------------------------------------------------- 1 | 2 | package com.speakin.recorder.module.control.socket.data; 3 | 4 | /** 5 | * @author keshuangjie 6 | * @date 2014-12-1 下午7:44:51 7 | * @package com.jimmy.im.server.data 8 | * @version 1.0 9 | * 实体类 10 | */ 11 | public class MsgEntity { 12 | 13 | public static final int TYPE_TEXT = 1; 14 | public static final int TYPE_VOICE = 2; 15 | 16 | /** 用户名 */ 17 | public String userName; 18 | 19 | /** 日期 */ 20 | public String date; 21 | 22 | /** 是否是自己发送 */ 23 | public boolean isSelf; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/data/MsgQueueManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.data; 2 | 3 | import java.util.concurrent.LinkedBlockingQueue; 4 | 5 | 6 | /** 7 | * @author keshuangjie 8 | * @date 2014-12-4 下午2:02:03 9 | * @package com.jimmy.im.client.data 10 | * @version 1.0 11 | * 待发送消息队列 12 | */ 13 | public class MsgQueueManager { 14 | 15 | LinkedBlockingQueue mQueueList; 16 | 17 | private static final class Holder{ 18 | public static final MsgQueueManager sINSTANCE = new MsgQueueManager(); 19 | } 20 | 21 | private MsgQueueManager(){ 22 | mQueueList = new LinkedBlockingQueue(); 23 | } 24 | 25 | public static MsgQueueManager getInstance(){ 26 | return Holder.sINSTANCE; 27 | } 28 | 29 | public MsgEntity poll(){ 30 | MsgEntity entity = null; 31 | if(mQueueList != null){ 32 | entity = mQueueList.poll(); 33 | } 34 | return entity; 35 | } 36 | 37 | public void push(MsgEntity entity){ 38 | try { 39 | if(mQueueList != null){ 40 | mQueueList = new LinkedBlockingQueue(); 41 | } 42 | mQueueList.put(entity); 43 | } catch (InterruptedException e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/data/TextMsgEntity.java: -------------------------------------------------------------------------------- 1 | 2 | package com.speakin.recorder.module.control.socket.data; 3 | 4 | /** 5 | * @author keshuangjie 6 | * @date 2014-12-1 下午7:45:01 7 | * @package com.jimmy.im.server.data 8 | * @version 1.0 9 | * 文本实体类 10 | */ 11 | public class TextMsgEntity extends MsgEntity{ 12 | 13 | /** 文本内容 */ 14 | public String msgContent; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/data/VoiceMsgEntity.java: -------------------------------------------------------------------------------- 1 | 2 | package com.speakin.recorder.module.control.socket.data; 3 | 4 | /** 5 | * @author keshuangjie 6 | * @date 2014-12-1 下午7:45:17 7 | * @package com.jimmy.im.server.data 8 | * @version 1.0 9 | * 语音文件实体类 10 | */ 11 | public class VoiceMsgEntity extends MsgEntity{ 12 | 13 | /** 文件名称 */ 14 | public String fileName; 15 | 16 | /** 文件路径 */ 17 | public String filePath; 18 | 19 | /** 文件时长,以秒为单位 */ 20 | public int time; 21 | 22 | /** 文件大小 */ 23 | public long size; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/FileHandlerSocket.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.DataOutputStream; 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.OutputStream; 14 | import java.net.Socket; 15 | import java.net.SocketException; 16 | 17 | import android.text.TextUtils; 18 | import android.util.Log; 19 | 20 | 21 | import com.speakin.recorder.module.control.socket.CommonUtil; 22 | import com.speakin.recorder.module.control.socket.data.VoiceMsgEntity; 23 | 24 | import org.greenrobot.eventbus.EventBus; 25 | 26 | 27 | /** 28 | * @author keshuangjie 29 | * @date 2014-12-1 下午7:45:40 30 | * @package com.jimmy.im.server.socket 31 | * @version 1.0 32 | * 文件传输接收处理,单通道通信 33 | */ 34 | @Deprecated 35 | public class FileHandlerSocket extends Thread { 36 | private static final String TAG = FileHandlerSocket.class.getSimpleName(); 37 | 38 | public static final int TYPE_SEND = 1; 39 | public static final int TYPE_RECEIVE = 2; 40 | 41 | private Socket mSocket; 42 | 43 | private VoiceMsgEntity latestEntity; 44 | 45 | private int mType; 46 | 47 | public FileHandlerSocket(Socket socket, int type) { 48 | this.mSocket = socket; 49 | this.mType = type; 50 | } 51 | 52 | public void run() { 53 | 54 | if (mSocket == null) 55 | return; 56 | 57 | InputStream mInputString = null; 58 | OutputStream mOutputString = null; 59 | DataInputStream mDataInputStream = null; 60 | DataOutputStream dataOutputStream = null; 61 | 62 | try { 63 | mSocket.setKeepAlive(true); 64 | mInputString = mSocket.getInputStream(); 65 | mOutputString = mSocket.getOutputStream(); 66 | // 1、访问Socket对象的getInputStream方法取得客户端发送过来的数据流 67 | mDataInputStream = new DataInputStream(new BufferedInputStream( 68 | mInputString)); 69 | dataOutputStream = new DataOutputStream(new BufferedOutputStream( 70 | mOutputString)); 71 | 72 | while (true) { 73 | 74 | try { 75 | Thread.sleep(1000); 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } 79 | 80 | if(mSocket == null || mSocket.isClosed()){ 81 | Log.i(TAG, "run() -> socket close"); 82 | break; 83 | } 84 | 85 | if(mType == TYPE_SEND){ 86 | // 发送文件 87 | doSend(dataOutputStream); 88 | 89 | }else if(mType == TYPE_RECEIVE){ 90 | 91 | if(mSocket.isInputShutdown()){ 92 | Log.i(TAG, "run() -> socket input shutdown"); 93 | continue; 94 | } 95 | 96 | // 接收文件 97 | doReceive(mDataInputStream); 98 | } 99 | 100 | } 101 | 102 | } catch (SocketException e1) { 103 | e1.printStackTrace(); 104 | } catch (IOException e1) { 105 | e1.printStackTrace(); 106 | }finally{ 107 | close(); 108 | } 109 | 110 | } 111 | 112 | /** 113 | * 接收文件处理 114 | * @param dis 115 | */ 116 | private void doReceive(DataInputStream dis) { 117 | if (dis == null) { 118 | return; 119 | } 120 | Log.i(TAG, "doReceive()"); 121 | BufferedOutputStream fo = null; 122 | 123 | try { 124 | Log.i(TAG, "doReceive() -> before readUTF()"); 125 | VoiceMsgEntity entity = new VoiceMsgEntity(); 126 | // 文件名 127 | entity.fileName = dis.readUTF(); 128 | 129 | Log.i(TAG, "doReceive() -> after readUTF()"); 130 | 131 | // 存储路径 132 | if (TextUtils.isEmpty(entity.fileName)) { 133 | // fileName = System.currentTimeMillis() + ""; 134 | Log.e(TAG, "run() -> name is null"); 135 | return; 136 | } 137 | 138 | Log.i(TAG, "run() -> 文件名: " + entity.fileName); 139 | 140 | entity.size = dis.readLong(); 141 | if(entity.size <= 0){ 142 | Log.e(TAG, "run() -> 文件大小为0"); 143 | return; 144 | } 145 | Log.i(TAG, "run() -> 文件大小: " + entity.size); 146 | 147 | entity.time = dis.readInt(); 148 | 149 | entity.filePath = CommonUtil.getAmrFilePath(entity.fileName); 150 | 151 | Log.i(TAG, "run() -> 保存路径:" + entity.filePath); 152 | // 创建目录 153 | CreateDir(entity.filePath); 154 | 155 | // 2、将数据流写到文件中 156 | fo = new BufferedOutputStream(new FileOutputStream(new File( 157 | entity.filePath))); 158 | 159 | int bytesRead = 0; 160 | byte[] buffer = new byte[2048]; 161 | int writeLens = 0; 162 | while ((bytesRead = dis.read(buffer, 0, buffer.length)) != -1) { 163 | writeLens += bytesRead; 164 | Log.i(TAG, "doReceive() -> 接收文件完成,文件长度:" + writeLens); 165 | fo.write(buffer, 0, bytesRead); 166 | //如果文件读取完,退出循环,避免阻塞 167 | if(writeLens >= entity.size){ 168 | break; 169 | } 170 | } 171 | fo.flush(); 172 | 173 | Log.i(TAG, "doReceive() -> 数据接收完毕"); 174 | 175 | EventBus.getDefault().post(entity); 176 | 177 | } catch (FileNotFoundException e) { 178 | e.printStackTrace(); 179 | } catch (IOException e) { 180 | // e.printStackTrace(); 181 | Log.i(TAG, "doReceive() -> IOException error"); 182 | } finally { 183 | try { 184 | if (fo != null) { 185 | fo.close(); 186 | } 187 | } catch (IOException e) { 188 | e.printStackTrace(); 189 | } 190 | } 191 | } 192 | 193 | /** 194 | * 发送文件处理 195 | * @param dos 196 | */ 197 | private void doSend(DataOutputStream dos) { 198 | if (dos == null) { 199 | return; 200 | } 201 | 202 | if (newSendRequest()) { 203 | String name = latestEntity.fileName; 204 | Log.i(TAG, "doSend() -> new file name: " + name); 205 | 206 | File file = new File(latestEntity.filePath); 207 | if(!file.exists()){ 208 | return; 209 | } 210 | 211 | FileInputStream reader = null; 212 | byte[] buf = null; 213 | 214 | try { 215 | // 1. 读取文件输入流 216 | reader = new FileInputStream(file); 217 | // 2. 将文件内容写到Socket的输出流中 218 | // out.writeInt(UPLOAD); 219 | Log.i(TAG, "doSend() -> before writeUTF()"); 220 | dos.writeUTF(name); 221 | Log.i(TAG, "doSend() -> after writeUTF()"); 222 | dos.flush(); 223 | dos.writeLong(file.length()); 224 | dos.flush(); 225 | dos.writeInt(latestEntity.time); 226 | dos.flush(); 227 | 228 | int bufferSize = 20480; // 20K 229 | buf = new byte[bufferSize]; 230 | int read = 0; 231 | // 将文件输入流 循环 读入 Socket的输出流中 232 | while ((read = reader.read(buf, 0, buf.length)) != -1) { 233 | dos.write(buf, 0, read); 234 | } 235 | // dos.write(null); 236 | Log.i(TAG, "socket执行完成"); 237 | dos.flush(); 238 | // mSocket.shutdownOutput(); 239 | } catch (FileNotFoundException e) { 240 | e.printStackTrace(); 241 | } catch (IOException e) { 242 | e.printStackTrace(); 243 | } finally { 244 | try { 245 | if (reader != null) { 246 | reader.close(); 247 | } 248 | } catch (IOException e) { 249 | e.printStackTrace(); 250 | } 251 | } 252 | } 253 | 254 | } 255 | 256 | private boolean newSendRequest() { 257 | VoiceMsgEntity entity = (VoiceMsgEntity) EventBus.getDefault().getStickyEvent( 258 | VoiceMsgEntity.class); 259 | if (entity == null) { 260 | return false; 261 | } 262 | 263 | File file = new File(entity.filePath); 264 | 265 | if(!file.exists()){ 266 | return false; 267 | } 268 | 269 | if (latestEntity == entity) { 270 | return false; 271 | } 272 | 273 | latestEntity = entity; 274 | 275 | return true; 276 | } 277 | 278 | private void close() { 279 | try { 280 | if (mSocket != null) { 281 | mSocket.close(); 282 | } 283 | } catch (IOException e) { 284 | Log.e(TAG, "run() -> io error"); 285 | } 286 | } 287 | 288 | // 创建目录(不存在则创建) 289 | public File CreateDir(String dir) { 290 | Log.i(TAG, "CreateDir()"); 291 | File file = new File(dir); 292 | if (!file.getParentFile().exists()) { 293 | file.getParentFile().mkdirs(); 294 | } 295 | return file; 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/MsgParam.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | 4 | import com.speakin.recorder.module.control.socket.data.MsgEntity; 5 | 6 | public class MsgParam { 7 | 8 | private MsgEntity mMsgEntity; 9 | 10 | public void setMsgEntity(MsgEntity entity){ 11 | this.mMsgEntity = entity; 12 | } 13 | 14 | public MsgEntity getMsgEntity(){ 15 | return mMsgEntity; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/MsgReceiveHandler.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.net.Socket; 12 | import java.net.SocketException; 13 | 14 | import android.text.TextUtils; 15 | import android.util.Log; 16 | 17 | import com.speakin.recorder.module.control.socket.CommonUtil; 18 | import com.speakin.recorder.module.control.socket.data.MsgEntity; 19 | import com.speakin.recorder.module.control.socket.data.TextMsgEntity; 20 | import com.speakin.recorder.module.control.socket.data.VoiceMsgEntity; 21 | 22 | import org.greenrobot.eventbus.EventBus; 23 | 24 | 25 | /** 26 | * 27 | * 双通道通信(接收和发送在不同的线程) 28 | */ 29 | public class MsgReceiveHandler extends Thread { 30 | 31 | private static final String TAG = MsgReceiveHandler.class.getSimpleName(); 32 | 33 | private InputStream mInputString = null; 34 | private DataInputStream mDataInputStream = null; 35 | private volatile boolean isCancel = false; 36 | 37 | private Socket mSocket; 38 | 39 | public MsgReceiveHandler(Socket socket) { 40 | this.mSocket = socket; 41 | } 42 | 43 | public void run() { 44 | 45 | try { 46 | mInputString = mSocket.getInputStream(); 47 | // 1、访问Socket对象的getInputStream方法取得客户端发送过来的数据流 48 | mDataInputStream = new DataInputStream(new BufferedInputStream( 49 | mInputString)); 50 | 51 | while (!isCancel) { 52 | 53 | try { 54 | Thread.sleep(1000); 55 | } catch (InterruptedException e) { 56 | e.printStackTrace(); 57 | } 58 | 59 | if (mSocket == null || mDataInputStream == null) { 60 | return; 61 | } 62 | 63 | Log.i(TAG, "before readInt"); 64 | 65 | int type = mDataInputStream.readInt(); 66 | 67 | Log.i(TAG, "run() -> type: " + type); 68 | 69 | Log.i(TAG, "after readInt"); 70 | 71 | if (type == MsgEntity.TYPE_VOICE) { 72 | // 接收文件 73 | doFileReceive(mDataInputStream); 74 | } else if(type == MsgEntity.TYPE_TEXT){ 75 | doTextReceive(mDataInputStream); 76 | } 77 | 78 | } 79 | 80 | } catch (SocketException e1) { 81 | e1.printStackTrace(); 82 | } catch (IOException e1) { 83 | e1.printStackTrace(); 84 | } 85 | 86 | } 87 | 88 | public void stopMsgRecevie() { 89 | isCancel = true; 90 | } 91 | 92 | /** 93 | * 接收文本信息 94 | * @param dis 95 | * @throws IOException 96 | */ 97 | private void doTextReceive(DataInputStream dis) throws IOException { 98 | String msg = null; 99 | int length = (int)dis.readInt(); 100 | Log.i(TAG, "doTextReceive() -> length: " + length); 101 | byte[] buffer = new byte[length]; 102 | dis.read(buffer, 0, buffer.length); 103 | msg = new String(buffer); 104 | Log.i(TAG, "doTextReceive() -> msg: " + msg); 105 | if (!TextUtils.isEmpty(msg.trim())) { 106 | TextMsgEntity entity = new TextMsgEntity(); 107 | entity.msgContent = msg; 108 | EventBus.getDefault().post(entity); 109 | } 110 | } 111 | 112 | /** 113 | * 接收文件处理 114 | * 115 | * @param dis 116 | */ 117 | private void doFileReceive(DataInputStream dis){ 118 | 119 | if (dis == null) { 120 | return; 121 | } 122 | Log.i(TAG, "doReceive()"); 123 | BufferedOutputStream fo = null; 124 | 125 | try { 126 | Log.i(TAG, "doReceive() -> before readUTF()"); 127 | VoiceMsgEntity entity = new VoiceMsgEntity(); 128 | // 文件名 129 | entity.fileName = dis.readUTF(); 130 | 131 | Log.i(TAG, "doReceive() -> after readUTF()"); 132 | 133 | // 存储路径 134 | if (TextUtils.isEmpty(entity.fileName)) { 135 | // fileName = System.currentTimeMillis() + ""; 136 | Log.e(TAG, "run() -> name is null"); 137 | return; 138 | } 139 | 140 | Log.i(TAG, "run() -> 文件名: " + entity.fileName); 141 | 142 | entity.size = dis.readLong(); 143 | if (entity.size <= 0) { 144 | Log.e(TAG, "run() -> 文件大小为0"); 145 | return; 146 | } 147 | Log.i(TAG, "run() -> 文件大小: " + entity.size); 148 | 149 | entity.time = dis.readInt(); 150 | 151 | entity.filePath = CommonUtil.getAmrFilePath(entity.fileName); 152 | 153 | Log.i(TAG, "run() -> 保存路径:" + entity.filePath); 154 | // 创建目录 155 | boolean isCreateSuccess = CommonUtil.CreateDir(entity.filePath); 156 | if(isCreateSuccess){ 157 | // 将数据流写到文件中,如果创建目录失败,为了不破坏数据流机构,需要读取但是不写入 158 | fo = new BufferedOutputStream(new FileOutputStream(new File( 159 | entity.filePath))); 160 | }else{ 161 | Log.e(TAG, "create dir error " + entity.filePath); 162 | } 163 | 164 | int bytesRead = 0; 165 | byte[] buffer = new byte[2048]; 166 | int writeLens = 0; 167 | while ((bytesRead = dis.read(buffer, 0, buffer.length)) != -1) { 168 | writeLens += bytesRead; 169 | Log.i(TAG, "doReceive() -> 接收文件完成,文件长度:" + writeLens); 170 | if(fo != null){ 171 | fo.write(buffer, 0, bytesRead); 172 | } 173 | // 如果文件读取完,退出循环,避免阻塞 174 | if (writeLens >= entity.size) { 175 | break; 176 | } 177 | } 178 | if(fo != null){ 179 | fo.flush(); 180 | 181 | Log.i(TAG, "doReceive() -> 数据接收完毕"); 182 | 183 | EventBus.getDefault().post(entity); 184 | } 185 | 186 | } catch (FileNotFoundException e) { 187 | e.printStackTrace(); 188 | } catch (IOException e) { 189 | // e.printStackTrace(); 190 | Log.i(TAG, "doReceive() -> IOException error"); 191 | } finally { 192 | try { 193 | if (fo != null) { 194 | fo.close(); 195 | } 196 | } catch (IOException e) { 197 | e.printStackTrace(); 198 | } 199 | } 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/MsgRequest.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | /** 4 | * @author keshuangjie 5 | * @date 2014-12-4 下午7:41:14 6 | * @package com.jimmy.im.client.socket 7 | * @version 1.0 8 | * 消息发送请求 9 | */ 10 | public class MsgRequest { 11 | 12 | private MsgParam mParam; 13 | private SendCallback mSendCallBack; 14 | 15 | public MsgRequest(MsgParam param, SendCallback callBack){ 16 | this.mParam = param; 17 | this.mSendCallBack = callBack; 18 | } 19 | 20 | public SendCallback getSendCallBack() { 21 | return mSendCallBack; 22 | } 23 | 24 | public void setSendCallBack(SendCallback mSendCallBack) { 25 | this.mSendCallBack = mSendCallBack; 26 | } 27 | 28 | public void setMsgParam(MsgParam param){ 29 | this.mParam = param; 30 | } 31 | 32 | public MsgParam getMsgParam(){ 33 | return mParam; 34 | } 35 | 36 | public interface SendCallback{ 37 | void onFinish(); 38 | 39 | void onError(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/MsgSendHandler.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.net.Socket; 10 | import java.net.SocketException; 11 | 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | 15 | import com.speakin.recorder.module.control.socket.data.MsgEntity; 16 | import com.speakin.recorder.module.control.socket.data.TextMsgEntity; 17 | import com.speakin.recorder.module.control.socket.data.VoiceMsgEntity; 18 | 19 | 20 | public class MsgSendHandler extends Thread { 21 | 22 | private static final String TAG = MsgSendHandler.class.getSimpleName(); 23 | 24 | private Socket mSocket; 25 | private OutputStream mOutputString = null; 26 | private DataOutputStream mDataOutputStream = null; 27 | private volatile boolean isCancel = false; 28 | 29 | /** 会话唯一标识 */ 30 | // private String mKey; 31 | 32 | public MsgSendHandler(Socket socket) { 33 | this.mSocket = socket; 34 | } 35 | 36 | public void run() { 37 | 38 | try { 39 | mOutputString = mSocket.getOutputStream(); 40 | mDataOutputStream = new DataOutputStream(new BufferedOutputStream( 41 | mOutputString)); 42 | 43 | while (!isCancel) { 44 | 45 | try { 46 | Thread.sleep(1000); 47 | } catch (InterruptedException e) { 48 | e.printStackTrace(); 49 | } 50 | 51 | MsgRequest request = RequestQueueManager.getInstance().poll(); 52 | if(request == null){ 53 | continue; 54 | } 55 | MsgRequest.SendCallback callback = request.getSendCallBack(); 56 | MsgParam param = request.getMsgParam(); 57 | MsgEntity entity = param.getMsgEntity(); 58 | 59 | if(entity == null){ 60 | continue; 61 | } 62 | 63 | if(entity instanceof VoiceMsgEntity){ 64 | //发送语音消息 65 | doFileSend((VoiceMsgEntity) entity, callback); 66 | }else if(entity instanceof TextMsgEntity){ 67 | //发送文字消息 68 | doTextSend((TextMsgEntity) entity, callback); 69 | } 70 | } 71 | 72 | } catch (SocketException e1) { 73 | e1.printStackTrace(); 74 | } catch (IOException e1) { 75 | e1.printStackTrace(); 76 | } 77 | 78 | } 79 | 80 | public void stopSendMsg() { 81 | isCancel = true; 82 | } 83 | 84 | /** 85 | * 发送文件处理 86 | * 87 | * @param dos 88 | */ 89 | private void doFileSend(VoiceMsgEntity entity, MsgRequest.SendCallback callback) { 90 | if (mDataOutputStream == null || entity == null) { 91 | return; 92 | } 93 | 94 | String name = entity.fileName; 95 | Log.i(TAG, "doSend() -> new file name: " + name); 96 | 97 | File file = new File(entity.filePath); 98 | if (!file.exists()) { 99 | return; 100 | } 101 | 102 | FileInputStream reader = null; 103 | byte[] buf = null; 104 | 105 | try { 106 | 107 | //写入类型:语音 108 | mDataOutputStream.writeInt(MsgEntity.TYPE_VOICE); 109 | mDataOutputStream.flush(); 110 | 111 | // 1. 读取文件输入流 112 | reader = new FileInputStream(file); 113 | // 2. 将文件内容写到Socket的输出流中 114 | // out.writeInt(UPLOAD); 115 | Log.i(TAG, "doSend() -> before writeUTF()"); 116 | mDataOutputStream.writeUTF(name); 117 | Log.i(TAG, "doSend() -> after writeUTF()"); 118 | mDataOutputStream.flush(); 119 | mDataOutputStream.writeLong(file.length()); 120 | mDataOutputStream.flush(); 121 | mDataOutputStream.writeInt(entity.time); 122 | mDataOutputStream.flush(); 123 | 124 | int bufferSize = 20480; // 20K 125 | buf = new byte[bufferSize]; 126 | int read = 0; 127 | // 将文件输入流 循环 读入 Socket的输出流中 128 | while ((read = reader.read(buf, 0, buf.length)) != -1) { 129 | mDataOutputStream.write(buf, 0, read); 130 | } 131 | Log.i(TAG, "socket执行完成"); 132 | mDataOutputStream.flush(); 133 | 134 | if(callback != null){ 135 | callback.onFinish(); 136 | } 137 | 138 | reader.close(); 139 | 140 | } catch (IOException e) { 141 | // e.printStackTrace(); 142 | 143 | if(callback != null){ 144 | callback.onError(); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * 发送文本消息 151 | * @param entity 152 | */ 153 | private void doTextSend(TextMsgEntity entity, MsgRequest.SendCallback callback) { 154 | if (mDataOutputStream == null || entity == null || TextUtils.isEmpty(entity.msgContent)) { 155 | return; 156 | } 157 | 158 | Log.i(TAG, "send to client: " + entity.msgContent); 159 | try { 160 | 161 | byte[] buffer = entity.msgContent.getBytes(); 162 | 163 | //写入类型:文本 164 | mDataOutputStream.writeInt(MsgEntity.TYPE_TEXT); 165 | mDataOutputStream.flush(); 166 | 167 | mDataOutputStream.writeInt(buffer.length); 168 | mDataOutputStream.flush(); 169 | 170 | mDataOutputStream.write(buffer); 171 | mDataOutputStream.flush(); 172 | 173 | if(callback != null){ 174 | callback.onFinish(); 175 | } 176 | 177 | } catch (IOException e) { 178 | // e.printStackTrace(); 179 | 180 | if(callback != null){ 181 | callback.onError(); 182 | } 183 | } 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/RequestQueueManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | import java.util.concurrent.LinkedBlockingQueue; 4 | 5 | /** 6 | * 7 | * 待发送消息队列 8 | */ 9 | public class RequestQueueManager { 10 | 11 | LinkedBlockingQueue mQueueList; 12 | 13 | private static final class Holder{ 14 | public static final RequestQueueManager sINSTANCE = new RequestQueueManager(); 15 | } 16 | 17 | private RequestQueueManager(){ 18 | mQueueList = new LinkedBlockingQueue(); 19 | } 20 | 21 | public static RequestQueueManager getInstance(){ 22 | return Holder.sINSTANCE; 23 | } 24 | 25 | public MsgRequest poll(){ 26 | MsgRequest entity = null; 27 | if(mQueueList != null){ 28 | entity = mQueueList.poll(); 29 | } 30 | return entity; 31 | } 32 | 33 | public void push(MsgRequest entity){ 34 | try { 35 | if(mQueueList != null){ 36 | mQueueList = new LinkedBlockingQueue(); 37 | } 38 | mQueueList.put(entity); 39 | } catch (InterruptedException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/socket/handle/TextHandlerSocket.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.socket.handle; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.net.SocketException; 8 | 9 | import android.text.TextUtils; 10 | import android.util.Log; 11 | 12 | import com.speakin.recorder.module.control.socket.data.TextMsgEntity; 13 | 14 | import org.greenrobot.eventbus.EventBus; 15 | 16 | 17 | /** 18 | * @author keshuangjie 19 | * @date 2014-12-1 下午7:46:21 20 | * @package com.jimmy.im.server.socket 21 | * @version 1.0 22 | * 字符串传输接收处理,双通道,但只能处理文本传输 23 | */ 24 | @Deprecated 25 | public class TextHandlerSocket extends Thread { 26 | private static final String TAG = TextHandlerSocket.class.getSimpleName(); 27 | 28 | private Socket mSocket; 29 | 30 | TextMsgEntity latestEntity; 31 | 32 | InputStream in = null; 33 | OutputStream out = null; 34 | 35 | public TextHandlerSocket(Socket socket) { 36 | mSocket = socket; 37 | } 38 | 39 | @Override 40 | public void run() { 41 | 42 | try { 43 | mSocket.setKeepAlive(true); 44 | in = mSocket.getInputStream(); 45 | out = mSocket.getOutputStream(); 46 | 47 | // 使用循环的方式,不停的与客户端交互会话 48 | while (true) { 49 | try { 50 | Thread.sleep(1000); 51 | } catch (InterruptedException e) { 52 | e.printStackTrace(); 53 | } 54 | 55 | if (mSocket != null && mSocket.isConnected()) { 56 | // 发送数据回客户端 57 | doSend(out); 58 | // 处理客户端发来的数据 59 | doReceive(in); 60 | } 61 | } 62 | } catch (SocketException e1) { 63 | e1.printStackTrace(); 64 | } catch (IOException e1) { 65 | e1.printStackTrace(); 66 | } finally { 67 | try { 68 | if (in != null) { 69 | in.close(); 70 | } 71 | if (out != null) { 72 | out.close(); 73 | } 74 | if (mSocket != null) { 75 | mSocket.close(); 76 | } 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | 83 | private void doReceive(InputStream in) { 84 | String msg = null; 85 | try { 86 | byte[] bytes = new byte[in.available()]; 87 | in.read(bytes); 88 | msg = new String(bytes); 89 | if (!TextUtils.isEmpty(msg.trim())) { 90 | TextMsgEntity entity = new TextMsgEntity(); 91 | entity.msgContent = msg; 92 | EventBus.getDefault().post(entity); 93 | } 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | private void doSend(OutputStream out) { 100 | TextMsgEntity entity = (TextMsgEntity) EventBus.getDefault() 101 | .getStickyEvent(TextMsgEntity.class); 102 | if (entity == null || TextUtils.isEmpty(entity.msgContent)) { 103 | return; 104 | } 105 | if (latestEntity == entity) { 106 | return; 107 | } 108 | latestEntity = entity; 109 | Log.i(TAG, "send to client: " + latestEntity.msgContent); 110 | try { 111 | out.write(latestEntity.msgContent.getBytes()); 112 | out.flush(); 113 | } catch (IOException e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/websocket/MasterWebSocketManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.websocket; 2 | 3 | import android.util.Log; 4 | 5 | import com.koushikdutta.async.AsyncNetworkSocket; 6 | import com.koushikdutta.async.ByteBufferList; 7 | import com.koushikdutta.async.DataEmitter; 8 | import com.koushikdutta.async.callback.CompletedCallback; 9 | import com.koushikdutta.async.callback.DataCallback; 10 | import com.koushikdutta.async.http.WebSocket; 11 | import com.koushikdutta.async.http.server.AsyncHttpServer; 12 | import com.koushikdutta.async.http.server.AsyncHttpServerRequest; 13 | import com.speakin.recorder.module.control.ControlConstants; 14 | 15 | import java.io.File; 16 | import java.io.FileNotFoundException; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Copyright 2017 SpeakIn.Inc 24 | * Created by west on 2017/9/28. 25 | */ 26 | 27 | public class MasterWebSocketManager { 28 | 29 | public interface MasterSocketManagerCallback { 30 | void onServerError(Exception ex); 31 | void onClientConnected(WebSocket clientSocket); 32 | void onClientDisconnect(WebSocket clientSocket); 33 | void onMessageReceive(WebSocket clientSocket, String message); 34 | void onFileReceive(WebSocket clientSocket, String filePath); 35 | } 36 | 37 | 38 | private static final String TAG = MasterWebSocketManager.class.getSimpleName(); 39 | private int clientMaxCount = 3; 40 | 41 | private AsyncHttpServer server = new AsyncHttpServer(); 42 | private final List socketClients = new ArrayList<>(); 43 | private MasterSocketManagerCallback masterSocketManagerCallback; 44 | 45 | public MasterWebSocketManager(int count) { 46 | clientMaxCount = count; 47 | } 48 | 49 | public void setMasterSocketManager(MasterSocketManagerCallback callback) { 50 | masterSocketManagerCallback = callback; 51 | } 52 | 53 | public void start() { 54 | server.websocket("/", ControlConstants.PROTOCOL, callback); 55 | server.listen(ControlConstants.SERVER_SOCKET_PORT); 56 | server.setErrorCallback(new CompletedCallback() { 57 | @Override 58 | public void onCompleted(Exception ex) { 59 | if (ex != null) { 60 | ex.printStackTrace(); 61 | Log.e(TAG, "Error"); 62 | if (masterSocketManagerCallback != null) masterSocketManagerCallback.onServerError(ex); 63 | } 64 | } 65 | }); 66 | Log.d(TAG, "start server and listen"); 67 | } 68 | 69 | public void stop() { 70 | Log.d(TAG, "stop"); 71 | 72 | for (SlaveClientSocket clientSocket : socketClients) { 73 | clientSocket.mSocket.close(); 74 | } 75 | socketClients.clear(); 76 | 77 | if (server != null) { 78 | server.stop(); 79 | } 80 | } 81 | 82 | public void sendMessage(String message) { 83 | for (SlaveClientSocket clientSocket : socketClients) { 84 | clientSocket.mSocket.send(message); 85 | } 86 | } 87 | 88 | public int getClientCount() { 89 | return socketClients.size(); 90 | } 91 | 92 | private AsyncHttpServer.WebSocketRequestCallback callback = new AsyncHttpServer.WebSocketRequestCallback() { 93 | @Override 94 | public void onConnected(final WebSocket webSocket, AsyncHttpServerRequest request) { 95 | Log.d(TAG, "request" + request.toString()); 96 | AsyncNetworkSocket asyncNetworkSocket = (AsyncNetworkSocket) webSocket.getSocket(); 97 | Log.d(TAG, asyncNetworkSocket.getRemoteAddress().getAddress() + ":" + asyncNetworkSocket.getRemoteAddress().getPort() + " connected"); 98 | if (socketClients.size() >= clientMaxCount) { 99 | webSocket.close(); 100 | return; 101 | } 102 | 103 | SlaveClientSocket found = null; 104 | for (SlaveClientSocket ws : socketClients ) { 105 | AsyncNetworkSocket anws = (AsyncNetworkSocket) ws.mSocket.getSocket(); 106 | if (asyncNetworkSocket.getRemoteAddress().getHostName().equals(anws.getRemoteAddress().getHostName())) { 107 | found = ws; 108 | break; 109 | } 110 | } 111 | if (found == null) { 112 | socketClients.add(new SlaveClientSocket(webSocket)); 113 | if (masterSocketManagerCallback != null) 114 | masterSocketManagerCallback.onClientConnected(webSocket); 115 | } else { 116 | Log.d(TAG, "slave already connect"); 117 | } 118 | } 119 | }; 120 | 121 | 122 | private class SlaveClientSocket implements CompletedCallback, WebSocket.StringCallback, DataCallback { 123 | WebSocket mSocket; 124 | 125 | SlaveClientSocket(WebSocket clientSocket) { 126 | mSocket = clientSocket; 127 | mSocket.setStringCallback(this); 128 | mSocket.setClosedCallback(this); 129 | mSocket.setDataCallback(this); 130 | } 131 | 132 | @Override 133 | public void onCompleted(Exception ex) { 134 | try { 135 | if (ex != null) { 136 | Log.e(TAG, "Error"); 137 | ex.printStackTrace(); 138 | } else { 139 | Log.d(TAG, "WebSocketClient closed normally."); 140 | } 141 | } finally { 142 | socketClients.remove(this); 143 | if (masterSocketManagerCallback != null) 144 | masterSocketManagerCallback.onClientDisconnect(mSocket); 145 | } 146 | } 147 | 148 | @Override 149 | public void onStringAvailable(String s) { 150 | AsyncNetworkSocket asyncNetworkSocket = (AsyncNetworkSocket) mSocket.getSocket(); 151 | Log.d(TAG, "from:" + asyncNetworkSocket.getRemoteAddress().getAddress() + ":" + asyncNetworkSocket.getRemoteAddress().getPort()); 152 | Log.d(TAG, "msg = " + s); 153 | if (masterSocketManagerCallback != null) { 154 | masterSocketManagerCallback.onMessageReceive(mSocket, s); 155 | } 156 | } 157 | 158 | private static final int BUFFER_LEN = 1024*8; 159 | @Override 160 | public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { 161 | Log.d(TAG, "onDataAvailable"); 162 | String filePath = getFilePath(); 163 | try { 164 | FileOutputStream outputStream = new FileOutputStream(new File(filePath)); 165 | long start = System.currentTimeMillis(); 166 | while (bb.hasRemaining()) { 167 | int remain = bb.remaining(); 168 | if (remain > BUFFER_LEN) { 169 | outputStream.write(bb.getBytes(BUFFER_LEN)); 170 | } else { 171 | outputStream.write(bb.getBytes(remain)); 172 | } 173 | } 174 | outputStream.flush(); 175 | outputStream.close(); 176 | Log.d(TAG, "receive file:" + filePath); 177 | Log.d(TAG, "spend " + (System.currentTimeMillis() - start)); 178 | if (masterSocketManagerCallback != null) { 179 | masterSocketManagerCallback.onFileReceive(mSocket, filePath); 180 | } 181 | } catch (FileNotFoundException e) { 182 | e.printStackTrace(); 183 | } catch (IOException e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | 188 | } 189 | 190 | private String getFilePath() { 191 | String fileName = System.currentTimeMillis() + ".wav"; 192 | String filePath = ControlConstants.RECEIVE_DIR; 193 | boolean ret = new File(filePath).mkdirs(); 194 | if (!ret) { 195 | // Log.e(TAG, "make dir error"); 196 | } 197 | return filePath+fileName; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/module/control/websocket/SlaveWebSocketManager.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.module.control.websocket; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | import com.koushikdutta.async.ByteBufferList; 7 | import com.koushikdutta.async.DataEmitter; 8 | import com.koushikdutta.async.callback.CompletedCallback; 9 | import com.koushikdutta.async.callback.DataCallback; 10 | import com.koushikdutta.async.http.AsyncHttpClient; 11 | import com.koushikdutta.async.http.WebSocket; 12 | import com.speakin.recorder.module.control.ControlConstants; 13 | 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.FileNotFoundException; 18 | import java.io.IOException; 19 | 20 | /** 21 | * Copyright 2017 SpeakIn.Inc 22 | * Created by west on 2017/9/29. 23 | */ 24 | 25 | public class SlaveWebSocketManager { 26 | 27 | private static final String TAG = SlaveWebSocketManager.class.getSimpleName(); 28 | 29 | public interface SlaveSocketManagerCallback { 30 | void onMasterConnected(WebSocket socket, Exception ex); 31 | void onMasterDisconnect(WebSocket socket, Exception ex); 32 | void onReceiveMessage(String message); 33 | } 34 | 35 | private SlaveSocketManagerCallback slaveSocketManagerCallback; 36 | private String serverIp; 37 | private int serverPort; 38 | private WebSocket socket; 39 | 40 | public SlaveWebSocketManager(String serverIp, int serverPort) { 41 | this.serverIp = serverIp; 42 | this.serverPort = serverPort; 43 | } 44 | 45 | public void setSlaveSocketManagerCallback(SlaveSocketManagerCallback callback) { 46 | slaveSocketManagerCallback = callback; 47 | } 48 | 49 | public void startConnect() { 50 | if (isConnected()) { 51 | return; 52 | } 53 | String url = "ws://" + serverIp + ":" + serverPort; 54 | Log.d(TAG, "start connect to url=" + url); 55 | AsyncHttpClient.getDefaultInstance().websocket(url, ControlConstants.PROTOCOL, new AsyncHttpClient.WebSocketConnectCallback() { 56 | @Override 57 | public void onCompleted(Exception ex, final WebSocket webSocket) { 58 | if (ex != null) { 59 | ex.printStackTrace(); 60 | Log.e(TAG, "connect error = " + ex.getMessage()); 61 | if (slaveSocketManagerCallback != null) slaveSocketManagerCallback.onMasterConnected(null, ex); 62 | return; 63 | } 64 | Log.d(TAG, "connected to server"); 65 | 66 | socket = webSocket; 67 | socket.setStringCallback(stringCallback); 68 | socket.setClosedCallback(closeCallback); 69 | socket.setDataCallback(dataCallback); 70 | socket.send("confirm from client"); 71 | 72 | if (slaveSocketManagerCallback != null) slaveSocketManagerCallback.onMasterConnected(socket, null); 73 | } 74 | }); 75 | } 76 | 77 | private WebSocket.StringCallback stringCallback = new WebSocket.StringCallback() { 78 | @Override 79 | public void onStringAvailable(String s) { 80 | Log.d(TAG, "msg from server: " + s); 81 | if (slaveSocketManagerCallback != null) slaveSocketManagerCallback.onReceiveMessage(s); 82 | } 83 | }; 84 | 85 | private DataCallback dataCallback = new DataCallback() { 86 | @Override 87 | public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { 88 | Log.d(TAG, "onDataAvailable"); 89 | } 90 | }; 91 | 92 | private CompletedCallback closeCallback = new CompletedCallback() { 93 | @Override 94 | public void onCompleted(Exception ex) { 95 | if (ex != null) { 96 | ex.printStackTrace(); 97 | Log.e(TAG, "close error = " + ex.getMessage()); 98 | } 99 | if (slaveSocketManagerCallback != null) slaveSocketManagerCallback.onMasterDisconnect(socket, ex); 100 | socket = null; 101 | } 102 | }; 103 | 104 | public boolean sendMessage(String message) { 105 | if (!isConnected()) { 106 | Log.d(TAG, "not connected"); 107 | return false; 108 | } 109 | if (TextUtils.isEmpty(message)) return false; 110 | 111 | socket.send(message); 112 | return true; 113 | } 114 | 115 | private static final int BUFFER_LEN = 8*1024; 116 | public void sendFile(final String filePath) { 117 | if (filePath == null || !(new File(filePath).exists())) return; 118 | new Thread(new Runnable() { 119 | @Override 120 | public void run() { 121 | Log.d(TAG, "send file " +filePath); 122 | long start = System.currentTimeMillis(); 123 | try { 124 | File file = new File(filePath); 125 | Log.d(TAG, "file len=" + file.length()); 126 | FileInputStream inputStream = new FileInputStream(file); 127 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 128 | byte[] buffer = new byte[BUFFER_LEN]; 129 | int read ; 130 | while ((read = inputStream.read(buffer)) != -1 ) { 131 | if (read == BUFFER_LEN) { 132 | baos.write(buffer); 133 | } else { 134 | byte[] bs2 = new byte[read]; 135 | System.arraycopy(buffer, 0, bs2, 0, read); 136 | baos.write(bs2); 137 | } 138 | } 139 | 140 | byte[] payload = baos.toByteArray(); 141 | 142 | socket.send(payload); 143 | Log.d(TAG, "send file done"); 144 | Log.d(TAG, "spend " + (System.currentTimeMillis() - start)); 145 | } catch (FileNotFoundException e) { 146 | e.printStackTrace(); 147 | } catch (IOException e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | }).start(); 152 | 153 | } 154 | 155 | public void stopConnect() { 156 | if (socket != null) { 157 | socket.close(); 158 | socket = null; 159 | } 160 | } 161 | 162 | public boolean isConnected() { 163 | return socket != null; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.ui; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | import android.widget.Toast; 8 | 9 | import com.speakin.recorder.R; 10 | import com.speakin.recorder.module.control.MasterControlManager; 11 | import com.speakin.recorder.module.control.SlaveControlManager; 12 | import com.speakin.recorder.utils.IpUtil; 13 | 14 | import org.json.JSONObject; 15 | 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | 20 | private TextView textView; 21 | private TextView textView2; 22 | 23 | private MasterControlManager masterControlManager; 24 | private SlaveControlManager slaveControlManager; 25 | private boolean isMaster = false; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | initData(); 32 | initView(); 33 | refreshIP(); 34 | } 35 | 36 | private void initData() { 37 | masterControlManager = new MasterControlManager(); 38 | slaveControlManager = new SlaveControlManager(); 39 | 40 | slaveControlManager.setControlManagerCallback(new SlaveControlManager.SlaveControlManagerCallback() { 41 | @Override 42 | public void onFoundMaster(String masterIp, JSONObject masterInfo) { 43 | textView2.setText(masterIp + " " + masterInfo.toString()); 44 | } 45 | 46 | @Override 47 | public void onConnectedMaster(String masterIp, Exception ex) { 48 | textView2.setText(masterIp + " connected"); 49 | } 50 | 51 | @Override 52 | public void onDisconnectMaster(String masterIp, Exception ex) { 53 | textView2.setText(masterIp + " disconnected"); 54 | } 55 | 56 | @Override 57 | public void onReceiveMessage(String message) { 58 | textView2.setText("message: " + message); 59 | } 60 | }); 61 | 62 | masterControlManager.setControlManagerCallback(new MasterControlManager.MasterControlManagerCallback() { 63 | @Override 64 | public void onServerError(Exception ex) { 65 | textView2.setText("server error: " + ex.getLocalizedMessage()); 66 | } 67 | 68 | @Override 69 | public void onClientConnected(String clientSocket) { 70 | textView2.setText("client: " + clientSocket); 71 | } 72 | 73 | @Override 74 | public void onClientDisconnect(String clientSocket) { 75 | textView2.setText("disconnected: " + clientSocket); 76 | } 77 | 78 | @Override 79 | public void onMessageReceive(String clientSocket, String message) { 80 | textView2.setText("message: " + message); 81 | } 82 | 83 | @Override 84 | public void onReceiveFile(String clientSocket, String filePath) { 85 | Toast.makeText(MainActivity.this, "receive file" + filePath, Toast.LENGTH_SHORT).show(); 86 | } 87 | }); 88 | } 89 | 90 | private void initView() { 91 | findViewById(R.id.masterBtn).setOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View view) { 94 | masterControlManager.start(); 95 | isMaster = true; 96 | } 97 | }); 98 | 99 | findViewById(R.id.slaveBtn).setOnClickListener(new View.OnClickListener() { 100 | @Override 101 | public void onClick(View view) { 102 | slaveControlManager.start(); 103 | } 104 | }); 105 | 106 | textView = (TextView) findViewById(R.id.text1); 107 | textView.setOnClickListener(new View.OnClickListener() { 108 | @Override 109 | public void onClick(View view) { 110 | refreshIP(); 111 | } 112 | }); 113 | 114 | textView2 = (TextView) findViewById(R.id.text2); 115 | 116 | findViewById(R.id.sendBtn).setOnClickListener(new View.OnClickListener() { 117 | @Override 118 | public void onClick(View view) { 119 | if (isMaster) { 120 | masterControlManager.send("Hello, I am server :" + System.currentTimeMillis()); 121 | } else { 122 | slaveControlManager.send("Hello, I am client :" + System.currentTimeMillis()); 123 | } 124 | } 125 | }); 126 | findViewById(R.id.stopBtn).setOnClickListener(new View.OnClickListener() { 127 | @Override 128 | public void onClick(View view) { 129 | if (isMaster) { 130 | masterControlManager.stop(); 131 | } else { 132 | slaveControlManager.stop(); 133 | } 134 | } 135 | }); 136 | findViewById(R.id.sendeFile).setOnClickListener(new View.OnClickListener() { 137 | @Override 138 | public void onClick(View view) { 139 | slaveControlManager.sendFile("/storage/emulated/0/wakeupIn/record/detected_1505991013.wav"); 140 | } 141 | }); 142 | } 143 | 144 | private void refreshIP() { 145 | String ip1 = IpUtil.getHostIP(); 146 | 147 | textView.setText("本机IP: " + ip1 ); 148 | } 149 | 150 | 151 | @Override 152 | protected void onDestroy() { 153 | super.onDestroy(); 154 | slaveControlManager.stop(); 155 | masterControlManager.stop(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/utils/DeviceUUID.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.telephony.TelephonyManager; 6 | import android.text.TextUtils; 7 | 8 | import java.util.UUID; 9 | 10 | import static android.text.TextUtils.isEmpty; 11 | 12 | /** 13 | * Copyright 2017 SpeakIn.Inc 14 | * Created by west on 2017/10/26. 15 | */ 16 | 17 | public class DeviceUUID { 18 | 19 | private static final String _UUID = "DeviceUUID"; 20 | private static final String KEY_UUID = "KEY_UUID"; 21 | 22 | public static String getDeviceUUID(Context context){ 23 | 24 | //IMEI(imei) 25 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 26 | String imei = tm.getDeviceId(); 27 | if(!isEmpty(imei) && !imei.startsWith("00000")){ 28 | return imei; 29 | } 30 | 31 | String sn = tm.getSimSerialNumber(); 32 | if(!isEmpty(sn) && !sn.startsWith("00000")){ 33 | return sn; 34 | } 35 | 36 | SharedPreferences preferences = context.getSharedPreferences(_UUID, Context.MODE_PRIVATE); 37 | String uuid = preferences.getString(KEY_UUID, ""); 38 | if (TextUtils.isEmpty(uuid)) { 39 | uuid = UUID.randomUUID().toString(); 40 | preferences.edit().putString(KEY_UUID,uuid).apply(); 41 | } 42 | return uuid; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/utils/DevicesInfo.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.telephony.TelephonyManager; 7 | 8 | import com.speakin.recorder.RecorderApp; 9 | 10 | 11 | public class DevicesInfo { 12 | 13 | private static DevicesInfo info = null; 14 | public String imei; 15 | public String deviceName; 16 | private String systemVer; 17 | 18 | public DevicesInfo(Context ctx) { 19 | imei = DeviceUUID.getDeviceUUID(ctx); 20 | deviceName = Build.MODEL; 21 | systemVer = Build.VERSION.SDK; 22 | } 23 | 24 | public static DevicesInfo getDeviceInfo() { 25 | if (info == null) { 26 | info = new DevicesInfo(RecorderApp.app); 27 | } 28 | return info; 29 | } 30 | 31 | public String getIMEI(){ 32 | return imei; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/utils/IpUtil.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.utils; 2 | 3 | import android.content.Context; 4 | import android.net.DhcpInfo; 5 | import android.net.wifi.WifiManager; 6 | import android.util.Log; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.net.Inet6Address; 11 | import java.net.InetAddress; 12 | import java.net.NetworkInterface; 13 | import java.net.SocketException; 14 | import java.util.Enumeration; 15 | 16 | /** 17 | * Copyright 2017 SpeakIn.Inc 18 | * Created by west on 2017/9/27. 19 | */ 20 | 21 | public class IpUtil { 22 | 23 | /** 24 | * 获取开启便携热点后自身热点IP地址 25 | * @param context 26 | * @return 27 | */ 28 | public static String getHotspotLocalIpAddress(Context context) { 29 | WifiManager wifimanager = (WifiManager) context.getSystemService(context.WIFI_SERVICE); 30 | DhcpInfo dhcpInfo = wifimanager.getDhcpInfo(); 31 | if(dhcpInfo != null) { 32 | int address = dhcpInfo.serverAddress; 33 | return ((address & 0xFF) 34 | + "." + ((address >> 8) & 0xFF) 35 | + "." + ((address >> 16) & 0xFF) 36 | + "." + ((address >> 24) & 0xFF)); 37 | } 38 | return null; 39 | } 40 | 41 | /** 42 | * 获取连接WiFi后的IP地址 43 | * @return 44 | */ 45 | public static String getIpAddressFromHotspot(Context context) { 46 | WifiManager wifimanager = (WifiManager) context.getSystemService(context.WIFI_SERVICE); 47 | DhcpInfo dhcpInfo = wifimanager.getDhcpInfo(); 48 | if(dhcpInfo != null) { 49 | int address = dhcpInfo.gateway; 50 | return ((address & 0xFF) 51 | + "." + ((address >> 8) & 0xFF) 52 | + "." + ((address >> 16) & 0xFF) 53 | + "." + ((address >> 24) & 0xFF)); 54 | } 55 | return null; 56 | } 57 | 58 | public static Boolean isWifiApEnabled(Context context) { 59 | try { 60 | WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); 61 | Method method = manager.getClass().getMethod("isWifiApEnabled"); 62 | return (Boolean)method.invoke(manager); 63 | } 64 | catch (NoSuchMethodException e) { 65 | e.printStackTrace(); 66 | } 67 | catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 68 | e.printStackTrace(); 69 | } 70 | return false; 71 | } 72 | 73 | /** 74 | * 获取ip地址 75 | * @return 76 | */ 77 | public static String getHostIP() { 78 | 79 | String hostIp = null; 80 | try { 81 | Enumeration nis = NetworkInterface.getNetworkInterfaces(); 82 | InetAddress ia = null; 83 | while (nis.hasMoreElements()) { 84 | NetworkInterface ni = (NetworkInterface) nis.nextElement(); 85 | Enumeration ias = ni.getInetAddresses(); 86 | while (ias.hasMoreElements()) { 87 | ia = ias.nextElement(); 88 | if (ia instanceof Inet6Address) { 89 | continue;// skip ipv6 90 | } 91 | String ip = ia.getHostAddress(); 92 | if (!"127.0.0.1".equals(ip)) { 93 | hostIp = ia.getHostAddress(); 94 | break; 95 | } 96 | } 97 | } 98 | } catch (SocketException e) { 99 | Log.i("yao", "SocketException"); 100 | e.printStackTrace(); 101 | } 102 | return hostIp; 103 | 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/speakin/recorder/utils/NetHelper.java: -------------------------------------------------------------------------------- 1 | package com.speakin.recorder.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.net.ConnectivityManager; 6 | import android.net.DhcpInfo; 7 | import android.net.NetworkInfo; 8 | import android.net.wifi.WifiManager; 9 | import android.os.Build; 10 | import android.telephony.TelephonyManager; 11 | 12 | import java.net.Inet4Address; 13 | import java.net.InetAddress; 14 | import java.net.NetworkInterface; 15 | import java.net.SocketException; 16 | import java.util.Enumeration; 17 | 18 | 19 | /** 20 | * 判断网络的的工具类 21 | * @author liuzongyao 22 | * 23 | */ 24 | public abstract class NetHelper 25 | { 26 | 27 | public static boolean isWifi(Context c) 28 | { 29 | boolean bRet = false; 30 | ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); 31 | if (cm != null) { 32 | NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 33 | if (null != wifiInfo && wifiInfo.isConnectedOrConnecting()) { 34 | bRet = true; 35 | } 36 | } 37 | return bRet; 38 | } 39 | 40 | public static boolean isWifiConnected(Context c) 41 | { 42 | boolean bRet = false; 43 | ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); 44 | if (cm != null) { 45 | NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 46 | if (null != wifiInfo && wifiInfo.isConnected()) { 47 | bRet = true; 48 | } 49 | } 50 | return bRet; 51 | } 52 | 53 | public static boolean isMobileNet(final Context c) 54 | { 55 | boolean ret = false; 56 | ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); 57 | if (null != cm) 58 | { 59 | NetworkInfo mobileInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 60 | if (null != mobileInfo && mobileInfo.isConnectedOrConnecting()) 61 | { 62 | ret = true; 63 | } 64 | } 65 | return ret; 66 | } 67 | 68 | public static boolean isNetworkAvailable(Context context) 69 | { 70 | Context ct = context.getApplicationContext(); 71 | ConnectivityManager cm = (ConnectivityManager) ct.getSystemService(Context.CONNECTIVITY_SERVICE); 72 | if (null == cm) 73 | { 74 | return false; 75 | } 76 | else 77 | { 78 | NetworkInfo[] info = cm.getAllNetworkInfo(); 79 | if (null != info) 80 | { 81 | for (int i = 0; i < info.length; i++) 82 | { 83 | if (info[i].getState() == NetworkInfo.State.CONNECTED) 84 | { 85 | return true; 86 | } 87 | } 88 | } 89 | } 90 | return false; 91 | } 92 | 93 | 94 | // 以网络编码格式返回,如 3gnet/3gwap/uninet/uniwap/cmnet/cmwap/ctnet/ctwap 95 | @SuppressLint("DefaultLocale") 96 | public static String getNetTypeName(Context context) 97 | { 98 | String typeName = "null"; 99 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 100 | NetworkInfo info = cm.getActiveNetworkInfo(); 101 | 102 | if (null == info) 103 | { 104 | typeName = "null"; 105 | } 106 | else if (info.getTypeName() != null) 107 | { 108 | typeName = info.getTypeName().toLowerCase(); // WIFI/MOBILE 109 | 110 | if (!typeName.equals("wifi")) 111 | { 112 | if (info.getExtraInfo() != null) 113 | { 114 | typeName = info.getExtraInfo().toLowerCase(); //3gnet/3gwap/uninet/uniwap/cmnet/cmwap/ctnet/ctwap 115 | } 116 | 117 | if (typeName.equals("#777") && info.getSubtypeName() != null) 118 | typeName = info.getSubtypeName(); 119 | } 120 | } 121 | 122 | return typeName; 123 | } 124 | 125 | // wuwenhua add: 以wifi、2g、3g、4g作为返回格式 126 | public static String getNetTypeNameEx(Context context) { 127 | 128 | String type = "null"; 129 | ConnectivityManager cm=(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); 130 | 131 | NetworkInfo mobileInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 132 | NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 133 | 134 | if(null != mobileInfo && mobileInfo.isConnected()) { 135 | TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); 136 | int netType = tm.getNetworkType(); 137 | switch (netType) { 138 | case TelephonyManager.NETWORK_TYPE_GPRS: 139 | case TelephonyManager.NETWORK_TYPE_EDGE: 140 | case TelephonyManager.NETWORK_TYPE_CDMA: 141 | case TelephonyManager.NETWORK_TYPE_1xRTT: 142 | case TelephonyManager.NETWORK_TYPE_IDEN: 143 | type = "2g"; 144 | break; 145 | case TelephonyManager.NETWORK_TYPE_UMTS: 146 | case TelephonyManager.NETWORK_TYPE_EVDO_0: 147 | case TelephonyManager.NETWORK_TYPE_EVDO_A: 148 | case TelephonyManager.NETWORK_TYPE_HSDPA: 149 | case TelephonyManager.NETWORK_TYPE_HSUPA: 150 | case TelephonyManager.NETWORK_TYPE_HSPA: 151 | case TelephonyManager.NETWORK_TYPE_EVDO_B: 152 | case TelephonyManager.NETWORK_TYPE_EHRPD: 153 | case TelephonyManager.NETWORK_TYPE_HSPAP: 154 | type = "3g"; 155 | break; 156 | case TelephonyManager.NETWORK_TYPE_LTE: 157 | type = "4g"; 158 | break; 159 | default: 160 | type = "null"; 161 | break; 162 | } 163 | 164 | } 165 | if(null != wifiInfo && wifiInfo.isConnected()) { 166 | type = "wifi"; 167 | } 168 | return type; 169 | } 170 | 171 | 172 | public static int getNetType(Context context) 173 | { 174 | int type = -1; 175 | 176 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 177 | 178 | NetworkInfo mobileInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 179 | NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 180 | 181 | if (null != mobileInfo && mobileInfo.isConnected()) 182 | { 183 | type = ConnectivityManager.TYPE_MOBILE; 184 | } 185 | if (null != wifiInfo && wifiInfo.isConnected()) 186 | { 187 | type = ConnectivityManager.TYPE_WIFI; 188 | } 189 | 190 | return type; 191 | } 192 | 193 | public static int getCurrentNetType(Context ctx) 194 | { 195 | ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); 196 | NetworkInfo ni = cm.getActiveNetworkInfo(); 197 | return ni == null ? -1 : ni.getType(); 198 | } 199 | 200 | public static String getNetworkSubType(int subType) 201 | { 202 | String type = null; 203 | switch (subType) 204 | { 205 | case TelephonyManager.NETWORK_TYPE_GPRS: 206 | case TelephonyManager.NETWORK_TYPE_EDGE: 207 | case TelephonyManager.NETWORK_TYPE_CDMA: 208 | // 2G网络 209 | type = "2g"; 210 | break; 211 | case TelephonyManager.NETWORK_TYPE_EVDO_0: 212 | case TelephonyManager.NETWORK_TYPE_EVDO_A: 213 | case TelephonyManager.NETWORK_TYPE_HSDPA: 214 | case TelephonyManager.NETWORK_TYPE_UMTS: 215 | // 3G网络 216 | type = "3g"; 217 | break; 218 | default: 219 | type = "other"; 220 | break; 221 | } 222 | 223 | return type; 224 | } 225 | 226 | /** 227 | * 获得ip地址【Old版本,在MX351手机上获取的ip地址有错】,修改by GeXianglin 228 | * @param context 229 | * @return 230 | * @throws SocketException 231 | */ 232 | public static String getIPAddress(Context context) throws SocketException { 233 | String ret = null; 234 | if ((ret = getIPAddressPrivate(context)) != null) { 235 | return ret; 236 | } 237 | 238 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 239 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); 240 | en.hasMoreElements();) { 241 | 242 | NetworkInterface intf = en.nextElement(); 243 | for (Enumeration enumIpAddr = intf.getInetAddresses(); 244 | enumIpAddr.hasMoreElements();) { 245 | 246 | InetAddress inetAddress = enumIpAddr.nextElement(); 247 | if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) { 248 | return inetAddress.getHostAddress().toString(); 249 | } 250 | } 251 | } 252 | } else { 253 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); 254 | en.hasMoreElements();) { 255 | 256 | NetworkInterface intf = en.nextElement(); 257 | for (Enumeration enumIpAddr = intf.getInetAddresses(); 258 | enumIpAddr.hasMoreElements();) { 259 | 260 | InetAddress inetAddress = enumIpAddr.nextElement(); 261 | if (!inetAddress.isLoopbackAddress()) { 262 | return inetAddress.getHostAddress().toString(); 263 | } 264 | } 265 | } 266 | } 267 | return null; 268 | } 269 | 270 | /* 271 | * 获取Ip地址,原来的ip地址获取,在魅族MX351手机上获取ip地址有错,增加这个在老方法里头。 272 | * @param context 273 | * @return 274 | * @author GeXianglin 275 | */ 276 | private static String getIPAddressPrivate(Context context) { 277 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 278 | DhcpInfo dhcp = wifiManager.getDhcpInfo(); 279 | if (dhcp != null) { 280 | int ipAddress = dhcp.ipAddress; 281 | return (ipAddress & 0xff) + "." + ((ipAddress >> 8) & 0xff) + "." + 282 | ((ipAddress >> 16) & 0xff) + "." + ((ipAddress >> 24) & 0xff); 283 | } 284 | 285 | return null; 286 | } 287 | 288 | /** 289 | * 获取当前wifi的ssid 290 | * 291 | */ 292 | public static String getSsid(Context ctx) 293 | { 294 | WifiManager mWifiManager = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE); 295 | if (mWifiManager.getConnectionInfo() != null) 296 | { 297 | return mWifiManager.getConnectionInfo().getSSID(); 298 | } 299 | return null; 300 | } 301 | 302 | 303 | /** 304 | * 判断任务的SSID是否和当前网络的SSID相同(和taskInfo解耦) 305 | * 306 | * @param context 307 | * @param ssid 308 | * @return 309 | */ 310 | public static boolean isSSIDSame(Context context, String ssid) 311 | { 312 | boolean ret = false; 313 | if (context != null && ssid != null) 314 | { 315 | if (ssid != null && !ssid.equals("")) 316 | { 317 | ret = ssid.equals(getSsid(context)); 318 | } 319 | else 320 | { 321 | ret = true; 322 | } 323 | } 324 | else 325 | { 326 | if (ssid == null || ssid.equals("")) 327 | { 328 | ret = true; 329 | } 330 | } 331 | return ret; 332 | } 333 | 334 | /** 335 | * 判断传入的IP地址与手机的wifi是否在同一局域网中。 336 | * 337 | * @param context 338 | * @param PCIP 339 | * @return 340 | */ 341 | public static boolean isNetSame(Context context, String PCIP) 342 | { 343 | WifiManager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 344 | DhcpInfo dhcp = null; 345 | if (null != mWifiManager && null != (dhcp = mWifiManager.getDhcpInfo())) 346 | { 347 | int networkIP = dhcp.ipAddress; 348 | int networkMask = dhcp.netmask; 349 | int networkId = networkIP & networkMask; 350 | int pcaddress = ipAddrToInt(PCIP); 351 | if (0 != pcaddress) 352 | { 353 | int pcNetWorkId = pcaddress & networkMask; 354 | if (pcNetWorkId == networkId) 355 | { 356 | return true; 357 | } 358 | } 359 | } 360 | return false; 361 | } 362 | 363 | @SuppressLint("DefaultLocale") 364 | public static String getLocalIpAddress() 365 | { 366 | try 367 | { 368 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) 369 | { 370 | NetworkInterface intf = en.nextElement(); 371 | for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) 372 | { 373 | InetAddress inetAddress = enumIpAddr.nextElement(); 374 | if (!inetAddress.isLoopbackAddress()) 375 | { 376 | return inetAddress.getHostAddress().toString(); 377 | } 378 | } 379 | } 380 | } 381 | catch (SocketException ex) 382 | { 383 | } 384 | return null; 385 | } 386 | 387 | /** 388 | * 将String类型的IP地址转换成int32类型。将192.168.11.101转换为1812703424 注:int32 高位在前,地位在后 389 | * 390 | * @param IPAddress 391 | * String类型的IP地址,如:192.168.11.101 392 | * @return int32类型的IP地址,如:1812703424, 如果IPAddress为null或者不合法,则返回0 393 | */ 394 | public static int ipAddrToInt(String IPAddress) { 395 | if (null == IPAddress) { 396 | // throw new NullPointerException("IPAddress is null."); 397 | // NullPointerException 如果IPAddress为null 398 | return 0; 399 | } 400 | if (!IPAddress 401 | .matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$")) { 402 | // throw new 403 | // RuntimeException("IPAddress is not valid...");RuntimeException 404 | // 如果String类型的IP地址格式不合法。 405 | return 0; 406 | } 407 | String[] array = IPAddress.split("\\."); 408 | int a = Integer.parseInt(array[0]); 409 | int b = Integer.parseInt(array[1]) << 8; 410 | int c = Integer.parseInt(array[2]) << 16; 411 | int d = Integer.parseInt(array[3]) << 24; 412 | return a | b | c | d; 413 | } 414 | } 415 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 |