├── .gitignore ├── AppSocket ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── melo │ │ └── com │ │ └── androidsocket │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── melo │ │ │ └── com │ │ │ └── androidsocket │ │ │ ├── bean │ │ │ └── Users.java │ │ │ ├── common │ │ │ └── Config.java │ │ │ ├── listener │ │ │ ├── OnConnectionStateListener.java │ │ │ └── OnMessageReceiveListener.java │ │ │ ├── socket │ │ │ ├── SocketManager.java │ │ │ ├── tcp │ │ │ │ └── TCPSocket.java │ │ │ └── udp │ │ │ │ └── UDPSocket.java │ │ │ └── utils │ │ │ ├── DeviceUtil.java │ │ │ ├── HeartbeatTimer.java │ │ │ └── WifiUtil.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ └── melo │ └── com │ └── androidsocket │ └── ExampleUnitTest.java ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── melo │ │ └── com │ │ └── app │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── melo │ │ │ └── com │ │ │ └── app │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── content_main.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── melo │ └── com │ └── app │ └── 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 | -------------------------------------------------------------------------------- /AppSocket/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /AppSocket/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.jakewharton.butterknife' 3 | 4 | android { 5 | compileSdkVersion 26 6 | defaultConfig { 7 | minSdkVersion 21 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | testImplementation 'junit:junit:4.12' 25 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 26 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 27 | 28 | compile 'com.jakewharton:butterknife:8.5.1' 29 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 30 | } 31 | -------------------------------------------------------------------------------- /AppSocket/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /AppSocket/src/androidTest/java/melo/com/androidsocket/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket; 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 | * Instrumented 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("melo.com.androidsocket", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AppSocket/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/bean/Users.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.bean; 2 | 3 | 4 | public class Users { 5 | 6 | private int softVersion; 7 | private int romVersion; 8 | private String imei; 9 | private String device; 10 | private String ip; 11 | private String loginTime; 12 | 13 | public int getSoftVersion() { 14 | return softVersion; 15 | } 16 | 17 | public void setSoftVersion(int softVersion) { 18 | this.softVersion = softVersion; 19 | } 20 | 21 | public int getRomVersion() { 22 | return romVersion; 23 | } 24 | 25 | public void setRomVersion(int romVersion) { 26 | this.romVersion = romVersion; 27 | } 28 | 29 | public String getImei() { 30 | return imei; 31 | } 32 | 33 | public void setImei(String imei) { 34 | this.imei = imei; 35 | } 36 | 37 | public String getDevice() { 38 | return device; 39 | } 40 | 41 | public void setDevice(String device) { 42 | this.device = device; 43 | } 44 | 45 | public String getIp() { 46 | return ip; 47 | } 48 | 49 | public void setIp(String ip) { 50 | this.ip = ip; 51 | } 52 | 53 | public String getLoginTime() { 54 | return loginTime; 55 | } 56 | 57 | public void setLoginTime(String loginTime) { 58 | this.loginTime = loginTime; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/common/Config.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.common; 2 | 3 | /** 4 | * Created by melo on 2017/11/27. 5 | */ 6 | 7 | public class Config { 8 | 9 | public static final String MSG = "msg"; 10 | public static final String HEARTBREAK = "heartbreak"; 11 | public static final String PING = "ping"; 12 | 13 | public static final String TCP_IP = "ip"; 14 | public static final String TCP_PORT = "port"; 15 | 16 | // 单个CPU线程池大小 17 | public static final int POOL_SIZE = 5; 18 | 19 | /** 20 | * 错误处理 21 | */ 22 | public static class ErrorCode { 23 | 24 | public static final int CREATE_TCP_ERROR = 1; 25 | 26 | public static final int PING_TCP_TIMEOUT = 2; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/listener/OnConnectionStateListener.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.listener; 2 | 3 | /** 4 | * Created by melo on 2017/11/29. 5 | */ 6 | 7 | public interface OnConnectionStateListener { 8 | void onSuccess(); 9 | 10 | void onFailed(int errorCode); 11 | } 12 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/listener/OnMessageReceiveListener.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.listener; 2 | 3 | /** 4 | * Created by melo on 2017/11/27. 5 | */ 6 | 7 | public interface OnMessageReceiveListener { 8 | void onMessageReceived(String message); 9 | } 10 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/socket/SocketManager.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.socket; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import melo.com.androidsocket.common.Config; 10 | import melo.com.androidsocket.listener.OnConnectionStateListener; 11 | import melo.com.androidsocket.listener.OnMessageReceiveListener; 12 | import melo.com.androidsocket.socket.tcp.TCPSocket; 13 | import melo.com.androidsocket.socket.udp.UDPSocket; 14 | 15 | /** 16 | * Created by melo on 2017/11/27. 17 | */ 18 | 19 | public class SocketManager { 20 | 21 | private static volatile SocketManager instance = null; 22 | private UDPSocket udpSocket; 23 | private TCPSocket tcpSocket; 24 | private Context mContext; 25 | 26 | private SocketManager(Context context) { 27 | mContext = context.getApplicationContext(); 28 | } 29 | 30 | public static SocketManager getInstance(Context context) { 31 | // if already inited, no need to get lock everytime 32 | if (instance == null) { 33 | synchronized (SocketManager.class) { 34 | if (instance == null) { 35 | instance = new SocketManager(context); 36 | } 37 | } 38 | } 39 | 40 | return instance; 41 | } 42 | 43 | public void startUdpConnection() { 44 | if (udpSocket == null) { 45 | udpSocket = new UDPSocket(mContext); 46 | } 47 | 48 | // 注册接收消息的接口 49 | udpSocket.addOnMessageReceiveListener(new OnMessageReceiveListener() { 50 | @Override 51 | public void onMessageReceived(String message) { 52 | handleUdpMessage(message); 53 | } 54 | }); 55 | 56 | udpSocket.startUDPSocket(); 57 | 58 | } 59 | 60 | /** 61 | * 处理 udp 收到的消息 62 | * 63 | * @param message 64 | */ 65 | private void handleUdpMessage(String message) { 66 | try { 67 | JSONObject jsonObject = new JSONObject(message); 68 | String ip = jsonObject.optString(Config.TCP_IP); 69 | String port = jsonObject.optString(Config.TCP_PORT); 70 | if (!TextUtils.isEmpty(ip) && !TextUtils.isEmpty(port)) { 71 | startTcpConnection(ip, port); 72 | } 73 | } catch (JSONException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | /** 79 | * 开始 TCP 连接 80 | * 81 | * @param ip 82 | * @param port 83 | */ 84 | private void startTcpConnection(String ip, String port) { 85 | if (tcpSocket == null) {// 保证收到消息后,只创建一次 86 | tcpSocket = new TCPSocket(mContext); 87 | tcpSocket.startTcpSocket(ip, port); 88 | 89 | tcpSocket.setOnConnectionStateListener(new OnConnectionStateListener() { 90 | @Override 91 | public void onSuccess() {// tcp 创建成功 92 | udpSocket.stopHeartbeatTimer(); 93 | } 94 | 95 | @Override 96 | public void onFailed(int errorCode) {// tcp 异常处理 97 | switch (errorCode) { 98 | case Config.ErrorCode.CREATE_TCP_ERROR: 99 | break; 100 | case Config.ErrorCode.PING_TCP_TIMEOUT: 101 | udpSocket.startHeartbeatTimer(); 102 | tcpSocket = null; 103 | break; 104 | } 105 | } 106 | }); 107 | } 108 | 109 | } 110 | 111 | public void stopSocket() { 112 | udpSocket.stopUDPSocket(); 113 | tcpSocket.stopTcpConnection(); 114 | 115 | if (udpSocket != null) { 116 | udpSocket = null; 117 | } 118 | if (tcpSocket != null) { 119 | tcpSocket = null; 120 | } 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/socket/tcp/TCPSocket.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.socket.tcp; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.BufferedWriter; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.io.OutputStream; 15 | import java.io.OutputStreamWriter; 16 | import java.io.PrintWriter; 17 | import java.net.Socket; 18 | import java.util.concurrent.ExecutorService; 19 | import java.util.concurrent.Executors; 20 | 21 | import melo.com.androidsocket.common.Config; 22 | import melo.com.androidsocket.listener.OnConnectionStateListener; 23 | import melo.com.androidsocket.utils.HeartbeatTimer; 24 | 25 | /** 26 | * Created by melo on 2017/11/28. 27 | */ 28 | 29 | public class TCPSocket { 30 | 31 | private static final String TAG = "TCPSocket"; 32 | 33 | private Context mContext; 34 | private ExecutorService mThreadPool; 35 | private Socket mSocket; 36 | private BufferedReader br; 37 | private PrintWriter pw; 38 | private HeartbeatTimer timer; 39 | private long lastReceiveTime = 0; 40 | 41 | private OnConnectionStateListener mListener; 42 | 43 | private static final long TIME_OUT = 15 * 1000; 44 | private static final long HEARTBEAT_MESSAGE_DURATION = 2 * 1000; 45 | 46 | 47 | public TCPSocket(Context context) { 48 | this.mContext = context; 49 | 50 | int cpuNumbers = Runtime.getRuntime().availableProcessors(); 51 | // 根据CPU数目初始化线程池 52 | mThreadPool = Executors.newFixedThreadPool(cpuNumbers * Config.POOL_SIZE); 53 | // 记录创建对象时的时间 54 | lastReceiveTime = System.currentTimeMillis(); 55 | } 56 | 57 | public void startTcpSocket(final String ip, final String port) { 58 | mThreadPool.execute(new Runnable() { 59 | @Override 60 | public void run() { 61 | if (startTcpConnection(ip, Integer.valueOf(port))) {// 尝试建立 TCP 连接 62 | if (mListener != null) { 63 | mListener.onSuccess(); 64 | } 65 | startReceiveTcpThread(); 66 | startHeartbeatTimer(); 67 | } else { 68 | if (mListener != null) { 69 | mListener.onFailed(Config.ErrorCode.CREATE_TCP_ERROR); 70 | } 71 | } 72 | } 73 | }); 74 | } 75 | 76 | public void setOnConnectionStateListener(OnConnectionStateListener listener) { 77 | this.mListener = listener; 78 | } 79 | 80 | /** 81 | * 创建接收线程 82 | */ 83 | private void startReceiveTcpThread() { 84 | mThreadPool.execute(new Runnable() { 85 | @Override 86 | public void run() { 87 | String line = ""; 88 | try { 89 | while ((line = br.readLine()) != null) { 90 | handleReceiveTcpMessage(line); 91 | } 92 | } catch (IOException e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | }); 97 | } 98 | 99 | /** 100 | * 处理 tcp 收到的消息 101 | * 102 | * @param line 103 | */ 104 | private void handleReceiveTcpMessage(String line) { 105 | Log.d(TAG, "接收 tcp 消息:" + line); 106 | lastReceiveTime = System.currentTimeMillis(); 107 | } 108 | 109 | private void sendTcpMessage(String json) { 110 | pw.println(json); 111 | Log.d(TAG, "tcp 消息发送成功..."); 112 | } 113 | 114 | /** 115 | * 启动心跳 116 | */ 117 | private void startHeartbeatTimer() { 118 | if (timer == null) { 119 | timer = new HeartbeatTimer(); 120 | } 121 | timer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() { 122 | @Override 123 | public void onSchedule() { 124 | Log.d(TAG, "timer is onSchedule..."); 125 | long duration = System.currentTimeMillis() - lastReceiveTime; 126 | Log.d(TAG, "duration:" + duration); 127 | if (duration > TIME_OUT) {//若超过十五秒都没收到我的心跳包,则认为对方不在线。 128 | Log.d(TAG, "tcp ping 超时,对方已经下线"); 129 | stopTcpConnection(); 130 | if (mListener != null) { 131 | mListener.onFailed(Config.ErrorCode.PING_TCP_TIMEOUT); 132 | } 133 | } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超过两秒他没收到我的心跳包,则重新发一个。 134 | JSONObject jsonObject = new JSONObject(); 135 | try { 136 | jsonObject.put(Config.MSG, Config.PING); 137 | } catch (JSONException e) { 138 | e.printStackTrace(); 139 | } 140 | sendTcpMessage(jsonObject.toString()); 141 | } 142 | } 143 | 144 | }); 145 | timer.startTimer(0, 1000 * 2); 146 | } 147 | 148 | public void stopHeartbeatTimer() { 149 | if (timer != null) { 150 | timer.exit(); 151 | timer = null; 152 | } 153 | } 154 | 155 | /** 156 | * 尝试建立tcp连接 157 | * 158 | * @param ip 159 | * @param port 160 | */ 161 | private boolean startTcpConnection(final String ip, final int port) { 162 | try { 163 | if (mSocket == null) { 164 | mSocket = new Socket(ip, port); 165 | mSocket.setKeepAlive(true); 166 | mSocket.setTcpNoDelay(true); 167 | mSocket.setReuseAddress(true); 168 | } 169 | InputStream is = mSocket.getInputStream(); 170 | br = new BufferedReader(new InputStreamReader(is)); 171 | OutputStream os = mSocket.getOutputStream(); 172 | pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)), true); 173 | Log.d(TAG, "tcp 创建成功..."); 174 | return true; 175 | } catch (Exception e) { 176 | e.printStackTrace(); 177 | } 178 | return false; 179 | } 180 | 181 | public void stopTcpConnection() { 182 | try { 183 | stopHeartbeatTimer(); 184 | if (br != null) { 185 | br.close(); 186 | } 187 | if (pw != null) { 188 | pw.close(); 189 | } 190 | if (mThreadPool != null) { 191 | mThreadPool.shutdown(); 192 | mThreadPool = null; 193 | } 194 | if (mSocket != null) { 195 | mSocket.close(); 196 | mSocket = null; 197 | } 198 | } catch (IOException e) { 199 | e.printStackTrace(); 200 | } 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/socket/udp/UDPSocket.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.socket.udp; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.io.IOException; 10 | import java.net.DatagramPacket; 11 | import java.net.DatagramSocket; 12 | import java.net.InetAddress; 13 | import java.net.SocketException; 14 | import java.net.UnknownHostException; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.concurrent.ExecutorService; 18 | import java.util.concurrent.Executors; 19 | 20 | import melo.com.androidsocket.bean.Users; 21 | import melo.com.androidsocket.common.Config; 22 | import melo.com.androidsocket.listener.OnMessageReceiveListener; 23 | import melo.com.androidsocket.utils.DeviceUtil; 24 | import melo.com.androidsocket.utils.HeartbeatTimer; 25 | import melo.com.androidsocket.utils.WifiUtil; 26 | 27 | 28 | /** 29 | * Created by melo on 2017/9/20. 30 | */ 31 | 32 | public class UDPSocket { 33 | 34 | private static final String TAG = "UDPSocket"; 35 | 36 | private static final int BUFFER_LENGTH = 1024; 37 | private byte[] receiveByte = new byte[BUFFER_LENGTH]; 38 | 39 | private static String BROADCAST_IP = "192.168.43.255"; 40 | 41 | // 端口号,飞鸽协议默认端口2425 42 | public static final int CLIENT_PORT = 2425; 43 | 44 | private boolean isThreadRunning = false; 45 | 46 | private Context mContext; 47 | private DatagramSocket client; 48 | private DatagramPacket receivePacket; 49 | 50 | private long lastReceiveTime = 0; 51 | private static final long TIME_OUT = 120 * 1000; 52 | private static final long HEARTBEAT_MESSAGE_DURATION = 5 * 1000; 53 | 54 | private ExecutorService mThreadPool; 55 | private Thread clientThread; 56 | private HeartbeatTimer timer; 57 | private Users localUser; 58 | private Users remoteUser; 59 | private final List messageReceiveList; 60 | 61 | public UDPSocket(Context context) { 62 | 63 | this.mContext = context; 64 | 65 | int cpuNumbers = Runtime.getRuntime().availableProcessors(); 66 | // 根据CPU数目初始化线程池 67 | mThreadPool = Executors.newFixedThreadPool(cpuNumbers * Config.POOL_SIZE); 68 | // 记录创建对象时的时间 69 | lastReceiveTime = System.currentTimeMillis(); 70 | 71 | messageReceiveList = new ArrayList<>(); 72 | 73 | Log.d(TAG, "创建 UDP 对象"); 74 | // createUser(); 75 | } 76 | 77 | public void addOnMessageReceiveListener(OnMessageReceiveListener listener) { 78 | messageReceiveList.add(listener); 79 | } 80 | 81 | /** 82 | * 创建本地用户信息 83 | */ 84 | private void createUser() { 85 | if (localUser == null) { 86 | localUser = new Users(); 87 | } 88 | if (remoteUser == null) { 89 | remoteUser = new Users(); 90 | } 91 | 92 | localUser.setImei(DeviceUtil.getDeviceId(mContext)); 93 | localUser.setSoftVersion(DeviceUtil.getPackageVersionCode(mContext)); 94 | 95 | if (WifiUtil.getInstance(mContext).isWifiApEnabled()) {// 判断当前是否是开启热点方 96 | localUser.setIp("192.168.43.1"); 97 | } else {// 当前是开启 wifi 方 98 | localUser.setIp(WifiUtil.getInstance(mContext).getLocalIPAddress()); 99 | remoteUser.setIp(WifiUtil.getInstance(mContext).getServerIPAddress()); 100 | } 101 | } 102 | 103 | 104 | public void startUDPSocket() { 105 | if (client != null) return; 106 | try { 107 | // 表明这个 Socket 在设置的端口上监听数据。 108 | client = new DatagramSocket(CLIENT_PORT); 109 | client.setReuseAddress(true); 110 | if (receivePacket == null) { 111 | // 创建接受数据的 packet 112 | receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH); 113 | } 114 | 115 | startSocketThread(); 116 | } catch (SocketException e) { 117 | e.printStackTrace(); 118 | } 119 | } 120 | 121 | /** 122 | * 开启接收数据的线程 123 | */ 124 | private void startSocketThread() { 125 | clientThread = new Thread(new Runnable() { 126 | @Override 127 | public void run() { 128 | receiveMessage(); 129 | } 130 | }); 131 | isThreadRunning = true; 132 | clientThread.start(); 133 | Log.d(TAG, "开启 UDP 数据接收线程"); 134 | 135 | startHeartbeatTimer(); 136 | } 137 | 138 | /** 139 | * 处理接受到的消息 140 | */ 141 | private void receiveMessage() { 142 | while (isThreadRunning) { 143 | try { 144 | if (client != null) { 145 | client.receive(receivePacket); 146 | } 147 | lastReceiveTime = System.currentTimeMillis(); 148 | Log.d(TAG, "receive packet success..."); 149 | } catch (IOException e) { 150 | Log.e(TAG, "UDP数据包接收失败!线程停止"); 151 | stopUDPSocket(); 152 | e.printStackTrace(); 153 | return; 154 | } 155 | 156 | if (receivePacket == null || receivePacket.getLength() == 0) { 157 | Log.e(TAG, "无法接收UDP数据或者接收到的UDP数据为空"); 158 | continue; 159 | } 160 | 161 | String strReceive = new String(receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength()); 162 | Log.d(TAG, strReceive + " from " + receivePacket.getAddress().getHostAddress() + ":" + receivePacket.getPort()); 163 | 164 | //解析接收到的 json 信息 165 | notifyMessageReceive(strReceive); 166 | // 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。 167 | if (receivePacket != null) { 168 | receivePacket.setLength(BUFFER_LENGTH); 169 | } 170 | } 171 | } 172 | 173 | /** 174 | * 将消息通过接口发送到每个页面 175 | * 176 | * @param strReceive 177 | */ 178 | private void notifyMessageReceive(String strReceive) { 179 | for (OnMessageReceiveListener listener : messageReceiveList) { 180 | if (listener != null) { 181 | listener.onMessageReceived(strReceive); 182 | } 183 | } 184 | } 185 | 186 | public void stopUDPSocket() { 187 | isThreadRunning = false; 188 | receivePacket = null; 189 | stopHeartbeatTimer(); 190 | if (clientThread != null) { 191 | clientThread.interrupt(); 192 | } 193 | if (mThreadPool != null) { 194 | mThreadPool.shutdown(); 195 | } 196 | if (client != null) { 197 | client.close(); 198 | client = null; 199 | } 200 | if (timer != null) { 201 | timer.exit(); 202 | } 203 | } 204 | 205 | /** 206 | * 启动心跳,timer 间隔十秒 207 | */ 208 | public void startHeartbeatTimer() { 209 | if (timer == null) { 210 | timer = new HeartbeatTimer(); 211 | } 212 | timer.setOnScheduleListener(new HeartbeatTimer.OnScheduleListener() { 213 | @Override 214 | public void onSchedule() { 215 | Log.d(TAG, "timer is onSchedule..."); 216 | long duration = System.currentTimeMillis() - lastReceiveTime; 217 | Log.d(TAG, "duration:" + duration); 218 | if (duration > TIME_OUT) {//若超过两分钟都没收到我的心跳包,则认为对方不在线。 219 | Log.d(TAG, "超时,对方已经下线"); 220 | // 刷新时间,重新进入下一个心跳周期 221 | lastReceiveTime = System.currentTimeMillis(); 222 | } else if (duration > HEARTBEAT_MESSAGE_DURATION) {//若超过十秒他没收到我的心跳包,则重新发一个。 223 | JSONObject jsonObject = new JSONObject(); 224 | try { 225 | jsonObject.put(Config.MSG, Config.HEARTBREAK); 226 | } catch (JSONException e) { 227 | e.printStackTrace(); 228 | } 229 | sendMessage(jsonObject.toString()); 230 | } 231 | } 232 | 233 | }); 234 | timer.startTimer(0, 1000 * 5); 235 | } 236 | 237 | public void stopHeartbeatTimer() { 238 | if (timer != null) { 239 | timer.exit(); 240 | timer = null; 241 | } 242 | } 243 | 244 | /** 245 | * 发送心跳包 246 | * 247 | * @param message 248 | */ 249 | public void sendMessage(final String message) { 250 | mThreadPool.execute(new Runnable() { 251 | @Override 252 | public void run() { 253 | try { 254 | BROADCAST_IP = WifiUtil.getBroadcastAddress(); 255 | Log.d(TAG, "BROADCAST_IP:" + BROADCAST_IP); 256 | InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP); 257 | 258 | DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT); 259 | 260 | client.send(packet); 261 | 262 | // 数据发送事件 263 | Log.d(TAG, "数据发送成功"); 264 | 265 | } catch (UnknownHostException e) { 266 | e.printStackTrace(); 267 | } catch (IOException e) { 268 | e.printStackTrace(); 269 | } 270 | 271 | } 272 | }); 273 | } 274 | 275 | 276 | } 277 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/utils/DeviceUtil.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.utils; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.PackageManager.NameNotFoundException; 8 | import android.net.wifi.WifiInfo; 9 | import android.net.wifi.WifiManager; 10 | import android.os.PowerManager; 11 | import android.provider.Settings; 12 | import android.provider.Settings.SettingNotFoundException; 13 | import android.telephony.TelephonyManager; 14 | import android.text.TextUtils; 15 | import android.util.DisplayMetrics; 16 | 17 | import java.io.BufferedReader; 18 | import java.io.File; 19 | import java.io.FileFilter; 20 | import java.io.IOException; 21 | import java.io.InputStreamReader; 22 | import java.lang.reflect.Method; 23 | import java.net.NetworkInterface; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.regex.Pattern; 27 | 28 | 29 | /** 30 | * 获取设备的信息 31 | * 32 | * @author melo 33 | */ 34 | public final class DeviceUtil { 35 | 36 | /** 37 | *

IMEI.

Returns the unique device ID, for example, the IMEI for GSM and the MEID 38 | * or ESN for CDMA phones. Return null if device ID is not available. 39 | *

40 | * Requires Permission: READ_PHONE_STATE 41 | * 42 | * @param context 43 | * @return 44 | */ 45 | public synchronized static String getDeviceId(Context context) { 46 | if (context == null) { 47 | return ""; 48 | } 49 | 50 | String imei = ""; 51 | 52 | try { 53 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 54 | if (tm == null || TextUtils.isEmpty(tm.getDeviceId())) { 55 | // 双卡双待需要通过phone1和phone2获取imei,默认取phone1的imei。 56 | tm = (TelephonyManager) context.getSystemService("phone1"); 57 | } 58 | 59 | if (tm != null) { 60 | imei = tm.getDeviceId(); 61 | } 62 | } catch (SecurityException e) { 63 | e.printStackTrace(); 64 | } 65 | 66 | 67 | return imei; 68 | } 69 | 70 | /** 71 | * Returns the serial number of the SIM, if applicable. Return null if it is 72 | * unavailable. 73 | *

74 | * Requires Permission: READ_PHONE_STATE 75 | * 76 | * @param context 77 | * @return 78 | */ 79 | public synchronized static String getSimSerialNumber(Context context) { 80 | if (context == null) { 81 | return ""; 82 | } 83 | final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 84 | return tm.getSimSerialNumber(); 85 | } 86 | 87 | /** 88 | * A 64-bit number (as a hex string) that is randomly generated on the 89 | * device's first boot and should remain constant for the lifetime of the 90 | * device. (The value may change if a factory reset is performed on the 91 | * device.) 92 | * 93 | * @param context 94 | * @return 95 | */ 96 | public synchronized static String getAndroidID(Context context) { 97 | return Settings.Secure.getString(context.getContentResolver(), 98 | Settings.Secure.ANDROID_ID); 99 | } 100 | 101 | /** 102 | * 操作系统版本 103 | * 104 | * @return 105 | */ 106 | public static String getOSversion() { 107 | return android.os.Build.VERSION.RELEASE; 108 | } 109 | 110 | /** 111 | * 设备商 112 | * 113 | * @return 114 | */ 115 | public static String getManufacturer() { 116 | return android.os.Build.MANUFACTURER; 117 | } 118 | 119 | /** 120 | * 设备型号 121 | * 122 | * @return 123 | */ 124 | public static String getModel() { 125 | return android.os.Build.MODEL; 126 | } 127 | 128 | /** 129 | * 序列号 130 | * 131 | * @return 132 | */ 133 | public static String getSerialNumber() { 134 | String serial = null; 135 | try { 136 | Class c = Class.forName("android.os.SystemProperties"); 137 | Method get = c.getMethod("get", String.class); 138 | serial = (String) get.invoke(c, "ro.serialno"); 139 | } catch (Exception ignored) { 140 | } 141 | return serial; 142 | } 143 | 144 | /** 145 | * SD CARD ID 146 | * 147 | * @return 148 | */ 149 | public static synchronized String getSDcardID() { 150 | try { 151 | String sdCid = null; 152 | String[] memBlkArray = new String[]{"/sys/block/mmcblk0", "/sys/block/mmcblk1", "/sys/block/mmcblk2"}; 153 | for (String memBlk : memBlkArray) { 154 | File file = new File(memBlk); 155 | if (file.exists() && file.isDirectory()) { 156 | Process cmd = Runtime.getRuntime().exec("cat " + memBlk + "/device/cid"); 157 | BufferedReader br = new BufferedReader(new InputStreamReader(cmd.getInputStream())); 158 | sdCid = br.readLine(); 159 | if (!TextUtils.isEmpty(sdCid)) { 160 | return sdCid; 161 | } 162 | } 163 | } 164 | return null; 165 | } catch (IOException e) { 166 | // TODO Auto-generated catch block 167 | e.printStackTrace(); 168 | return null; 169 | } 170 | } 171 | 172 | /** 173 | * 获取mac地址 174 | * 175 | * @param context 176 | * @return 177 | */ 178 | public static String getMac(Context context) { 179 | if (context == null) { 180 | return ""; 181 | } 182 | String mac = null; 183 | try { 184 | final WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 185 | if (wifi != null) { 186 | WifiInfo info = wifi.getConnectionInfo(); 187 | if (null != info && info.getMacAddress() != null) { 188 | mac = info.getMacAddress(); 189 | } 190 | } 191 | } catch (Exception e) { 192 | e.printStackTrace(); 193 | } 194 | return mac; 195 | } 196 | 197 | /** 198 | * 获取mac地址 199 | * 可以突破android6.0的限制 200 | * 201 | * @return 202 | */ 203 | public static String getWifiMacAddress() { 204 | try { 205 | String interfaceName = "wlan0"; 206 | List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); 207 | for (NetworkInterface intf : interfaces) { 208 | if (!intf.getName().equalsIgnoreCase(interfaceName)) { 209 | continue; 210 | } 211 | 212 | byte[] mac = intf.getHardwareAddress(); 213 | if (mac == null) { 214 | return ""; 215 | } 216 | 217 | StringBuilder buf = new StringBuilder(); 218 | for (byte aMac : mac) { 219 | buf.append(String.format("%02X:", aMac)); 220 | } 221 | if (buf.length() > 0) { 222 | buf.deleteCharAt(buf.length() - 1); 223 | } 224 | return buf.toString(); 225 | } 226 | } catch (Exception ex) { 227 | } // for now eat exceptions 228 | return ""; 229 | } 230 | 231 | /** 232 | * 获取IMSI 233 | * 234 | * @param context 235 | * @return 236 | */ 237 | public static String getIMSI(Context context) { 238 | 239 | TelephonyManager tm = (TelephonyManager) context 240 | .getSystemService(Context.TELEPHONY_SERVICE); 241 | 242 | return tm.getSubscriberId(); 243 | 244 | } 245 | 246 | /** 247 | * get sim serial number 248 | */ 249 | public static String getSimSerialNum(Context context) { 250 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE); 251 | return tm.getSimSerialNumber(); 252 | } 253 | 254 | /** 255 | * 获取屏幕的分辨率 256 | * 257 | * @param context 258 | * @return int array with 2 items. The first item is width, and the second is height. 259 | */ 260 | public static int[] getScreenResolution(Context context) { 261 | DisplayMetrics dm = context.getResources().getDisplayMetrics(); 262 | 263 | int[] resolution = new int[2]; 264 | resolution[0] = dm.widthPixels; 265 | resolution[1] = dm.heightPixels; 266 | 267 | return resolution; 268 | } 269 | 270 | /** 271 | * 获取WIFI的Mac地址 272 | * 273 | * @param context 274 | * @return Wifi的BSSID即mac地址 275 | */ 276 | public static String getWifiBSSID(Context context) { 277 | if (context == null) { 278 | return null; 279 | } 280 | 281 | String mac = null; 282 | WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 283 | WifiInfo info = wm.getConnectionInfo(); 284 | if (info != null) { 285 | mac = info.getBSSID();// 获得本机的MAC地址 286 | } 287 | 288 | return mac; 289 | } 290 | 291 | public static String getPackageVersion(Context context) { 292 | PackageManager packageManager = context.getPackageManager(); 293 | PackageInfo packInfo; 294 | try { 295 | packInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 296 | return packInfo.versionName; 297 | } catch (NameNotFoundException e) { 298 | e.printStackTrace(); 299 | } 300 | 301 | return null; 302 | } 303 | 304 | public static int getPackageVersionCode(Context context) { 305 | PackageManager packageManager = context.getPackageManager(); 306 | PackageInfo packInfo; 307 | try { 308 | packInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 309 | return packInfo.versionCode; 310 | } catch (NameNotFoundException e) { 311 | e.printStackTrace(); 312 | } 313 | 314 | return 0; 315 | } 316 | 317 | /** 318 | * 获取系统休眠时间。 319 | * 320 | * @return 321 | */ 322 | public static int getScreenOffTimeOut(Context context) { 323 | int sleepTime; 324 | try { 325 | sleepTime = Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); 326 | } catch (SettingNotFoundException e) { 327 | e.printStackTrace(); 328 | sleepTime = 15 * 1000; 329 | } 330 | return sleepTime; 331 | } 332 | 333 | 334 | public static boolean isScreenOn(Context context) { 335 | PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 336 | return powerManager.isScreenOn(); 337 | } 338 | 339 | /** 340 | * Gets the number of cores available in this device, across all processors. 341 | * Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu" 342 | * 343 | * @return The number of cores, or 1 if failed to get result 344 | */ 345 | public static int getCPUNumCores() { 346 | try { 347 | //Get directory containing CPU info 348 | File dir = new File("/sys/devices/system/cpu/"); 349 | //Filter to only list the devices we care about 350 | File[] files = dir.listFiles(new FileFilter() { 351 | @Override 352 | public boolean accept(File pathname) { 353 | //Check if filename is "cpu", followed by a single digit number 354 | if (Pattern.matches("cpu[0-9]", pathname.getName())) { 355 | return true; 356 | } 357 | return false; 358 | } 359 | }); 360 | //Return the number of cores (virtual CPU devices) 361 | return files.length; 362 | } catch (Exception e) { 363 | return 1; 364 | } 365 | } 366 | 367 | /** 368 | * 获取系统参数 369 | * 370 | * @param configName 371 | * @return 372 | */ 373 | 374 | public static String getSystemConf(String configName) { 375 | try { 376 | Process process = Runtime.getRuntime().exec("getprop " + configName); 377 | InputStreamReader ir = new InputStreamReader(process.getInputStream()); 378 | BufferedReader input = new BufferedReader(ir); 379 | String value = input.readLine(); 380 | input.close(); 381 | ir.close(); 382 | process.destroy(); 383 | return value; 384 | } catch (Exception e) { 385 | e.printStackTrace(); 386 | } 387 | return ""; 388 | } 389 | 390 | /** 391 | * 获取硬件版本 392 | * 393 | * @return 394 | */ 395 | public static String getHardwareVersion() { 396 | return getSystemConf("ro.hardware"); 397 | } 398 | 399 | /** 400 | * 获取rom版本 401 | */ 402 | public static String getRomVersion() { 403 | return getSystemConf("ro.mediatek.version.release"); 404 | } 405 | 406 | /** 407 | * 获取hq rom版本 408 | */ 409 | private static String gethqRomVersion() { 410 | return getSystemConf("ro.huaqin.version.release"); 411 | } 412 | 413 | public static String getShowhqRomVersion() { 414 | String showHq = "hq"; 415 | String hqRomVer = gethqRomVersion(); 416 | if (TextUtils.isEmpty(hqRomVer) == false) { 417 | String[] s = hqRomVer.split("_"); 418 | if (s != null && s.length >= 3) { 419 | showHq = s[2]; 420 | } 421 | } 422 | return showHq; 423 | } 424 | 425 | /** 426 | * 获取installed apk版本 427 | */ 428 | public static PackageInfo getInstalledAppInfo(Context context, String pname) { 429 | try { 430 | List packages = context.getPackageManager().getInstalledPackages(0); 431 | if (packages != null) { 432 | for (PackageInfo pinfo : packages) { 433 | if (pinfo != null && pinfo.packageName.equals(pname)) { 434 | return pinfo; 435 | } 436 | } 437 | } 438 | } catch (Exception e) { 439 | e.printStackTrace(); 440 | } 441 | return null; 442 | } 443 | 444 | } -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/utils/HeartbeatTimer.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.utils; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | 6 | /** 7 | * Created by melo on 2017/9/21. 8 | */ 9 | 10 | public class HeartbeatTimer { 11 | 12 | private Timer timer; 13 | private TimerTask task; 14 | private OnScheduleListener mListener; 15 | 16 | public HeartbeatTimer() { 17 | timer = new Timer(); 18 | } 19 | 20 | public void startTimer(long delay, long period) { 21 | task = new TimerTask() { 22 | @Override 23 | public void run() { 24 | if (mListener != null) { 25 | mListener.onSchedule(); 26 | } 27 | } 28 | }; 29 | timer.schedule(task, delay, period); 30 | } 31 | 32 | public void exit() { 33 | if (task != null) { 34 | task.cancel(); 35 | } 36 | if (timer != null) { 37 | timer.cancel(); 38 | } 39 | } 40 | 41 | public interface OnScheduleListener { 42 | void onSchedule(); 43 | } 44 | 45 | public void setOnScheduleListener(OnScheduleListener listener) { 46 | this.mListener = listener; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /AppSocket/src/main/java/melo/com/androidsocket/utils/WifiUtil.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket.utils; 2 | 3 | import android.content.Context; 4 | import android.net.DhcpInfo; 5 | import android.net.wifi.WifiInfo; 6 | import android.net.wifi.WifiManager; 7 | 8 | import java.lang.reflect.Method; 9 | import java.net.InetAddress; 10 | import java.net.InterfaceAddress; 11 | import java.net.NetworkInterface; 12 | import java.util.Enumeration; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by melo on 2017/9/23. 18 | */ 19 | 20 | public class WifiUtil { 21 | 22 | private static final String TAG = "LocationUtils"; 23 | 24 | private static volatile WifiUtil instance = null; 25 | 26 | private WifiManager mWifiManager; 27 | 28 | private Context mContext; 29 | 30 | private WifiUtil(Context context) { 31 | mContext = context; 32 | mWifiManager = (WifiManager) mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE); 33 | } 34 | 35 | public static WifiUtil getInstance(Context context) { 36 | if (instance == null) { 37 | synchronized (WifiUtil.class) { 38 | if (instance == null) { 39 | instance = new WifiUtil(context); 40 | } 41 | } 42 | } 43 | return instance; 44 | } 45 | 46 | public boolean isWifiApEnabled() { 47 | try { 48 | Method method = mWifiManager.getClass().getMethod("isWifiApEnabled"); 49 | method.setAccessible(true); 50 | return (Boolean) method.invoke(mWifiManager); 51 | 52 | } catch (NoSuchMethodException e) { 53 | // TODO Auto-generated catch block 54 | e.printStackTrace(); 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | 59 | return false; 60 | } 61 | 62 | public String getLocalIPAddress() { 63 | WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 64 | return intToIp(wifiInfo.getIpAddress()); 65 | } 66 | 67 | public String getServerIPAddress() { 68 | DhcpInfo mDhcpInfo = mWifiManager.getDhcpInfo(); 69 | return intToIp(mDhcpInfo.gateway); 70 | } 71 | 72 | private static String intToIp(int i) { 73 | return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." 74 | + ((i >> 24) & 0xFF); 75 | } 76 | 77 | /** 78 | * @return 优先获取网卡地址 79 | */ 80 | public static String getBroadcastAddress() { 81 | String broadcast = getBroadcastAddress("p2p"); 82 | if (broadcast == null) { 83 | return getBroadcastAddress("wlan0"); 84 | } 85 | return broadcast; 86 | } 87 | 88 | /** 89 | * @param netCardName 网卡名称 90 | * @return 获取的广播地址 91 | */ 92 | public static String getBroadcastAddress(String netCardName) { 93 | try { 94 | Enumeration eni = NetworkInterface 95 | .getNetworkInterfaces(); 96 | while (eni.hasMoreElements()) { 97 | NetworkInterface networkCard = eni.nextElement(); 98 | if (networkCard.getDisplayName().startsWith(netCardName)) { 99 | List ncAddrList = networkCard 100 | .getInterfaceAddresses(); 101 | Iterator ncAddrIterator = ncAddrList.iterator(); 102 | while (ncAddrIterator.hasNext()) { 103 | InterfaceAddress networkCardAddress = ncAddrIterator.next(); 104 | InetAddress address = networkCardAddress.getAddress(); 105 | if (!address.isLoopbackAddress()) { 106 | String hostAddress = address.getHostAddress(); 107 | if (hostAddress.indexOf(":") > 0) { 108 | // case : ipv6 109 | continue; 110 | } else { 111 | // case : ipv4 112 | String broadcastAddress = networkCardAddress.getBroadcast().getHostAddress(); 113 | return broadcastAddress; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } catch (Exception e) { 120 | e.printStackTrace(); 121 | } 122 | 123 | return null; 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsMelo/AndroidSocket/525513e25e8cae78f1518b4d2d8d68e82db18fb6/AppSocket/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AppSocket/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidSocket 3 | 4 | -------------------------------------------------------------------------------- /AppSocket/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /AppSocket/src/test/java/melo/com/androidsocket/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package melo.com.androidsocket; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidSocket 2 | 3 | 项目介绍地址: 4 | 5 | http://www.jianshu.com/p/61de9478c9aa 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | 6 | 7 | 8 | defaultConfig { 9 | applicationId "melo.com.app" 10 | minSdkVersion 21 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | 31 | implementation 'com.android.support:appcompat-v7:26.1.0' 32 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 33 | implementation 'com.android.support:design:26.1.0' 34 | testImplementation 'junit:junit:4.12' 35 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 36 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 37 | 38 | implementation project(':AppSocket') 39 | } 40 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/melo/com/app/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package melo.com.app; 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 | * Instrumented 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("melo.com.app", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/melo/com/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package melo.com.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.FloatingActionButton; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | 8 | import butterknife.BindView; 9 | import butterknife.ButterKnife; 10 | import butterknife.OnClick; 11 | import melo.com.androidsocket.socket.SocketManager; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | @BindView(R.id.toolbar) 16 | Toolbar toolbar; 17 | @BindView(R.id.fab) 18 | FloatingActionButton fab; 19 | private SocketManager manager; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | ButterKnife.bind(this); 26 | manager = SocketManager.getInstance(this); 27 | manager.startUdpConnection(); 28 | } 29 | 30 | @OnClick(R.id.fab) 31 | public void onViewClicked() { 32 | 33 | } 34 | 35 | @Override 36 | protected void onDestroy() { 37 | super.onDestroy(); 38 | manager.stopSocket(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 24 | 25 |