├── .gitignore
├── .idea
├── checkstyle-idea.xml
├── checkstyleidea-libs
│ └── readme.txt
├── codeStyles
│ └── Project.xml
├── compiler.xml
├── encodings.xml
├── gradle.xml
├── jarRepositories.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── easysocket
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── easysocket
│ │ │ ├── CallbackIDFactoryImpl.java
│ │ │ ├── MainActivity.java
│ │ │ └── message
│ │ │ ├── AbsMessage.java
│ │ │ ├── CallbackSender.java
│ │ │ ├── ClientHeartBeat.java
│ │ │ ├── IMessage.java
│ │ │ ├── SimpleMessage.java
│ │ │ └── TestMessage.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_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
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── easysocket
│ └── ExampleUnitTest.java
├── build.gradle
├── easysocket
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── easysocket
│ ├── ConnectionHolder.java
│ ├── EasySocket.java
│ ├── callback
│ ├── ProgressDialogCallBack.java
│ ├── SimpleCallBack.java
│ └── SuperCallBack.java
│ ├── config
│ ├── CallbackIDFactory.java
│ ├── DefaultMessageProtocol.java
│ ├── DefaultX509ProtocolTrustManager.java
│ ├── EasySocketOptions.java
│ ├── SocketFactory.java
│ └── SocketSSLConfig.java
│ ├── connection
│ ├── action
│ │ ├── IOAction.java
│ │ ├── SocketAction.java
│ │ └── SocketStatus.java
│ ├── connect
│ │ ├── SuperConnection.java
│ │ └── TcpConnection.java
│ ├── dispatcher
│ │ ├── CallbackResponseDispatcher.java
│ │ ├── MainThreadExecutor.java
│ │ └── SocketActionDispatcher.java
│ ├── heartbeat
│ │ └── HeartManager.java
│ ├── iowork
│ │ ├── EasyReader.java
│ │ ├── EasyWriter.java
│ │ └── IOManager.java
│ └── reconnect
│ │ ├── AbsReconnection.java
│ │ └── DefaultReConnection.java
│ ├── entity
│ ├── OriginReadData.java
│ ├── SocketAddress.java
│ └── basemsg
│ │ ├── IResponse.java
│ │ ├── ISender.java
│ │ ├── SuperCallbackResponse.java
│ │ ├── SuperCallbackSender.java
│ │ └── SuperSender.java
│ ├── exception
│ ├── InitialExeption.java
│ ├── NotNullException.java
│ ├── ReadRecoverableExeption.java
│ ├── ReadUnrecoverableException.java
│ ├── RequestCancelException.java
│ └── RequestTimeOutException.java
│ ├── interfaces
│ ├── callback
│ │ ├── ICallBack.java
│ │ ├── IProgressDialog.java
│ │ ├── IType.java
│ │ └── ProgressCancelListener.java
│ ├── config
│ │ ├── IConnectionSwitchListener.java
│ │ ├── IMessageProtocol.java
│ │ └── IOptions.java
│ ├── conn
│ │ ├── IConnectionManager.java
│ │ ├── IHeartManager.java
│ │ ├── IReconnListener.java
│ │ ├── ISend.java
│ │ ├── ISocketActionDispatch.java
│ │ ├── ISocketActionListener.java
│ │ ├── ISubscribeSocketAction.java
│ │ └── SocketActionListener.java
│ └── io
│ │ ├── IIOManager.java
│ │ ├── IReader.java
│ │ └── IWriter.java
│ └── utils
│ ├── LogUtil.java
│ └── Utils.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── socker_server
├── .gitignore
├── build.gradle
└── src
└── main
└── java
└── com
└── socker_server
├── HandlerIO.java
├── MainClass.java
├── ServerConfig.java
├── entity
├── DefaultMessageProtocol.java
├── IMessageProtocol.java
├── MessageID.java
├── OriginReadData.java
└── message
│ ├── CallbackResponse.java
│ ├── DelayResponse.java
│ ├── ServerHeartBeat.java
│ ├── TestResponse.java
│ └── base
│ ├── BaseResponse.java
│ ├── IClient.java
│ ├── IResponse.java
│ ├── SuperClient.java
│ └── SuperResponse.java
└── iowork
├── IIOManager.java
├── IReader.java
├── IWriter.java
├── ServerIOManager.java
├── ServerReader.java
└── ServerWriter.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/checkstyleidea-libs/readme.txt:
--------------------------------------------------------------------------------
1 | This folder contains libraries copied from the "EasySocket" project.
2 | It is managed by the CheckStyle-IDEA IDE plugin.
3 | Do not modify this folder while the IDE is running.
4 | When the IDE is stopped, you may delete this folder at any time. It will be recreated as needed.
5 | In order to prevent the CheckStyle-IDEA IDE plugin from creating this folder,
6 | uncheck the "Copy libraries from project directory" option in the CheckStyle-IDEA settings dialog.
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | # 这里主要是为了隐藏作者的一些隐私信息,你运行的时候可删除
3 | /src/main/res/values/evil.xml
4 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.easysocket"
7 | minSdkVersion 19
8 | targetSdkVersion 28
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-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | implementation 'com.google.code.gson:gson:2.2.4'
29 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
30 | implementation project(':easysocket')
31 | }
32 |
--------------------------------------------------------------------------------
/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/com/easysocket/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.easysocket;
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() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.easysocket", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/CallbackIDFactoryImpl.java:
--------------------------------------------------------------------------------
1 | package com.easysocket;
2 |
3 | import com.easysocket.config.CallbackIDFactory;
4 | import com.easysocket.entity.OriginReadData;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | /**
10 | * Author:枪花
11 | * Date:2020/3/20
12 | * note:根据自己的实际情况实现
13 | */
14 | public class CallbackIDFactoryImpl extends CallbackIDFactory {
15 |
16 | /**
17 | * @param
18 | * @return
19 | */
20 | @Override
21 | public String getCallbackID(OriginReadData data) {
22 | try {
23 | JSONObject body = new JSONObject(data.getBodyString());
24 | String callbackId = body.getString("callbackId");
25 | return callbackId;
26 | } catch (JSONException e) {
27 | e.printStackTrace();
28 | return null;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/AbsMessage.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | import com.easysocket.EasySocket;
4 | import com.google.gson.Gson;
5 |
6 | import java.nio.ByteBuffer;
7 |
8 | /**
9 | * Author:Mapogo
10 | * Date:2021/1/13
11 | * Note:
12 | */
13 | public class AbsMessage implements IMessage {
14 |
15 | public AbsMessage() {
16 | }
17 |
18 | @Override
19 | public byte[] pack() {
20 |
21 | byte[] body = new Gson().toJson(this).getBytes();
22 | // 如果没有设置消息协议,则直接发送消息
23 | if (EasySocket.getInstance().getDefOptions().getMessageProtocol() == null) {
24 | return body;
25 | }
26 | ByteBuffer bb = ByteBuffer.allocate(body.length + 4);
27 | bb.putInt(body.length);
28 | bb.put(body);
29 | return bb.array();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/CallbackSender.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | import com.easysocket.EasySocket;
4 | import com.easysocket.entity.basemsg.SuperCallbackSender;
5 | import com.google.gson.Gson;
6 |
7 | import java.nio.ByteBuffer;
8 |
9 | /**
10 | * Author:Alex
11 | * Date:2019/6/11
12 | * Note:带有回调标识的发送消息
13 | */
14 | public class CallbackSender extends SuperCallbackSender {
15 |
16 | private String msgId;
17 | private String from;
18 |
19 | public String getMsgId() {
20 | return msgId;
21 | }
22 |
23 | public void setMsgId(String msgId) {
24 | this.msgId = msgId;
25 | }
26 |
27 | public String getFrom() {
28 | return from;
29 | }
30 |
31 | public void setFrom(String from) {
32 | this.from = from;
33 | }
34 |
35 |
36 | @Override
37 | public byte[] pack() {
38 | byte[] body = new Gson().toJson(this).getBytes();
39 | // 如果没有设置消息协议,则直接发送消息
40 | if (EasySocket.getInstance().getDefOptions().getMessageProtocol() == null) {
41 | return body;
42 | }
43 | ByteBuffer bb = ByteBuffer.allocate(body.length + 4);
44 | bb.putInt(body.length);
45 | bb.put(body);
46 | return bb.array();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/ClientHeartBeat.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/6
6 | * Note:客户端心跳
7 | */
8 | public class ClientHeartBeat extends AbsMessage {
9 | private String msgId;
10 | private String from;
11 |
12 | public String getMsgId() {
13 | return msgId;
14 | }
15 |
16 | public void setMsgId(String msgId) {
17 | this.msgId = msgId;
18 | }
19 |
20 | public String getFrom() {
21 | return from;
22 | }
23 |
24 | public void setFrom(String from) {
25 | this.from = from;
26 | }
27 |
28 | @Override
29 | public String toString() {
30 | return "ClientHeartBeat{" +
31 | "msgId='" + msgId + '\'' +
32 | ", from='" + from + '\'' +
33 | '}';
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/IMessage.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | /**
4 | * Author:Mapogo
5 | * Date:2021/1/13
6 | * Note:所有消息的接口
7 | */
8 | public interface IMessage {
9 |
10 | /**
11 | * 根据自己的协议打包消息
12 | *
13 | * @return 返回打包好的byte[]
14 | */
15 | byte[] pack();
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/SimpleMessage.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | /**
4 | * Author:Mapogo
5 | * Date:2021/1/14
6 | * Note:
7 | */
8 | public class SimpleMessage {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/easysocket/message/TestMessage.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.message;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/12/6
6 | * Note:不带回调标识的消息
7 | */
8 | public class TestMessage extends AbsMessage {
9 |
10 | private String msgId;
11 | private String from;
12 |
13 | public String getMsgId() {
14 | return msgId;
15 | }
16 |
17 | public void setMsgId(String msgId) {
18 | this.msgId = msgId;
19 | }
20 |
21 | public String getFrom() {
22 | return from;
23 | }
24 |
25 | public void setFrom(String from) {
26 | this.from = from;
27 | }
28 |
29 | @Override
30 | public String toString() {
31 | return "TestMessage{" +
32 | "msgId='" + msgId + '\'' +
33 | ", from='" + from + '\'' +
34 | '}';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/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 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
41 |
42 |
48 |
49 |
55 |
56 |
57 |
63 |
64 |
65 |
71 |
72 |
78 |
79 |
85 |
86 |
92 |
93 |
99 |
100 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EasySocket
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/easysocket/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.easysocket;
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() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.0'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/easysocket/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/easysocket/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | // JitPack Maven
3 | apply plugin: 'com.github.dcendents.android-maven'
4 | // Your Group
5 | group='com.github.jiusetian'
6 |
7 | android {
8 | compileSdkVersion 27
9 | //buildToolsVersion rootProject.ext.android.buildToolsVersion
10 |
11 | defaultConfig {
12 | minSdkVersion 14
13 | targetSdkVersion 27
14 | versionCode 1
15 | versionName "1.0"
16 | javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
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 | implementation 'com.google.code.gson:gson:2.2.4'
31 | }
32 |
33 | sourceCompatibility = "7"
34 | targetCompatibility = "7"
35 |
--------------------------------------------------------------------------------
/easysocket/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/ConnectionHolder.java:
--------------------------------------------------------------------------------
1 | package com.easysocket;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 | import com.easysocket.connection.connect.SuperConnection;
5 | import com.easysocket.connection.connect.TcpConnection;
6 | import com.easysocket.entity.SocketAddress;
7 | import com.easysocket.interfaces.config.IConnectionSwitchListener;
8 | import com.easysocket.interfaces.conn.IConnectionManager;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * Author:Alex
15 | * Date:2019/6/4
16 | * Note:socket连接管理器
17 | */
18 | public class ConnectionHolder {
19 |
20 | private volatile Map mConnectionManagerMap = new HashMap<>();
21 |
22 |
23 | private static class InstanceHolder {
24 | private static final ConnectionHolder INSTANCE = new ConnectionHolder();
25 | }
26 |
27 | public static ConnectionHolder getInstance() {
28 | return InstanceHolder.INSTANCE;
29 | }
30 |
31 | private ConnectionHolder() {
32 | mConnectionManagerMap.clear();
33 | }
34 |
35 | /**
36 | * 移除某个连接
37 | *
38 | * @param socketAddress
39 | */
40 | public void removeConnection(SocketAddress socketAddress) {
41 | removeConnection(createKey(socketAddress));
42 | }
43 |
44 | public void removeConnection(String socketAddress) {
45 | mConnectionManagerMap.remove(socketAddress);
46 | }
47 |
48 | /**
49 | * 获取指定SocketAddress的连接,参数配置使用默认的
50 | *
51 | * @param address
52 | * @return
53 | */
54 | public IConnectionManager getConnection(SocketAddress address) {
55 | return getConnection(createKey(address));
56 | }
57 |
58 | public IConnectionManager getConnection(String address) {
59 | IConnectionManager manager = mConnectionManagerMap.get(address);
60 | if (manager == null) {
61 | return getConnection(address, EasySocketOptions.getDefaultOptions());
62 | } else {
63 | return getConnection(address, manager.getOptions());
64 | }
65 | }
66 |
67 | /**
68 | * 获取指定SocketAddress的连接
69 | *
70 | * @param address
71 | * @param socketOptions
72 | * @return
73 | */
74 | public IConnectionManager getConnection(SocketAddress address, EasySocketOptions socketOptions) {
75 | return getConnection(createKey(address),socketOptions);
76 | }
77 |
78 | public IConnectionManager getConnection(String address, EasySocketOptions socketOptions) {
79 | IConnectionManager manager = mConnectionManagerMap.get(address);
80 | if (manager != null) { // 有缓存
81 | manager.setOptions(socketOptions);
82 | return manager;
83 | } else {
84 | return createNewManagerAndCache(address, socketOptions);
85 | }
86 | }
87 |
88 | /**
89 | * 创建新的连接并缓存
90 | *
91 | * @param address
92 | * @param socketOptions
93 | * @return
94 | */
95 | private IConnectionManager createNewManagerAndCache(SocketAddress address, EasySocketOptions socketOptions) {
96 | SuperConnection manager = new TcpConnection(address); // 创建连接管理器
97 | manager.setOptions(socketOptions); // 设置参数
98 | // 连接主机的切换监听
99 | manager.setOnConnectionSwitchListener(new IConnectionSwitchListener() {
100 | @Override
101 | public void onSwitchConnectionInfo(IConnectionManager manager, SocketAddress oldAddress,
102 | SocketAddress newAddress) {
103 | // 切换了另外一个主机的连接,删除旧的连接和添加新的连接
104 | synchronized (mConnectionManagerMap) {
105 | // 首先断开连接,销毁相关线程和资源
106 | mConnectionManagerMap.get(createKey(oldAddress)).disconnect(false);
107 | mConnectionManagerMap.remove(createKey(oldAddress));
108 | mConnectionManagerMap.put(createKey(newAddress), manager);
109 | }
110 | }
111 | });
112 |
113 | synchronized (mConnectionManagerMap) {
114 | mConnectionManagerMap.put(createKey(address), manager);
115 | }
116 | return manager;
117 | }
118 |
119 | private IConnectionManager createNewManagerAndCache(String address, EasySocketOptions socketOptions) {
120 | return createNewManagerAndCache(createSocketAddress(address), socketOptions);
121 | }
122 |
123 | /**
124 | * @param socketAddress
125 | * @return
126 | */
127 | private String createKey(SocketAddress socketAddress) {
128 | return socketAddress.getIp() + ":" + socketAddress.getPort();
129 | }
130 |
131 | private SocketAddress createSocketAddress(String address) {
132 | String[] s = address.split(":");
133 | return new SocketAddress(s[0], Integer.parseInt(s[1]));
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/EasySocket.java:
--------------------------------------------------------------------------------
1 | package com.easysocket;
2 |
3 | import android.content.Context;
4 |
5 | import com.easysocket.config.EasySocketOptions;
6 | import com.easysocket.connection.heartbeat.HeartManager;
7 | import com.easysocket.entity.SocketAddress;
8 | import com.easysocket.entity.basemsg.SuperCallbackSender;
9 | import com.easysocket.exception.InitialExeption;
10 | import com.easysocket.exception.NotNullException;
11 | import com.easysocket.interfaces.conn.IConnectionManager;
12 | import com.easysocket.interfaces.conn.ISocketActionListener;
13 |
14 | /**
15 | * Author:Alex
16 | * Date:2019/6/4
17 | * Note:EasySocket API
18 | */
19 | public class EasySocket {
20 |
21 | /**
22 | * 连接的缓存
23 | */
24 | private static ConnectionHolder connectionHolder = ConnectionHolder.getInstance();
25 | // 单例
26 | private volatile static EasySocket singleton = null;
27 | /**
28 | * 默认的连接参数
29 | */
30 | private EasySocketOptions defOptions;
31 | /**
32 | * 默认的连接
33 | */
34 | private IConnectionManager defConnection;
35 | /**
36 | * 上下文
37 | */
38 | private Context context;
39 |
40 | /**
41 | * 单例
42 | *
43 | * @return
44 | */
45 | public static EasySocket getInstance() {
46 | if (singleton == null) {
47 | synchronized (EasySocket.class) {
48 | if (singleton == null) {
49 | singleton = new EasySocket();
50 | }
51 | }
52 | }
53 | return singleton;
54 | }
55 |
56 | /**
57 | * 获取上下文
58 | *
59 | * @return
60 | */
61 | public Context getContext() {
62 | return context;
63 | }
64 |
65 | /**
66 | * 获取默认的配置参数
67 | *
68 | * @return
69 | */
70 | public EasySocketOptions getDefOptions() {
71 | return defOptions == null ? EasySocketOptions.getDefaultOptions() : defOptions;
72 | }
73 |
74 | /**
75 | * 创建socket连接,此连接为默认的连接,如果你的项目只有一个Socket连接,可以用这个方法,
76 | * 在方法不指定连接地址的情况下,默认使用都是这个连接,
77 | * 比如: upMessage(byte[] message)、 connect()等
78 | *
79 | * @return
80 | */
81 | public EasySocket createConnection(EasySocketOptions options, Context context) {
82 | this.defOptions = options;
83 | this.context = context;
84 | SocketAddress socketAddress = options.getSocketAddress();
85 | if (options.getSocketAddress() == null) {
86 | throw new InitialExeption("请在初始化的时候设置SocketAddress");
87 | }
88 | // 如果有备用主机则设置
89 | if (options.getBackupAddress() != null) {
90 | socketAddress.setBackupAddress(options.getBackupAddress());
91 | }
92 | if (defConnection == null) {
93 | defConnection = connectionHolder.getConnection(socketAddress,
94 | options == null ? EasySocketOptions.getDefaultOptions() : options);
95 | }
96 | // 执行连接
97 | defConnection.connect();
98 | return this;
99 | }
100 |
101 | /**
102 | * 连接socket,作用于默认连接
103 | *
104 | * @return
105 | */
106 | public EasySocket connect() {
107 | getDefconnection().connect();
108 | return this;
109 | }
110 |
111 | /**
112 | * @param address socket地址,包括ip和端口
113 | * @return
114 | */
115 | public EasySocket connect(String address) {
116 | getConnection(address).connect();
117 | return this;
118 | }
119 |
120 |
121 | /**
122 | * 关闭连接,作用于默认连接
123 | *
124 | * @param isNeedReconnect 是否需要重连
125 | * @return
126 | */
127 | public EasySocket disconnect(boolean isNeedReconnect) {
128 | getDefconnection().disconnect(isNeedReconnect);
129 | return this;
130 | }
131 |
132 |
133 | /**
134 | * 关闭连接
135 | *
136 | * @param isNeedReconnect 是否需要重连
137 | * @return
138 | */
139 | public EasySocket disconnect(String address, boolean isNeedReconnect) {
140 | getConnection(address).disconnect(isNeedReconnect);
141 | return this;
142 | }
143 |
144 | /**
145 | * 销毁连接对象,作用于默认连接
146 | *
147 | * @return
148 | */
149 | public EasySocket destroyConnection() {
150 | // 断开连接
151 | getDefconnection().disconnect(false);
152 | // 移除连接
153 | connectionHolder.removeConnection(defOptions.getSocketAddress());
154 | defConnection = null;
155 | return this;
156 | }
157 |
158 |
159 | /**
160 | * 销毁连接对象
161 | *
162 | * @return
163 | */
164 | public EasySocket destroyConnection(String address) {
165 | // 断开连接
166 | getConnection(address).disconnect(false);
167 | // 移除连接
168 | connectionHolder.removeConnection(address);
169 | return this;
170 | }
171 |
172 | /**
173 | * 发送有回调的消息,作用于默认连接
174 | *
175 | * @param sender
176 | * @return
177 | */
178 | public IConnectionManager upCallbackMessage(SuperCallbackSender sender) {
179 | getDefconnection().upCallbackMessage(sender);
180 | return defConnection;
181 | }
182 |
183 | /**
184 | * 发送有回调的消息
185 | *
186 | * @param sender
187 | * @return
188 | */
189 | public IConnectionManager upCallbackMessage(SuperCallbackSender sender, String address) {
190 | return getConnection(address).upCallbackMessage(sender);
191 | }
192 |
193 |
194 | /**
195 | * 发送byte[]
196 | *
197 | * @param message
198 | * @return
199 | */
200 | public IConnectionManager upMessage(byte[] message, String address) {
201 | return getConnection(address).upBytes(message);
202 | }
203 |
204 | /**
205 | * 发送byte[],作用于默认连接
206 | *
207 | * @param message
208 | * @return
209 | */
210 | public IConnectionManager upMessage(byte[] message) {
211 | return getDefconnection().upBytes(message);
212 | }
213 |
214 |
215 | /**
216 | * 注册监听socket行为,作用于默认连接
217 | *
218 | * @param socketActionListener
219 | */
220 | public EasySocket subscribeSocketAction(ISocketActionListener socketActionListener) {
221 | getDefconnection().subscribeSocketAction(socketActionListener);
222 | return this;
223 | }
224 |
225 |
226 | /**
227 | * 注册监听socket行为
228 | *
229 | * @param socketActionListener
230 | */
231 | public EasySocket subscribeSocketAction(ISocketActionListener socketActionListener, String address) {
232 | getConnection(address).subscribeSocketAction(socketActionListener);
233 | return this;
234 | }
235 |
236 | /**
237 | * 开启心跳检测,作用于默认连接
238 | *
239 | * @param clientHeart
240 | * @return
241 | */
242 | public EasySocket startHeartBeat(byte[] clientHeart, HeartManager.HeartbeatListener listener) {
243 | getDefconnection().getHeartManager().startHeartbeat(clientHeart, listener);
244 | return this;
245 | }
246 |
247 | /**
248 | * 开启心跳检测
249 | *
250 | * @param clientHeart
251 | * @return
252 | */
253 | public EasySocket startHeartBeat(byte[] clientHeart, String address, HeartManager.HeartbeatListener listener) {
254 | getConnection(address).getHeartManager().startHeartbeat(clientHeart, listener);
255 | return this;
256 | }
257 |
258 |
259 | /**
260 | * 获取连接
261 | *
262 | * @return
263 | */
264 | public IConnectionManager getDefconnection() {
265 | if (defConnection == null) {
266 | throw new NotNullException("你还没有创建:" + defOptions.getSocketAddress().getIp() + ":" + defOptions.getSocketAddress().getPort()
267 | + "的Socket的连接,请使用com.easysocket.EasySocket.connect()方法创建一个默认的连接");
268 | }
269 | return defConnection;
270 | }
271 |
272 | /**
273 | * 获取连接
274 | *
275 | * @return
276 | */
277 | public IConnectionManager getConnection(String address) {
278 | IConnectionManager connectionManager = connectionHolder.getConnection(address);
279 | if (connectionManager == null) {
280 | throw new NotNullException("请先创建:" + address + "的Socket连接");
281 | }
282 | return connectionManager;
283 | }
284 |
285 | /**
286 | * 创建指定的socket连接,如果你的项目有多个socket连接,可以用这个方法创建更多的连接,
287 | * 当你使用带有socket地址为参数的方法的时候,作用的就是对应的连接
288 | * 比如:connect(String address)、 upMessage(byte[] message, String address)等
289 | *
290 | * @param socketOptions
291 | * @return
292 | */
293 | public IConnectionManager createSpecifyConnection(EasySocketOptions socketOptions, Context context) {
294 | this.context = context;
295 | IConnectionManager connectionManager = connectionHolder.getConnection(socketOptions.getSocketAddress(), socketOptions == null
296 | ? EasySocketOptions.getDefaultOptions() : socketOptions);
297 |
298 | connectionManager.connect();
299 | return connectionManager;
300 | }
301 |
302 | /**
303 | * 获取指定的连接
304 | *
305 | * @param socketAddress
306 | * @return
307 | */
308 | public IConnectionManager getSpecifyConnection(String socketAddress) {
309 | return connectionHolder.getConnection(socketAddress);
310 | }
311 |
312 | /**
313 | * 发送消息至指定的连接
314 | *
315 | * @param sender
316 | * @param socketAddress
317 | */
318 | public IConnectionManager upToSpecifyConnection(byte[] sender, String socketAddress) {
319 | IConnectionManager connect = getSpecifyConnection(socketAddress);
320 | if (connect != null) {
321 | connect.upBytes(sender);
322 | }
323 | return connect;
324 | }
325 |
326 | /**
327 | * 是否为debug
328 | *
329 | * @param debug
330 | */
331 | public void setDebug(boolean debug) {
332 | EasySocketOptions.setIsDebug(debug);
333 | }
334 |
335 | }
336 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/callback/ProgressDialogCallBack.java:
--------------------------------------------------------------------------------
1 |
2 | package com.easysocket.callback;
3 |
4 | import android.app.Dialog;
5 | import android.content.DialogInterface;
6 |
7 | import com.easysocket.entity.OriginReadData;
8 | import com.easysocket.exception.RequestCancelException;
9 | import com.easysocket.interfaces.callback.IProgressDialog;
10 | import com.easysocket.interfaces.callback.ProgressCancelListener;
11 |
12 |
13 | /**
14 | * 自定义带有加载进度框的回调
15 | */
16 | public abstract class ProgressDialogCallBack extends SuperCallBack implements ProgressCancelListener {
17 |
18 | private IProgressDialog progressDialog;
19 | private Dialog mDialog;
20 | private boolean isShowProgress = true;
21 |
22 | /**
23 | * @param
24 | */
25 | public ProgressDialogCallBack(IProgressDialog progressDialog, String callbackId) {
26 | super(callbackId);
27 | this.progressDialog = progressDialog;
28 | init(false);
29 | onStart();
30 | }
31 |
32 | /**
33 | * 自定义加载进度框,可以设置是否显示弹出框,是否可以取消
34 | *
35 | * @param progressDialog dialog
36 | * @param isShowProgress 是否显示进度
37 | * @param isCancel 对话框是否可以取消
38 | * @param
39 | */
40 | public ProgressDialogCallBack(IProgressDialog progressDialog, boolean isShowProgress,
41 | boolean isCancel, String callbackId) {
42 | super(callbackId);
43 | this.progressDialog = progressDialog;
44 | this.isShowProgress = isShowProgress;
45 | init(isCancel);
46 | onStart();
47 | }
48 |
49 | /**
50 | * 初始化
51 | *
52 | * @param isCancel
53 | */
54 | private void init(boolean isCancel) {
55 | if (progressDialog == null) return;
56 | mDialog = progressDialog.getDialog();
57 | if (mDialog == null) return;
58 | mDialog.setCancelable(isCancel);
59 | if (isCancel) {
60 | mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
61 | @Override
62 | public void onCancel(DialogInterface dialogInterface) {
63 | ProgressDialogCallBack.this.onCancelProgress();
64 | }
65 | });
66 | }
67 | }
68 |
69 | /**
70 | * 展示进度框
71 | */
72 | private void showProgress() {
73 | if (!isShowProgress) {
74 | return;
75 | }
76 | if (mDialog != null && !mDialog.isShowing()) {
77 | mDialog.show();
78 | }
79 | }
80 |
81 | /**
82 | * 取消进度框
83 | */
84 | private void dismissProgress() {
85 | if (!isShowProgress) {
86 | return;
87 | }
88 | if (mDialog != null && mDialog.isShowing()) {
89 | mDialog.dismiss();
90 | }
91 | }
92 |
93 | @Override
94 | public void onStart() {
95 | showProgress();
96 | }
97 |
98 | @Override
99 | public void onCompleted() {
100 | dismissProgress();
101 | }
102 |
103 | public abstract void onResponse(OriginReadData data);
104 |
105 | @Override
106 | public void onError(Exception e) {
107 | onCompleted();
108 | }
109 |
110 | @Override
111 | public void onCancelProgress() {
112 | onCompleted();
113 | onError(new RequestCancelException("网络请求被取消"));
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/callback/SimpleCallBack.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.callback;
2 |
3 |
4 | /**
5 | * Created by LXR ON 2018/8/29.
6 | */
7 | public abstract class SimpleCallBack extends SuperCallBack{
8 |
9 |
10 | public SimpleCallBack(String callbackId) {
11 | super(callbackId);
12 | }
13 |
14 | @Override
15 | public void onStart() {
16 | }
17 |
18 | @Override
19 | public void onCompleted() {
20 |
21 | }
22 |
23 | @Override
24 | public void onError(Exception e) {
25 |
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/callback/SuperCallBack.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.callback;
2 |
3 |
4 | import com.easysocket.entity.OriginReadData;
5 |
6 | /**
7 | * Created by LXR ON 2018/8/29.
8 | */
9 | public abstract class SuperCallBack {
10 | /**
11 | * 随机字符串,识别服务端应答消息的唯一标识
12 | */
13 | private String callbackId;
14 |
15 | /**
16 | * @param callbackId 识别服务端应答消息的唯一标识
17 | */
18 | public SuperCallBack(String callbackId) {
19 | this.callbackId = callbackId;
20 | }
21 |
22 | /**
23 | * 获取回调ID
24 | *
25 | * @return
26 | */
27 | public String getCallbackId() {
28 | return callbackId;
29 | }
30 |
31 | public abstract void onStart();
32 |
33 | public abstract void onCompleted();
34 |
35 | public abstract void onError(Exception e);
36 |
37 | public void onSuccess(OriginReadData data) {
38 | onCompleted();
39 | onResponse(data);
40 | }
41 |
42 | public abstract void onResponse(OriginReadData data);
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/CallbackIDFactory.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import com.easysocket.entity.OriginReadData;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/4
8 | * Note:要想实现EasySocket的回调功能,必须实现此工厂类,callbackID作为回调消息的唯一标识
9 | */
10 | public abstract class CallbackIDFactory {
11 | /**
12 | * 返回callbackID
13 | *
14 | * @param
15 | * @return 如果没有callbackID请返回null
16 | */
17 | public abstract String getCallbackID(OriginReadData data);
18 | }
19 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/DefaultMessageProtocol.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import com.easysocket.interfaces.config.IMessageProtocol;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 |
8 | /**
9 | * Author:Alex
10 | * Date:2019/5/31
11 | * Note:默认的消息协议,header为4个字节,保存消息体 body的长度
12 | */
13 | public class DefaultMessageProtocol implements IMessageProtocol {
14 | @Override
15 | public int getHeaderLength() {
16 | return 4; // 包头长度,用来保存body的长度值
17 | }
18 |
19 | @Override
20 | public int getBodyLength(byte[] header, ByteOrder byteOrder) {
21 | if (header == null || header.length < getHeaderLength()) {
22 | return 0;
23 | }
24 | ByteBuffer bb = ByteBuffer.wrap(header);
25 | bb.order(byteOrder);
26 | return bb.getInt(); // body的长度以int的形式保存在 header
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/DefaultX509ProtocolTrustManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import java.security.cert.CertificateException;
4 | import java.security.cert.X509Certificate;
5 |
6 | import javax.net.ssl.X509TrustManager;
7 |
8 | /**
9 | * Author:Alex
10 | * Date:2019/6/3
11 | * Note:
12 | */
13 | public class DefaultX509ProtocolTrustManager implements X509TrustManager {
14 | @Override
15 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
16 |
17 | }
18 |
19 | @Override
20 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
21 |
22 | }
23 |
24 | @Override
25 | public X509Certificate[] getAcceptedIssuers() {
26 | return new X509Certificate[0];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/EasySocketOptions.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import com.easysocket.connection.reconnect.AbsReconnection;
4 | import com.easysocket.connection.reconnect.DefaultReConnection;
5 | import com.easysocket.entity.SocketAddress;
6 | import com.easysocket.interfaces.config.IMessageProtocol;
7 |
8 | import java.nio.ByteOrder;
9 |
10 | /**
11 | * Author:Alex。
12 | * Date:2019/5/31。
13 | * Note:socket相关配置。
14 | */
15 | public class EasySocketOptions {
16 |
17 | /**
18 | * 是否调试模式
19 | */
20 | private static boolean isDebug = true;
21 | /**
22 | * 主机地址
23 | */
24 | private SocketAddress socketAddress;
25 | /**
26 | * 备用主机地址
27 | */
28 | private SocketAddress backupAddress;
29 | /**
30 | * 写入Socket管道的字节序
31 | */
32 | private ByteOrder writeOrder;
33 | /**
34 | * 从Socket读取字节时的字节序
35 | */
36 | private ByteOrder readOrder;
37 | /**
38 | * 从socket读取数据时遵从的数据包结构协议,在业务层进行定义
39 | */
40 | private IMessageProtocol messageProtocol;
41 | /**
42 | * 写数据时单个数据包的最大值
43 | */
44 | private int maxWriteBytes;
45 | /**
46 | * 读数据时单次读取最大缓存值,数值越大效率越高,但是系统消耗也越大
47 | */
48 | private int maxReadBytes;
49 | /**
50 | * 心跳频率/毫秒
51 | */
52 | private long heartbeatFreq;
53 | /**
54 | * 心跳最大的丢失次数,大于这个数据,将断开socket连接
55 | */
56 | private int maxHeartbeatLoseTimes;
57 | /**
58 | * 连接超时时间(毫秒)
59 | */
60 | private int connectTimeout;
61 | /**
62 | * 服务器返回数据的最大值(单位Mb),防止客户端内存溢出
63 | */
64 | private int maxResponseDataMb;
65 | /**
66 | * socket重连管理器
67 | */
68 | private AbsReconnection reconnectionManager;
69 | /**
70 | * 安全套接字相关配置
71 | */
72 | private SocketSSLConfig easySSLConfig;
73 | /**
74 | * socket工厂
75 | */
76 | private SocketFactory socketFactory;
77 | /**
78 | * 实现回调功能需要callbackID,而callbackID是保存在发送消息和应答消息中的,此工厂用来获取socket消息中
79 | * 保存callbackID值的key,比如json格式中的key-value中的key
80 | */
81 | private CallbackIDFactory callbackIDFactory;
82 | /**
83 | * 请求超时时间,单位毫秒
84 | */
85 | private long requestTimeout;
86 | /**
87 | * 是否开启请求超时检测
88 | */
89 | private boolean isOpenRequestTimeout;
90 |
91 | /**
92 | * IO字符流的编码方式,默认utf-8
93 | */
94 | private String charsetName;
95 |
96 | public boolean isDebug() {
97 | return isDebug;
98 | }
99 |
100 |
101 | /**
102 | * 静态内部类
103 | */
104 | public static class Builder {
105 | EasySocketOptions socketOptions;
106 |
107 | // 首先获得一个默认的配置
108 | public Builder() {
109 | this(getDefaultOptions());
110 | }
111 |
112 | public Builder(EasySocketOptions defaultOptions) {
113 | socketOptions = defaultOptions;
114 | }
115 |
116 | /**
117 | * 设置socket 主机地址
118 | *
119 | * @param socketAddress
120 | * @return
121 | */
122 | public Builder setSocketAddress(SocketAddress socketAddress) {
123 | socketOptions.socketAddress = socketAddress;
124 | return this;
125 | }
126 |
127 | /**
128 | * 设置备用的主机地址
129 | *
130 | * @param backupAddress
131 | * @return
132 | */
133 | public Builder setBackupAddress(SocketAddress backupAddress) {
134 | socketOptions.backupAddress = backupAddress;
135 | return this;
136 | }
137 |
138 | /**
139 | * 设置是否开启请求超时的检测
140 | *
141 | * @param openRequestTimeout
142 | * @return
143 | */
144 | public Builder setOpenRequestTimeout(boolean openRequestTimeout) {
145 | socketOptions.isOpenRequestTimeout = openRequestTimeout;
146 | return this;
147 | }
148 |
149 | /**
150 | * 设置请求超时时间
151 | *
152 | * @param requestTimeout 毫秒
153 | * @return
154 | */
155 | public Builder setRequestTimeout(long requestTimeout) {
156 | socketOptions.requestTimeout = requestTimeout;
157 | return this;
158 | }
159 |
160 | /**
161 | * 设置请求ack的工厂
162 | *
163 | * @param callbackIDFactory
164 | */
165 | public Builder setCallbackIDFactory(CallbackIDFactory callbackIDFactory) {
166 | socketOptions.callbackIDFactory = callbackIDFactory;
167 | return this;
168 | }
169 |
170 |
171 | /**
172 | * 设置写数据的字节顺序
173 | *
174 | * @param writeOrder
175 | * @return
176 | */
177 | public Builder setWriteOrder(ByteOrder writeOrder) {
178 | socketOptions.writeOrder = writeOrder;
179 | return this;
180 | }
181 |
182 | /**
183 | * 设置读数据的字节顺序
184 | *
185 | * @param readOrder
186 | * @return
187 | */
188 | public Builder setReadOrder(ByteOrder readOrder) {
189 | socketOptions.readOrder = readOrder;
190 | return this;
191 | }
192 |
193 | /**
194 | * 设置读取数据的数据结构协议
195 | *
196 | * @param readerProtocol
197 | * @return
198 | */
199 | public Builder setReaderProtocol(IMessageProtocol readerProtocol) {
200 | socketOptions.messageProtocol = readerProtocol;
201 | return this;
202 | }
203 |
204 | /**
205 | * 设置写数据时单个数据包的最大值
206 | *
207 | * @param maxWriteBytes
208 | * @return
209 | */
210 | public Builder setMaxWriteBytes(int maxWriteBytes) {
211 | socketOptions.maxWriteBytes = maxWriteBytes;
212 | return this;
213 | }
214 |
215 | /**
216 | * 设置读数据时单次读取的最大缓存值
217 | *
218 | * @param maxReadBytes
219 | * @return
220 | */
221 | public Builder setMaxReadBytes(int maxReadBytes) {
222 | socketOptions.maxReadBytes = maxReadBytes;
223 | return this;
224 | }
225 |
226 | /**
227 | * 设置心跳发送频率,单位毫秒
228 | *
229 | * @param heartbeatFreq
230 | * @return
231 | */
232 | public Builder setHeartbeatFreq(long heartbeatFreq) {
233 | socketOptions.heartbeatFreq = heartbeatFreq;
234 | return this;
235 | }
236 |
237 | /**
238 | * 设置心跳丢失的最大允许数,如果超过这个最大数就断开socket连接
239 | *
240 | * @param maxHeartbeatLoseTimes
241 | * @return
242 | */
243 | public Builder setMaxHeartbeatLoseTimes(int maxHeartbeatLoseTimes) {
244 | socketOptions.maxHeartbeatLoseTimes = maxHeartbeatLoseTimes;
245 | return this;
246 | }
247 |
248 | /**
249 | * 设置连接超时时间
250 | *
251 | * @param connectTimeout
252 | * @return
253 | */
254 | public Builder setConnectTimeout(int connectTimeout) {
255 | socketOptions.connectTimeout = connectTimeout;
256 | return this;
257 | }
258 |
259 | /**
260 | * 设置服务器返回数据的允许的最大值,单位兆
261 | *
262 | * @param maxResponseDataMb
263 | * @return
264 | */
265 | public Builder setMaxResponseDataMb(int maxResponseDataMb) {
266 | socketOptions.maxResponseDataMb = maxResponseDataMb;
267 | return this;
268 | }
269 |
270 | /**
271 | * 设置重连管理器
272 | *
273 | * @param reconnectionManager
274 | * @return
275 | */
276 | public Builder setReconnectionManager(AbsReconnection reconnectionManager) {
277 | socketOptions.reconnectionManager = reconnectionManager;
278 | return this;
279 | }
280 |
281 | /**
282 | * 安全套接字的配置
283 | *
284 | * @param easySSLConfig
285 | * @return
286 | */
287 | public Builder setEasySSLConfig(SocketSSLConfig easySSLConfig) {
288 | socketOptions.easySSLConfig = easySSLConfig;
289 | return this;
290 | }
291 |
292 | /**
293 | * 自定义创建socket工厂
294 | *
295 | * @param socketFactory
296 | * @return
297 | */
298 | public Builder setSocketFactory(SocketFactory socketFactory) {
299 | socketOptions.socketFactory = socketFactory;
300 | return this;
301 | }
302 |
303 | public Builder setCharsetName(String charsetName) {
304 | socketOptions.charsetName = charsetName;
305 | return this;
306 | }
307 |
308 | public EasySocketOptions build() {
309 | return socketOptions;
310 | }
311 | }
312 |
313 |
314 | /**
315 | * 获取默认的配置
316 | *
317 | * @return
318 | */
319 | public static EasySocketOptions getDefaultOptions() {
320 | EasySocketOptions options = new EasySocketOptions();
321 | options.socketAddress = null;
322 | options.backupAddress = null;
323 | options.heartbeatFreq = 5 * 1000;
324 | options.messageProtocol = null;
325 | options.maxResponseDataMb = 5;
326 | options.connectTimeout = 5 * 1000; // 连接超时默认5秒
327 | options.maxWriteBytes = 100;
328 | options.maxReadBytes = 50;
329 | options.readOrder = ByteOrder.BIG_ENDIAN;
330 | options.writeOrder = ByteOrder.BIG_ENDIAN;
331 | options.maxHeartbeatLoseTimes = 5;
332 | options.reconnectionManager = new DefaultReConnection();
333 | options.easySSLConfig = null;
334 | options.socketFactory = null;
335 | options.callbackIDFactory = null;
336 | options.requestTimeout = 10 * 1000; // 默认十秒
337 | options.isOpenRequestTimeout = true; // 默认开启
338 | options.charsetName = "UTF-8";
339 | return options;
340 | }
341 |
342 | public String getCharsetName() {
343 | return charsetName;
344 | }
345 |
346 | public ByteOrder getWriteOrder() {
347 | return writeOrder;
348 | }
349 |
350 | public ByteOrder getReadOrder() {
351 | return readOrder;
352 | }
353 |
354 | public IMessageProtocol getMessageProtocol() {
355 | return messageProtocol;
356 | }
357 |
358 | public int getMaxWriteBytes() {
359 | return maxWriteBytes;
360 | }
361 |
362 | public int getMaxReadBytes() {
363 | return maxReadBytes;
364 | }
365 |
366 | public long getHeartbeatFreq() {
367 | return heartbeatFreq;
368 | }
369 |
370 | public int getMaxHeartbeatLoseTimes() {
371 | return maxHeartbeatLoseTimes;
372 | }
373 |
374 | public int getConnectTimeout() {
375 | return connectTimeout;
376 | }
377 |
378 | public int getMaxResponseDataMb() {
379 | return maxResponseDataMb;
380 | }
381 |
382 | public AbsReconnection getReconnectionManager() {
383 | return reconnectionManager;
384 | }
385 |
386 | public SocketSSLConfig getEasySSLConfig() {
387 | return easySSLConfig;
388 | }
389 |
390 | public SocketFactory getSocketFactory() {
391 | return socketFactory;
392 | }
393 |
394 | public long getRequestTimeout() {
395 | return requestTimeout;
396 | }
397 |
398 | public boolean isOpenRequestTimeout() {
399 | return isOpenRequestTimeout;
400 | }
401 |
402 | public CallbackIDFactory getCallbackIDFactory() {
403 | return callbackIDFactory;
404 | }
405 |
406 | public static void setIsDebug(boolean isDebug) {
407 | EasySocketOptions.isDebug = isDebug;
408 | }
409 |
410 | public void setWriteOrder(ByteOrder writeOrder) {
411 | this.writeOrder = writeOrder;
412 | }
413 |
414 | public void setReadOrder(ByteOrder readOrder) {
415 | this.readOrder = readOrder;
416 | }
417 |
418 | public void setMessageProtocol(IMessageProtocol messageProtocol) {
419 | this.messageProtocol = messageProtocol;
420 | }
421 |
422 | public void setMaxWriteBytes(int maxWriteBytes) {
423 | this.maxWriteBytes = maxWriteBytes;
424 | }
425 |
426 | public void setMaxReadBytes(int maxReadBytes) {
427 | this.maxReadBytes = maxReadBytes;
428 | }
429 |
430 | public void setHeartbeatFreq(long heartbeatFreq) {
431 | this.heartbeatFreq = heartbeatFreq;
432 | }
433 |
434 | public void setMaxHeartbeatLoseTimes(int maxHeartbeatLoseTimes) {
435 | this.maxHeartbeatLoseTimes = maxHeartbeatLoseTimes;
436 | }
437 |
438 | public void setConnectTimeout(int connectTimeout) {
439 | this.connectTimeout = connectTimeout;
440 | }
441 |
442 | public void setMaxResponseDataMb(int maxResponseDataMb) {
443 | this.maxResponseDataMb = maxResponseDataMb;
444 | }
445 |
446 | public void setReconnectionManager(AbsReconnection reconnectionManager) {
447 | this.reconnectionManager = reconnectionManager;
448 | }
449 |
450 | public void setEasySSLConfig(SocketSSLConfig easySSLConfig) {
451 | this.easySSLConfig = easySSLConfig;
452 | }
453 |
454 | public void setSocketFactory(SocketFactory socketFactory) {
455 | this.socketFactory = socketFactory;
456 | }
457 |
458 | public void setCallbackIDFactory(CallbackIDFactory callbackIDFactory) {
459 | this.callbackIDFactory = callbackIDFactory;
460 | }
461 |
462 | public void setRequestTimeout(long requestTimeout) {
463 | this.requestTimeout = requestTimeout;
464 | }
465 |
466 | public void setOpenRequestTimeout(boolean openRequestTimeout) {
467 | isOpenRequestTimeout = openRequestTimeout;
468 | }
469 |
470 | public SocketAddress getSocketAddress() {
471 | return socketAddress;
472 | }
473 |
474 | public SocketAddress getBackupAddress() {
475 | return backupAddress;
476 | }
477 |
478 | }
479 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/SocketFactory.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import com.easysocket.entity.SocketAddress;
4 |
5 | import java.net.Socket;
6 |
7 | /**
8 | * Author:Alex
9 | * Date:2019/5/31
10 | * Note:socket工厂
11 | */
12 | public abstract class SocketFactory {
13 | public abstract Socket createSocket(SocketAddress info, EasySocketOptions options) throws Exception;
14 | }
15 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/config/SocketSSLConfig.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.config;
2 |
3 | import javax.net.ssl.KeyManager;
4 | import javax.net.ssl.SSLSocketFactory;
5 | import javax.net.ssl.TrustManager;
6 |
7 | /**
8 | * socket的ssl配置
9 | */
10 |
11 | public class SocketSSLConfig {
12 | /**
13 | * 安全协议名称(缺省为SSL)
14 | */
15 | private String mProtocol;
16 | /**
17 | * 信任证书管理器(缺省为X509)
18 | */
19 | private TrustManager[] mTrustManagers;
20 | /**
21 | * 证书秘钥管理器(缺省为null)
22 | */
23 | private KeyManager[] mKeyManagers;
24 | /**
25 | * 自定义SSLFactory(缺省为null)
26 | */
27 | private SSLSocketFactory mCustomSSLFactory;
28 |
29 | private SocketSSLConfig() {
30 |
31 | }
32 |
33 | public static class Builder {
34 |
35 | private SocketSSLConfig mConfig;
36 |
37 | public Builder() {
38 | mConfig = new SocketSSLConfig();
39 | }
40 |
41 | public Builder setProtocol(String protocol) {
42 | mConfig.mProtocol = protocol;
43 | return this;
44 | }
45 |
46 | public Builder setTrustManagers(TrustManager[] trustManagers) {
47 | mConfig.mTrustManagers = trustManagers;
48 | return this;
49 | }
50 |
51 | public Builder setKeyManagers(KeyManager[] keyManagers) {
52 | mConfig.mKeyManagers = keyManagers;
53 | return this;
54 | }
55 |
56 | public Builder setCustomSSLFactory(SSLSocketFactory customSSLFactory) {
57 | mConfig.mCustomSSLFactory = customSSLFactory;
58 | return this;
59 | }
60 |
61 | public SocketSSLConfig build() {
62 | return mConfig;
63 | }
64 | }
65 |
66 | public KeyManager[] getKeyManagers() {
67 | return mKeyManagers;
68 | }
69 |
70 | public String getProtocol() {
71 | return mProtocol;
72 | }
73 |
74 | public TrustManager[] getTrustManagers() {
75 | return mTrustManagers;
76 | }
77 |
78 | public SSLSocketFactory getCustomSSLFactory() {
79 | return mCustomSSLFactory;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/action/IOAction.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.action;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/3
6 | * Note:
7 | */
8 | public interface IOAction {
9 | // 收到消息响应
10 | String ACTION_READ_COMPLETE = "action_read_complete";
11 | }
12 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/action/SocketAction.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.action;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface SocketAction {
9 | // 连接成功
10 | String ACTION_CONN_SUCCESS="action_conn_success";
11 | // 连接失败
12 | String ACTION_CONN_FAIL="action_conn_fail";
13 | // 断开连接
14 | String ACTION_DISCONNECTION="action_disconnection";
15 | }
16 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/action/SocketStatus.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.action;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:连接状态
7 | */
8 | public interface SocketStatus {
9 | // 已断开连接
10 | int SOCKET_DISCONNECTED = 0;
11 | // 正在连接
12 | int SOCKET_CONNECTING = 1;
13 | // 已连接
14 | int SOCKET_CONNECTED = 2;
15 | // 正在断开连接
16 | int SOCKET_DISCONNECTING =3;
17 | }
18 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/connect/SuperConnection.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.connect;
2 |
3 | import com.easysocket.EasySocket;
4 | import com.easysocket.callback.SuperCallBack;
5 | import com.easysocket.config.EasySocketOptions;
6 | import com.easysocket.connection.action.SocketAction;
7 | import com.easysocket.connection.action.SocketStatus;
8 | import com.easysocket.connection.dispatcher.CallbackResponseDispatcher;
9 | import com.easysocket.connection.dispatcher.SocketActionDispatcher;
10 | import com.easysocket.connection.heartbeat.HeartManager;
11 | import com.easysocket.connection.iowork.IOManager;
12 | import com.easysocket.connection.reconnect.AbsReconnection;
13 | import com.easysocket.entity.SocketAddress;
14 | import com.easysocket.entity.basemsg.SuperCallbackSender;
15 | import com.easysocket.exception.NotNullException;
16 | import com.easysocket.interfaces.config.IConnectionSwitchListener;
17 | import com.easysocket.interfaces.conn.IConnectionManager;
18 | import com.easysocket.interfaces.conn.ISocketActionListener;
19 | import com.easysocket.utils.LogUtil;
20 | import com.easysocket.utils.Utils;
21 |
22 | import java.io.IOException;
23 | import java.util.concurrent.ExecutorService;
24 | import java.util.concurrent.Executors;
25 | import java.util.concurrent.atomic.AtomicInteger;
26 |
27 | /**
28 | * Author:Alex
29 | * Date:2019/5/29
30 | * Note:socket连接的超类
31 | */
32 | public abstract class SuperConnection implements IConnectionManager {
33 |
34 | /**
35 | * 连接状态,初始值为断开连接
36 | */
37 | protected final AtomicInteger connectionStatus = new AtomicInteger(SocketStatus.SOCKET_DISCONNECTED);
38 | /**
39 | * 连接线程
40 | */
41 | private ExecutorService connExecutor;
42 | /**
43 | * socket地址信息
44 | */
45 | protected SocketAddress socketAddress;
46 | /**
47 | * socket行为分发器
48 | */
49 | private SocketActionDispatcher actionDispatcher;
50 | /**
51 | * 重连管理器
52 | */
53 | private AbsReconnection reconnection;
54 | /**
55 | * io管理器
56 | */
57 | private IOManager ioManager;
58 | /**
59 | * 心跳管理器
60 | */
61 | private HeartManager heartManager;
62 | /**
63 | * 配置信息
64 | */
65 | protected EasySocketOptions socketOptions;
66 | /**
67 | * socket回调消息的分发器
68 | */
69 | private CallbackResponseDispatcher callbackResponseDispatcher;
70 | /**
71 | * 连接切换的监听
72 | */
73 | private IConnectionSwitchListener connectionSwitchListener;
74 |
75 | public SuperConnection(SocketAddress socketAddress) {
76 | this.socketAddress = socketAddress;
77 | actionDispatcher = new SocketActionDispatcher(this, socketAddress);
78 | }
79 |
80 | @Override
81 | public void subscribeSocketAction(ISocketActionListener iSocketActionListener) {
82 | actionDispatcher.subscribe(iSocketActionListener);
83 | }
84 |
85 | @Override
86 | public void unSubscribeSocketAction(ISocketActionListener iSocketActionListener) {
87 | actionDispatcher.unsubscribe(iSocketActionListener);
88 | }
89 |
90 | @Override
91 | public synchronized IConnectionManager setOptions(EasySocketOptions socketOptions) {
92 | if (socketOptions == null) return this;
93 |
94 | this.socketOptions = socketOptions;
95 |
96 | if (ioManager != null)
97 | ioManager.setOptions(socketOptions);
98 |
99 | if (heartManager != null)
100 | heartManager.setOptions(socketOptions);
101 |
102 | if (callbackResponseDispatcher != null)
103 | callbackResponseDispatcher.setSocketOptions(socketOptions);
104 |
105 | // 更改了重连器
106 | if (reconnection != null && !reconnection.equals(socketOptions.getReconnectionManager())) {
107 | reconnection.detach();
108 | reconnection = socketOptions.getReconnectionManager();
109 | reconnection.attach(this);
110 | }
111 | return this;
112 | }
113 |
114 | @Override
115 | public EasySocketOptions getOptions() {
116 | return socketOptions;
117 | }
118 |
119 | @Override
120 | public synchronized void connect() {
121 | LogUtil.d("---> socket开始连接");
122 | if (socketAddress.getIp() == null) {
123 | throw new NotNullException("请检查是否设置了IP地址");
124 | }
125 | // 正在连接
126 | connectionStatus.set(SocketStatus.SOCKET_CONNECTING);
127 |
128 | // 心跳管理器
129 | if (heartManager == null) {
130 | heartManager = new HeartManager(this, actionDispatcher);
131 | }
132 |
133 | // 重连管理器
134 | if (reconnection != null) {
135 | reconnection.detach();
136 | }
137 | reconnection = socketOptions.getReconnectionManager();
138 | if (reconnection != null) {
139 | reconnection.attach(this);
140 | }
141 |
142 | // 开启分发消息线程
143 | if (actionDispatcher != null) {
144 | actionDispatcher.startDispatchThread();
145 | }
146 |
147 | // 开启连接线程
148 | if (connExecutor == null || connExecutor.isShutdown()) {
149 | // 核心线程数为0,非核心线程数可以有Integer.MAX_VALUE个,存活时间为60秒,适合于在不断进行连接的情况下,避免重复创建和销毁线程
150 | connExecutor = Executors.newCachedThreadPool();
151 | }
152 | // 执行连接任务
153 | connExecutor.execute(connTask);
154 | }
155 |
156 | @Override
157 | public synchronized void disconnect(boolean isNeedReconnect) {
158 | // 判断当前socket的连接状态
159 | if (connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED) {
160 | return;
161 | }
162 | // 正在重连中
163 | if (isNeedReconnect && reconnection.isReconning()) {
164 | return;
165 | }
166 | // 正在断开连接
167 | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTING);
168 |
169 | // 开启断开连接线程
170 | String info = socketAddress.getIp() + " : " + socketAddress.getPort();
171 | Thread disconnThread = new DisconnectThread(isNeedReconnect, "disconn thread:" + info);
172 | disconnThread.setDaemon(true);
173 | disconnThread.start();
174 | }
175 |
176 | /**
177 | * 断开连接线程
178 | */
179 | private class DisconnectThread extends Thread {
180 | boolean isNeedReconnect; // 当前连接的断开是否需要自动重连
181 |
182 | public DisconnectThread(boolean isNeedReconnect, String name) {
183 | super(name);
184 | this.isNeedReconnect = isNeedReconnect;
185 | }
186 |
187 | @Override
188 | public void run() {
189 | try {
190 | // 关闭io线程
191 | if (ioManager != null)
192 | ioManager.closeIO();
193 | // 关闭回调分发器线程
194 | if (callbackResponseDispatcher != null)
195 | callbackResponseDispatcher.shutdownThread();
196 | // 关闭连接线程
197 | if (connExecutor != null && !connExecutor.isShutdown()) {
198 | connExecutor.shutdown();
199 | connExecutor = null;
200 | }
201 | // 关闭连接
202 | closeConnection();
203 | LogUtil.d("---> 关闭socket连接");
204 | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
205 | actionDispatcher.dispatchAction(SocketAction.ACTION_DISCONNECTION, new Boolean(isNeedReconnect));
206 | } catch (IOException e) {
207 | // 断开连接发生异常
208 | e.printStackTrace();
209 | }
210 | }
211 | }
212 |
213 | // 连接任务
214 | private Runnable connTask = new Runnable() {
215 | @Override
216 | public void run() {
217 | try {
218 | openConnection();
219 | } catch (Exception e) {
220 | // 连接异常
221 | e.printStackTrace();
222 | LogUtil.d("---> socket连接失败");
223 | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
224 | // 第二个参数指需要重连
225 | actionDispatcher.dispatchAction(SocketAction.ACTION_CONN_FAIL, new Boolean(true));
226 |
227 | }
228 | }
229 | };
230 |
231 | /**
232 | * 连接成功
233 | */
234 | protected void onConnectionOpened() {
235 | LogUtil.d("---> socket连接成功");
236 | actionDispatcher.dispatchAction(SocketAction.ACTION_CONN_SUCCESS);
237 | connectionStatus.set(SocketStatus.SOCKET_CONNECTED);
238 | openSocketManager();
239 | }
240 |
241 | // 开启socket相关管理器
242 | private void openSocketManager() {
243 | if (callbackResponseDispatcher == null)
244 | callbackResponseDispatcher = new CallbackResponseDispatcher(this);
245 | if (ioManager == null) {
246 | ioManager = new IOManager(this, actionDispatcher);
247 | }
248 | ioManager.startIO();
249 |
250 | // 启动相关线程
251 | callbackResponseDispatcher.engineThread();
252 | ioManager.startIO();
253 | }
254 |
255 | // 切换了主机IP和端口
256 | @Override
257 | public synchronized void switchHost(SocketAddress socketAddress) {
258 | if (socketAddress != null) {
259 | SocketAddress oldAddress = this.socketAddress;
260 | this.socketAddress = socketAddress;
261 |
262 | if (actionDispatcher != null)
263 | actionDispatcher.setSocketAddress(socketAddress);
264 | // 切换主机
265 | if (connectionSwitchListener != null) {
266 | connectionSwitchListener.onSwitchConnectionInfo(this, oldAddress, socketAddress);
267 | }
268 | }
269 |
270 | }
271 |
272 | public void setOnConnectionSwitchListener(IConnectionSwitchListener listener) {
273 | connectionSwitchListener = listener;
274 | }
275 |
276 | @Override
277 | public boolean isConnectViable() {
278 | // 当前socket是否处于可连接状态
279 | return Utils.isNetConnected(EasySocket.getInstance().getContext()) && connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED;
280 | }
281 |
282 | @Override
283 | public int getConnectionStatus() {
284 | return connectionStatus.get();
285 | }
286 |
287 | /**
288 | * 打开连接
289 | *
290 | * @throws IOException
291 | */
292 | protected abstract void openConnection() throws Exception;
293 |
294 | /**
295 | * 关闭连接
296 | *
297 | * @throws IOException
298 | */
299 | protected abstract void closeConnection() throws IOException;
300 |
301 | /**
302 | * 发送bytes数据
303 | *
304 | * @param bytes
305 | * @return
306 | */
307 | private IConnectionManager sendBytes(byte[] bytes) {
308 | if (ioManager == null || connectionStatus.get() != SocketStatus.SOCKET_CONNECTED) {
309 | return this;
310 | }
311 | ioManager.sendBytes(bytes);
312 | return this;
313 | }
314 |
315 | @Override
316 | public void onCallBack(SuperCallBack callBack) {
317 | callbackResponseDispatcher.addSocketCallback(callBack);
318 | }
319 |
320 |
321 | @Override
322 | public synchronized IConnectionManager upBytes(byte[] bytes) {
323 | sendBytes(bytes);
324 | return this;
325 | }
326 |
327 | @Override
328 | public synchronized IConnectionManager upCallbackMessage(SuperCallbackSender sender) {
329 | callbackResponseDispatcher.checkCallbackSender(sender);
330 | // 发送
331 | sendBytes(sender.pack());
332 | return this;
333 | }
334 |
335 |
336 | @Override
337 | public HeartManager getHeartManager() {
338 | return heartManager;
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/connect/TcpConnection.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.connect;
2 |
3 | import com.easysocket.config.DefaultX509ProtocolTrustManager;
4 | import com.easysocket.config.SocketSSLConfig;
5 | import com.easysocket.connection.action.SocketStatus;
6 | import com.easysocket.entity.SocketAddress;
7 | import com.easysocket.utils.LogUtil;
8 | import com.easysocket.utils.Utils;
9 |
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.OutputStream;
13 | import java.net.InetSocketAddress;
14 | import java.net.Socket;
15 | import java.security.SecureRandom;
16 |
17 | import javax.net.ssl.SSLContext;
18 | import javax.net.ssl.SSLSocketFactory;
19 | import javax.net.ssl.TrustManager;
20 |
21 | /**
22 | * Author:Alex
23 | * Date:2019/5/29
24 | * Note:tcp连接
25 | */
26 | public class TcpConnection extends SuperConnection {
27 | /**
28 | * socket对象
29 | */
30 | private Socket socket;
31 |
32 | public TcpConnection(SocketAddress socketAddress) {
33 | super(socketAddress);
34 | }
35 |
36 | @Override
37 | protected void openConnection() throws Exception {
38 | try {
39 | socket = getSocket();
40 | } catch (Exception e) {
41 | e.printStackTrace();
42 | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED); // 设置为未连接
43 | throw new RuntimeException("创建socket失败");
44 | }
45 |
46 | // 进行socket连接
47 | socket.connect(new InetSocketAddress(socketAddress.getIp(), socketAddress.getPort()), socketOptions.getConnectTimeout());
48 |
49 | // 关闭Nagle算法,无论TCP数据报大小,立即发送
50 | socket.setTcpNoDelay(true);
51 | // 连接已经打开
52 | if (socket.isConnected() && !socket.isClosed()) {
53 | onConnectionOpened();
54 | }
55 | }
56 |
57 | @Override
58 | protected void closeConnection() throws IOException {
59 | if (socket != null)
60 | socket.close();
61 | }
62 |
63 | /**
64 | * 根据配置信息获取对应的socket
65 | *
66 | * @return
67 | */
68 | private synchronized Socket getSocket() throws Exception {
69 | // 自定义的socket生成工厂
70 | if (socketOptions.getSocketFactory() != null) {
71 | return socketOptions.getSocketFactory().createSocket(socketAddress, socketOptions);
72 | }
73 | // 默认操作
74 | SocketSSLConfig config = socketOptions.getEasySSLConfig();
75 | if (config == null) {
76 | return new Socket();
77 | }
78 | // 获取SSL配置工厂
79 | SSLSocketFactory factory = config.getCustomSSLFactory();
80 | if (factory == null) {
81 | String protocol = "SSL";
82 | if (!Utils.isStringEmpty(config.getProtocol())) {
83 | protocol = config.getProtocol();
84 | }
85 |
86 | TrustManager[] trustManagers = config.getTrustManagers();
87 | if (trustManagers == null || trustManagers.length == 0) {
88 | // 缺省信任所有证书
89 | trustManagers = new TrustManager[]{new DefaultX509ProtocolTrustManager()};
90 | }
91 |
92 | try {
93 | SSLContext sslContext = SSLContext.getInstance(protocol);
94 | sslContext.init(config.getKeyManagers(), trustManagers, new SecureRandom());
95 | return sslContext.getSocketFactory().createSocket();
96 | } catch (Exception e) {
97 | if (socketOptions.isDebug()) {
98 | e.printStackTrace();
99 | }
100 | LogUtil.e(e.getMessage());
101 | return new Socket();
102 | }
103 |
104 | } else {
105 | try {
106 | return factory.createSocket();
107 | } catch (IOException e) {
108 | if (socketOptions.isDebug()) {
109 | e.printStackTrace();
110 | }
111 | LogUtil.e(e.getMessage());
112 | return new Socket();
113 | }
114 | }
115 | }
116 |
117 |
118 | @Override
119 | public InputStream getInputStream() {
120 | if (socket != null && socket.isConnected() && !socket.isClosed()) {
121 | try {
122 | return socket.getInputStream();
123 | } catch (IOException e) {
124 | e.printStackTrace();
125 | }
126 | }
127 | return null;
128 | }
129 |
130 | @Override
131 | public OutputStream getOutStream() {
132 | if (socket != null && socket.isConnected() && !socket.isClosed()) {
133 | try {
134 | return socket.getOutputStream();
135 | } catch (IOException e) {
136 | e.printStackTrace();
137 | }
138 | }
139 | return null;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/dispatcher/CallbackResponseDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.dispatcher;
2 |
3 | import com.easysocket.callback.SuperCallBack;
4 | import com.easysocket.config.EasySocketOptions;
5 | import com.easysocket.entity.OriginReadData;
6 | import com.easysocket.entity.SocketAddress;
7 | import com.easysocket.entity.basemsg.SuperCallbackSender;
8 | import com.easysocket.exception.RequestTimeOutException;
9 | import com.easysocket.interfaces.conn.IConnectionManager;
10 | import com.easysocket.interfaces.conn.SocketActionListener;
11 | import com.easysocket.utils.LogUtil;
12 | import com.easysocket.utils.Utils;
13 |
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.concurrent.DelayQueue;
17 | import java.util.concurrent.Delayed;
18 | import java.util.concurrent.ExecutorService;
19 | import java.util.concurrent.Executors;
20 | import java.util.concurrent.TimeUnit;
21 |
22 | /**
23 | * Author:Alex
24 | * Date:2019/6/4
25 | * Note:回调消息分发器
26 | */
27 | public class CallbackResponseDispatcher {
28 | /**
29 | * 保存发送的每个回调消息的监听实例,key为回调标识callbackId,这样回调消息有反馈的时候,就可以找到并调用
30 | * 对应的监听对象
31 | */
32 | private Map callbacks = new HashMap<>();
33 | /**
34 | * 保存需要进行超时检测的请求,这是一个延时队列,元素超时的时候会被取出来
35 | */
36 | private DelayQueue timeoutQueue = new DelayQueue<>();
37 | /**
38 | * 超时检测的线程管理器
39 | */
40 | private ExecutorService timeoutExecutor;
41 |
42 | /**
43 | * 连接管理
44 | */
45 | IConnectionManager connectionManager;
46 |
47 | private EasySocketOptions socketOptions;
48 |
49 |
50 | public CallbackResponseDispatcher(IConnectionManager connectionManager) {
51 | this.connectionManager = connectionManager;
52 | socketOptions = connectionManager.getOptions();
53 | // 注册监听
54 | connectionManager.subscribeSocketAction(socketActionListener);
55 | // 开始超时检测线程
56 | engineThread();
57 | }
58 |
59 | /**
60 | * 设置socketoptions
61 | *
62 | * @param socketOptions
63 | */
64 | public void setSocketOptions(EasySocketOptions socketOptions) {
65 | this.socketOptions = socketOptions;
66 | }
67 |
68 | /**
69 | * 超时检测线程
70 | */
71 | public void engineThread() {
72 | if (timeoutExecutor == null || timeoutExecutor.isShutdown()) {
73 | timeoutExecutor = Executors.newSingleThreadExecutor();
74 | timeoutExecutor.execute(new Runnable() {
75 | @Override
76 | public void run() {
77 | try {
78 | // 只有超时的元素才会被取出,没有的话会被等待
79 | timeoutItem item = timeoutQueue.take();
80 | if (item != null) {
81 | SuperCallBack callBack = callbacks.remove(item.callbackId);
82 | if (callBack != null)
83 | callBack.onError(new RequestTimeOutException("request timeout"));
84 | }
85 | } catch (InterruptedException e) {
86 | e.printStackTrace();
87 | }
88 | // 继续循环
89 | if (timeoutExecutor != null && !timeoutExecutor.isShutdown()) {
90 | run();
91 | }
92 | }
93 | });
94 | }
95 | }
96 |
97 | /**
98 | * 关闭线程
99 | */
100 | public void shutdownThread() {
101 | if (timeoutExecutor != null && !timeoutExecutor.isShutdown()) {
102 | // shutdown和shutdownNow的主要区别是前者中断未执行的线程,后者中断所有线程
103 | timeoutExecutor.shutdownNow();
104 | timeoutExecutor = null;
105 | }
106 |
107 | }
108 |
109 | /**
110 | * socket行为监听,重写反馈消息的回调方法
111 | */
112 | private SocketActionListener socketActionListener = new SocketActionListener() {
113 | @Override
114 | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) {
115 | if (callbacks.size() == 0) return;
116 | if (socketOptions.getCallbackIDFactory() == null) return;
117 | // 获取回调ID
118 | String callbackID = socketOptions.getCallbackIDFactory().getCallbackID(originReadData);
119 | if (callbackID != null) {
120 | // 获取callbackID对应的callback
121 | SuperCallBack callBack = callbacks.get(callbackID);
122 | if (callBack != null) {
123 | // 回调
124 | callBack.onSuccess(originReadData);
125 | callbacks.remove(callbackID); // 移除完成任务的callback
126 | LogUtil.d("移除的callbackId-->" + callbackID);
127 | }
128 | }
129 | }
130 | };
131 |
132 |
133 | /**
134 | * 每发一条回调消息都要在这里添加监听对象
135 | *
136 | * @param superCallBack
137 | */
138 | public void addSocketCallback(SuperCallBack superCallBack) {
139 | callbacks.put(superCallBack.getCallbackId(), superCallBack);
140 | // 放入延时队列
141 | long delayTime = socketOptions == null ?
142 | EasySocketOptions.getDefaultOptions().getRequestTimeout() : socketOptions.getRequestTimeout();
143 | timeoutQueue.add(new timeoutItem(superCallBack.getCallbackId(), delayTime, TimeUnit.MILLISECONDS));
144 | }
145 |
146 | /**
147 | * 延时队列的item
148 | */
149 | class timeoutItem implements Delayed {
150 |
151 | String callbackId; // 当前callback的callbackId
152 | long executeTime; // 触发时间
153 |
154 | public timeoutItem(String callbackId, long delayTime, TimeUnit timeUnit) {
155 | this.callbackId = callbackId;
156 | this.executeTime = System.currentTimeMillis() + (delayTime > 0 ? timeUnit.toMillis(delayTime) : 0);
157 | }
158 |
159 | @Override
160 | public long getDelay(TimeUnit unit) {
161 | return executeTime - System.currentTimeMillis();
162 | }
163 |
164 | @Override
165 | public int compareTo(Delayed o) {
166 | return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
167 | }
168 | }
169 |
170 | /**
171 | * 同一个消息发送多次,callbackId是不能一样的,所以这里要先check一下,否则服务端反馈的时候,客户端接收就会乱套
172 | *
173 | * @param callbackSender
174 | * @return
175 | */
176 | public void checkCallbackSender(SuperCallbackSender callbackSender) {
177 |
178 | Utils.checkNotNull(socketOptions.getCallbackIDFactory(), "要想实现EasySocket的回调功能,CallbackIdFactory不能为null," +
179 | "请实现一个CallbackIdFactory并在初始化的时候通过EasySocketOptions的setCallbackIdFactory进行配置");
180 | String callbackId = callbackSender.getCallbackId();
181 | // 同一个消息发送两次以上,callbackId是不能一样的,否则服务端反馈的时候,客户端接收就会乱套
182 | if (callbacks.containsKey(callbackId)) {
183 | callbackSender.generateCallbackId();
184 | }
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/dispatcher/MainThreadExecutor.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.dispatcher;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import java.util.concurrent.Executor;
7 |
8 | /**
9 | * Author:Mapogo
10 | * Date:2020/4/8
11 | * Note:切到主线程
12 | */
13 | public class MainThreadExecutor implements Executor {
14 |
15 | private final Handler handler = new Handler(Looper.getMainLooper());
16 |
17 | @Override
18 | public void execute(Runnable r) {
19 | handler.post(r);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/dispatcher/SocketActionDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.dispatcher;
2 |
3 | import com.easysocket.EasySocket;
4 | import com.easysocket.entity.OriginReadData;
5 | import com.easysocket.entity.SocketAddress;
6 | import com.easysocket.interfaces.conn.IConnectionManager;
7 | import com.easysocket.interfaces.conn.ISocketActionDispatch;
8 | import com.easysocket.interfaces.conn.ISocketActionListener;
9 | import com.easysocket.utils.Utils;
10 |
11 | import java.io.Serializable;
12 | import java.nio.charset.Charset;
13 | import java.util.ArrayList;
14 | import java.util.Iterator;
15 | import java.util.List;
16 | import java.util.concurrent.LinkedBlockingQueue;
17 |
18 | import static com.easysocket.connection.action.IOAction.ACTION_READ_COMPLETE;
19 | import static com.easysocket.connection.action.SocketAction.ACTION_CONN_FAIL;
20 | import static com.easysocket.connection.action.SocketAction.ACTION_CONN_SUCCESS;
21 | import static com.easysocket.connection.action.SocketAction.ACTION_DISCONNECTION;
22 |
23 | /**
24 | * Author:Alex
25 | * Date:2019/6/1
26 | * Note:socket行为分发器
27 | */
28 | public class SocketActionDispatcher implements ISocketActionDispatch {
29 | /**
30 | * 连接地址
31 | */
32 | private SocketAddress socketAddress;
33 | /**
34 | * 连接器
35 | */
36 | private IConnectionManager connectionManager;
37 | /**
38 | * 回调监听集合
39 | */
40 | private List actionListeners = new ArrayList<>();
41 | /**
42 | * 处理socket行为的线程
43 | */
44 | private Thread actionThread;
45 | /**
46 | * 是否停止分发
47 | */
48 | private boolean isStop;
49 |
50 | /**
51 | * 事件消费队列
52 | */
53 | private final LinkedBlockingQueue socketActions = new LinkedBlockingQueue();
54 | /**
55 | * 切换到UI线程
56 | */
57 | private MainThreadExecutor mainThreadExecutor = new MainThreadExecutor();
58 |
59 |
60 | public SocketActionDispatcher(IConnectionManager connectionManager, SocketAddress socketAddress) {
61 | this.socketAddress = socketAddress;
62 | this.connectionManager = connectionManager;
63 | }
64 |
65 | public void setSocketAddress(SocketAddress info) {
66 | socketAddress = info;
67 | }
68 |
69 |
70 | @Override
71 | public void dispatchAction(String action) {
72 | dispatchAction(action, null);
73 | }
74 |
75 | @Override
76 | public void dispatchAction(String action, Serializable serializable) {
77 | // 将接收到的socket行为封装入列
78 | ActionBean actionBean = new ActionBean(action, serializable, this);
79 | socketActions.offer(actionBean);
80 | }
81 |
82 | @Override
83 | public void subscribe(ISocketActionListener iSocketActionListener) {
84 | if (iSocketActionListener != null && !actionListeners.contains(iSocketActionListener)) {
85 | actionListeners.add(iSocketActionListener);
86 | }
87 | }
88 |
89 | @Override
90 | public void unsubscribe(ISocketActionListener iSocketActionListener) {
91 | actionListeners.remove(iSocketActionListener);
92 | }
93 |
94 | /**
95 | * 分发线程
96 | */
97 | private class DispatchThread extends Thread {
98 |
99 | public DispatchThread() {
100 | super("dispatch thread");
101 | }
102 |
103 | @Override
104 | public void run() {
105 | // 循环处理socket的行为信息
106 | while (!isStop) {
107 | try {
108 | ActionBean actionBean = socketActions.take();
109 | if (actionBean != null && actionBean.mDispatcher != null) {
110 | SocketActionDispatcher actionDispatcher = actionBean.mDispatcher;
111 | List copyListeners = new ArrayList<>(actionDispatcher.actionListeners);
112 | Iterator listeners = copyListeners.iterator();
113 | // 通知所有监听者
114 | while (listeners.hasNext()) {
115 | ISocketActionListener listener = listeners.next();
116 | actionDispatcher.dispatchActionToListener(actionBean.mAction, actionBean.arg, listener);
117 | }
118 | }
119 | } catch (InterruptedException e) {
120 | e.printStackTrace();
121 | }
122 | }
123 | }
124 | }
125 |
126 | /**
127 | * socket行为的封装
128 | */
129 | protected static class ActionBean {
130 |
131 | public ActionBean(String action, Serializable arg, SocketActionDispatcher dispatcher) {
132 | mAction = action;
133 | this.arg = arg;
134 | mDispatcher = dispatcher;
135 | }
136 |
137 | String mAction = "";
138 | Serializable arg;
139 | SocketActionDispatcher mDispatcher;
140 | }
141 |
142 | /**
143 | * 分发行为给监听者
144 | *
145 | * @param action
146 | * @param content
147 | * @param actionListener
148 | */
149 | private void dispatchActionToListener(String action, final Serializable content, final ISocketActionListener actionListener) {
150 | switch (action) {
151 |
152 | case ACTION_CONN_SUCCESS: // 连接成功
153 | mainThreadExecutor.execute(new Runnable() {
154 | @Override
155 | public void run() {
156 | actionListener.onSocketConnSuccess(socketAddress);
157 | }
158 | });
159 |
160 | break;
161 |
162 | case ACTION_CONN_FAIL: // 连接失败
163 | mainThreadExecutor.execute(new Runnable() {
164 | @Override
165 | public void run() {
166 | actionListener.onSocketConnFail(socketAddress, ((Boolean) content).booleanValue());
167 | }
168 | });
169 |
170 | break;
171 |
172 | case ACTION_DISCONNECTION: // 连接断开
173 | mainThreadExecutor.execute(new Runnable() {
174 | @Override
175 | public void run() {
176 | actionListener.onSocketDisconnect(socketAddress, ((Boolean) content).booleanValue());
177 | // 不需要重连,则释放资源
178 | if (!(Boolean) content) {
179 | stopDispatchThread();
180 | }
181 | }
182 | });
183 | break;
184 |
185 | case ACTION_READ_COMPLETE: // 读取数据完成
186 | mainThreadExecutor.execute(new Runnable() {
187 | @Override
188 | public void run() {
189 | // response有三种形式
190 | actionListener.onSocketResponse(socketAddress, (OriginReadData) content);
191 | byte[] data = Utils.concatBytes(((OriginReadData) content).getHeaderData(), ((OriginReadData) content).getBodyBytes());
192 | actionListener.onSocketResponse(socketAddress, new String(data, Charset.forName(EasySocket.getInstance().getDefOptions().getCharsetName())));
193 | actionListener.onSocketResponse(socketAddress, data);
194 | }
195 | });
196 | break;
197 | }
198 | }
199 |
200 | // 开始分发线程
201 | @Override
202 | public void startDispatchThread() {
203 | isStop = false;
204 | if (actionThread == null) {
205 | actionThread = new DispatchThread();
206 | actionThread.start();
207 | }
208 | }
209 |
210 | @Override
211 | public void stopDispatchThread() {
212 | if (actionThread != null && actionThread.isAlive() && !actionThread.isInterrupted()) {
213 | socketActions.clear();
214 | //actionListeners.clear();
215 | isStop = true;
216 | actionThread.interrupt();
217 | actionThread = null;
218 | }
219 | }
220 |
221 | }
222 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/heartbeat/HeartManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.heartbeat;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 | import com.easysocket.entity.OriginReadData;
5 | import com.easysocket.entity.SocketAddress;
6 | import com.easysocket.interfaces.config.IOptions;
7 | import com.easysocket.interfaces.conn.IConnectionManager;
8 | import com.easysocket.interfaces.conn.IHeartManager;
9 | import com.easysocket.interfaces.conn.ISocketActionDispatch;
10 | import com.easysocket.interfaces.conn.SocketActionListener;
11 |
12 | import java.util.concurrent.Executors;
13 | import java.util.concurrent.ScheduledExecutorService;
14 | import java.util.concurrent.TimeUnit;
15 | import java.util.concurrent.atomic.AtomicInteger;
16 |
17 | /**
18 | * Author:Alex
19 | * Date:2019/12/8
20 | * Note:心跳包检测管理器
21 | */
22 | public class HeartManager extends SocketActionListener implements IOptions, IHeartManager {
23 |
24 | /**
25 | * 连接器
26 | */
27 | private IConnectionManager connectionManager;
28 | /**
29 | * 连接参数
30 | */
31 | private EasySocketOptions socketOptions;
32 | /**
33 | * 客户端心跳包
34 | */
35 | private byte[] clientHeart;
36 | /**
37 | * 心跳包发送线程
38 | */
39 | private ScheduledExecutorService heartExecutor;
40 | /**
41 | * 记录心跳的失联次数
42 | */
43 | private AtomicInteger loseTimes = new AtomicInteger(-1);
44 | /**
45 | * 心跳频率
46 | */
47 | private long freq;
48 | /**
49 | * 是否激活了心跳
50 | */
51 | private boolean isActivate;
52 |
53 |
54 | /**
55 | * 心跳包接收监听
56 | */
57 | private HeartbeatListener heartbeatListener;
58 |
59 |
60 | public HeartManager(IConnectionManager iConnectionManager, ISocketActionDispatch actionDispatch) {
61 | this.connectionManager = iConnectionManager;
62 | socketOptions = iConnectionManager.getOptions();
63 | actionDispatch.subscribe(this); // 注册监听
64 | }
65 |
66 | /**
67 | * 心跳发送任务
68 | */
69 | private final Runnable beatTask = new Runnable() {
70 | @Override
71 | public void run() {
72 | // 心跳丢失次数判断,心跳包丢失了一定的次数则会进行socket的断开重连
73 | if (socketOptions.getMaxHeartbeatLoseTimes() != -1
74 | && loseTimes.incrementAndGet() >= socketOptions.getMaxHeartbeatLoseTimes()) {
75 | // 断开重连
76 | connectionManager.disconnect(true);
77 | resetLoseTimes();
78 | } else { // 发送心跳包
79 | connectionManager.upBytes(clientHeart);
80 | }
81 | }
82 | };
83 |
84 |
85 | @Override
86 | public void startHeartbeat(byte[] clientHeart, HeartbeatListener listener) {
87 | this.clientHeart = clientHeart;
88 | this.heartbeatListener = listener;
89 | isActivate = true;
90 | openThread();
91 | }
92 |
93 |
94 | // 启动心跳线程
95 | private void openThread() {
96 | freq = socketOptions.getHeartbeatFreq(); // 心跳频率
97 | // 启动线程发送心跳
98 | if (heartExecutor == null || heartExecutor.isShutdown()) {
99 | heartExecutor = Executors.newSingleThreadScheduledExecutor();
100 | heartExecutor.scheduleWithFixedDelay(beatTask, 0, freq, TimeUnit.MILLISECONDS);
101 | }
102 | }
103 |
104 | /**
105 | * 停止心跳发送
106 | */
107 | @Override
108 | public void stopHeartbeat() {
109 | isActivate = false;
110 | closeThread();
111 | }
112 |
113 | // 停止心跳线程
114 | private void closeThread() {
115 | if (heartExecutor != null && !heartExecutor.isShutdown()) {
116 | heartExecutor.shutdownNow();
117 | heartExecutor = null;
118 | resetLoseTimes(); // 重置
119 | }
120 | }
121 |
122 | @Override
123 | public void onReceiveHeartBeat() {
124 | resetLoseTimes();
125 | }
126 |
127 |
128 | private void resetLoseTimes() {
129 | loseTimes.set(-1);
130 | }
131 |
132 | @Override
133 | public void onSocketConnSuccess(SocketAddress socketAddress) {
134 | if (isActivate) {
135 | openThread();
136 | }
137 | }
138 |
139 | @Override
140 | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) {
141 | // 如果不需要重连,则停止心跳频率线程
142 | if (!isNeedReconnect) {
143 | closeThread();
144 | }
145 | }
146 |
147 | @Override
148 | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) {
149 | // 如果不需要重连,则停止心跳检测
150 | if (!isNeedReconnect) {
151 | closeThread();
152 | }
153 | }
154 |
155 | @Override
156 | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) {
157 | if (heartbeatListener != null && heartbeatListener.isServerHeartbeat(originReadData)) {
158 | // 收到服务器心跳
159 | onReceiveHeartBeat();
160 | }
161 | }
162 |
163 | @Override
164 | public Object setOptions(EasySocketOptions socketOptions) {
165 | this.socketOptions = socketOptions;
166 | freq = socketOptions.getHeartbeatFreq();
167 | freq = freq < 1000 ? 1000 : freq; // 不能小于一秒
168 | return this;
169 | }
170 |
171 | @Override
172 | public EasySocketOptions getOptions() {
173 | return socketOptions;
174 | }
175 |
176 | public interface HeartbeatListener {
177 | // 是否为服务器心跳
178 | boolean isServerHeartbeat(OriginReadData orginReadData);
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/iowork/EasyReader.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.iowork;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 | import com.easysocket.connection.action.IOAction;
5 | import com.easysocket.entity.OriginReadData;
6 | import com.easysocket.exception.ReadRecoverableExeption;
7 | import com.easysocket.exception.ReadUnrecoverableException;
8 | import com.easysocket.interfaces.config.IMessageProtocol;
9 | import com.easysocket.interfaces.conn.IConnectionManager;
10 | import com.easysocket.interfaces.conn.ISocketActionDispatch;
11 | import com.easysocket.interfaces.io.IReader;
12 | import com.easysocket.utils.LogUtil;
13 |
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.nio.ByteBuffer;
17 |
18 | /**
19 | * Author:Alex
20 | * Date:2019/6/1
21 | * Note:
22 | */
23 | public class EasyReader implements IReader {
24 | /**
25 | * 输入流
26 | */
27 | private InputStream inputStream;
28 | /**
29 | * 读取原始数据的缓存空间
30 | */
31 | private ByteBuffer originBuf;
32 | /**
33 | * socket行为分发器
34 | */
35 | private ISocketActionDispatch actionDispatch;
36 | /**
37 | * 连接器
38 | */
39 | private IConnectionManager connectionManager;
40 | /**
41 | * 连接参数
42 | */
43 | private EasySocketOptions socketOptions;
44 |
45 | /**
46 | * 读数据时,余留数据的缓存
47 | */
48 | private ByteBuffer remainingBuf;
49 | /**
50 | * 读数据线程
51 | */
52 | private Thread readerThread;
53 | /**
54 | * 是否停止线程
55 | */
56 | private boolean stopThread;
57 |
58 | public EasyReader(IConnectionManager connectionManager, ISocketActionDispatch actionDispatch) {
59 | this.actionDispatch = actionDispatch;
60 | this.connectionManager = connectionManager;
61 | socketOptions = connectionManager.getOptions();
62 | }
63 |
64 | @Override
65 | public void read() throws IOException, ReadRecoverableExeption, ReadUnrecoverableException {
66 | OriginReadData originalData = new OriginReadData();
67 | IMessageProtocol messageProtocol = socketOptions.getMessageProtocol();
68 | // 消息协议为null,则直接读原始消息,不建议这样使用,因为会发生黏包、分包的问题
69 | if (messageProtocol == null) {
70 | readOriginDataFromSteam(originalData);
71 | return;
72 | }
73 |
74 | // 定义了消息协议
75 | int headerLength = messageProtocol.getHeaderLength(); // 包头长度
76 | ByteBuffer headBuf = ByteBuffer.allocate(headerLength); // 包头数据的buffer
77 | headBuf.order(socketOptions.getReadOrder());
78 |
79 | /*1、读 header=====>>>*/
80 | if (remainingBuf != null) { // 有余留
81 | // flip方法:一般从Buffer读数据前调用,将limit设置为当前position,将position设置为0,在读数据时,limit代表可读数据的有效长度
82 | remainingBuf.flip();
83 | // 读余留数据的长度
84 | int length = Math.min(remainingBuf.remaining(), headerLength);
85 | // 读入余留数据
86 | headBuf.put(remainingBuf.array(), 0, length);
87 |
88 | if (length < headerLength) { // 余留数据小于一个header
89 | // there are no data left
90 | remainingBuf = null;
91 | // 从stream中读剩下的header数据
92 | readHeaderFromSteam(headBuf, headerLength - length);
93 | } else {
94 | // 移动开始读数据的指针
95 | remainingBuf.position(headerLength);
96 | }
97 | } else { // 无余留
98 | // 从stream读取一个完整的 header
99 | readHeaderFromSteam(headBuf, headBuf.capacity());
100 | }
101 |
102 | // 保存header
103 | originalData.setHeaderData(headBuf.array());
104 |
105 | /*2、读 body=====>>>*/
106 | int bodyLength = messageProtocol.getBodyLength(originalData.getHeaderData(), socketOptions.getReadOrder());
107 | if (bodyLength > 0) {
108 | if (bodyLength > socketOptions.getMaxResponseDataMb() * 1024 * 1024) {
109 | throw new ReadUnrecoverableException("服务器返回的单次数据超过了规定的最大值,可能你的Socket消息协议不对,一般消息格式" +
110 | "为:Header+Body,其中Header保存消息长度和类型等,Body保存消息内容,请规范好你的协议");
111 | }
112 | // 分配空间
113 | ByteBuffer bodyBuf = ByteBuffer.allocate(bodyLength);
114 | bodyBuf.order(socketOptions.getReadOrder());
115 |
116 | // 有余留
117 | if (remainingBuf != null) {
118 | int bodyStartPosition = remainingBuf.position();
119 |
120 | int length = Math.min(remainingBuf.remaining(), bodyLength);
121 | // 读length大小的余留数据
122 | bodyBuf.put(remainingBuf.array(), bodyStartPosition, length);
123 | // 移动position位置
124 | remainingBuf.position(bodyStartPosition + length);
125 |
126 | // 读的余留数据刚好等于一个body
127 | if (length == bodyLength) {
128 | if (remainingBuf.remaining() > 0) { // 余留数据未读完
129 | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining());
130 | temp.order(socketOptions.getReadOrder());
131 | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining());
132 | remainingBuf = temp;
133 | } else { // there are no data left
134 | remainingBuf = null;
135 | }
136 |
137 | // 保存body
138 | originalData.setBodyData(bodyBuf.array());
139 |
140 | LogUtil.d("Socket收到数据-->" + originalData.getBodyString());
141 | // 分发数据
142 | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData);
143 |
144 | /*return读取结束*/
145 | return;
146 |
147 | } else { // there are no data left in buffer and some data pieces in channel
148 | remainingBuf = null;
149 | }
150 | }
151 | // 无余留,则从stream中读
152 | readBodyFromStream(bodyBuf);
153 | // 保存body到originalData
154 | originalData.setBodyData(bodyBuf.array());
155 |
156 | } else if (bodyLength == 0) { // 没有body数据
157 | originalData.setBodyData(new byte[0]);
158 | if (remainingBuf != null) {
159 | // the body is empty so header remaining buf need set null
160 | if (remainingBuf.hasRemaining()) {
161 | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining());
162 | temp.order(socketOptions.getReadOrder());
163 | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining());
164 | remainingBuf = temp;
165 | } else {
166 | remainingBuf = null;
167 | }
168 | }
169 | } else if (bodyLength < 0) {
170 | throw new ReadUnrecoverableException("数据body的长度不能小于0");
171 | }
172 |
173 | LogUtil.d("Socket收到数据-->" + originalData.getBodyString());
174 | // 分发
175 | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData);
176 |
177 | }
178 |
179 |
180 | /**
181 | * 读数据任务
182 | */
183 | private Runnable readerTask = new Runnable() {
184 | @Override
185 | public void run() {
186 | try {
187 | while (!stopThread) {
188 | read();
189 | }
190 | } catch (ReadUnrecoverableException unrecoverableException) {
191 | // 读异常
192 | unrecoverableException.printStackTrace();
193 | // 停止线程
194 | stopThread = true;
195 | release();
196 | } catch (ReadRecoverableExeption readRecoverableExeption) {
197 | readRecoverableExeption.printStackTrace();
198 | // 重连
199 | connectionManager.disconnect(true);
200 |
201 | } catch (IOException e) {
202 | e.printStackTrace();
203 | // 重连
204 | connectionManager.disconnect(true);
205 | }
206 | }
207 | };
208 |
209 |
210 | private void readHeaderFromSteam(ByteBuffer headBuf, int readLength) throws ReadRecoverableExeption, IOException {
211 | for (int i = 0; i < readLength; i++) {
212 | byte[] bytes = new byte[1];
213 | // 从输入流中读数据,无数据时会阻塞
214 | int value = inputStream.read(bytes);
215 | // -1代表读到了文件的末尾,一般是因为服务器断开了连接
216 | if (value == -1) {
217 | throw new ReadRecoverableExeption("读数据失败,可能是因为socket跟服务器断开了连接");
218 | }
219 | headBuf.put(bytes);
220 | }
221 | }
222 |
223 | private void readOriginDataFromSteam(OriginReadData readData) throws ReadRecoverableExeption, IOException {
224 | // 用 全局originBuf避免重复创建字节数组
225 | int len = inputStream.read(originBuf.array());
226 | // no more data
227 | if (len == -1) {
228 | throw new ReadRecoverableExeption("读数据失败,可能因为socket跟服务器断开了连接");
229 | }
230 | // bytes复制
231 | byte[] data = new byte[len];
232 | originBuf.get(data, 0, len);
233 | readData.setBodyData(data);
234 | LogUtil.d("Socket收到数据-->" + readData.getBodyString());
235 | // 分发数据
236 | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, readData);
237 | // 相当于把指针重新指向positon=0
238 | originBuf.clear();
239 | }
240 |
241 | private void readBodyFromStream(ByteBuffer byteBuffer) throws ReadRecoverableExeption, IOException {
242 | // while循环直到byteBuffer装满数据
243 | while (byteBuffer.hasRemaining()) {
244 | byte[] bufArray = new byte[socketOptions.getMaxReadBytes()]; // 从服务器单次读取的最大值
245 | int len = inputStream.read(bufArray);
246 | if (len == -1) { // no more data
247 | throw new ReadRecoverableExeption("读数据失败,可能是因为socket跟服务器断开了连接");
248 | }
249 | int remaining = byteBuffer.remaining();
250 | if (len > remaining) { // 从stream读的数据超过byteBuffer的剩余空间
251 | byteBuffer.put(bufArray, 0, remaining);
252 | // 将多余的数据保存到remainingBuf中缓存,等下一次读取
253 | remainingBuf = ByteBuffer.allocate(len - remaining);
254 | remainingBuf.order(socketOptions.getReadOrder());
255 | remainingBuf.put(bufArray, remaining, len - remaining);
256 | } else { // 从stream读的数据小于或等于byteBuffer的剩余空间
257 | byteBuffer.put(bufArray, 0, len);
258 | }
259 | }
260 | }
261 |
262 | @Override
263 | public void openReader() {
264 | init();
265 | if (readerThread == null || !readerThread.isAlive()) {
266 | readerThread = new Thread(readerTask, "reader thread");
267 | stopThread = false;
268 | readerThread.start();
269 | }
270 | }
271 |
272 | @Override
273 | public void closeReader() {
274 | try {
275 | // 关闭线程释放资源
276 | shutDownThread();
277 | release();
278 | } catch (InterruptedException e) {
279 | e.printStackTrace();
280 | }
281 | }
282 |
283 | // 释放资源
284 | private void release() {
285 | if (originBuf != null) {
286 | originBuf = null;
287 | }
288 | if (remainingBuf != null) {
289 | remainingBuf = null;
290 | }
291 | if (readerThread != null && !readerThread.isAlive()) {
292 | readerThread = null;
293 | }
294 |
295 | try {
296 | if (inputStream != null)
297 | inputStream.close();
298 | } catch (IOException e) {
299 | e.printStackTrace();
300 | } finally {
301 | inputStream = null;
302 | }
303 | }
304 |
305 | // 初始化
306 | private void init() {
307 | inputStream = connectionManager.getInputStream();
308 | // 没有定义消息协议
309 | if (socketOptions.getMessageProtocol() == null) {
310 | originBuf = ByteBuffer.allocate(1024 * 4);
311 | }
312 | }
313 |
314 | @Override
315 | public void setOption(EasySocketOptions socketOptions) {
316 | this.socketOptions = socketOptions;
317 | }
318 |
319 | // 关闭读数据线程
320 | private void shutDownThread() throws InterruptedException {
321 | if (readerThread != null && readerThread.isAlive() && !readerThread.isInterrupted()) {
322 | stopThread = true;
323 | readerThread.interrupt();
324 | readerThread.join();
325 | }
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/iowork/EasyWriter.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.iowork;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 | import com.easysocket.interfaces.conn.IConnectionManager;
5 | import com.easysocket.interfaces.conn.ISocketActionDispatch;
6 | import com.easysocket.interfaces.io.IWriter;
7 | import com.easysocket.utils.LogUtil;
8 |
9 | import java.io.IOException;
10 | import java.io.OutputStream;
11 | import java.nio.ByteBuffer;
12 | import java.nio.charset.Charset;
13 | import java.util.Arrays;
14 | import java.util.concurrent.LinkedBlockingDeque;
15 |
16 | /**
17 | * Author:Alex
18 | * Date:2019/6/1
19 | * Note:
20 | */
21 | public class EasyWriter implements IWriter {
22 |
23 | /**
24 | * 输出流
25 | */
26 | private OutputStream outputStream;
27 |
28 | /**
29 | * 连接管理器
30 | */
31 | private IConnectionManager connectionManager;
32 | /**
33 | * socket参数
34 | */
35 | private EasySocketOptions socketOptions;
36 | /**
37 | * 行为分发
38 | */
39 | private ISocketActionDispatch actionDispatch;
40 | /**
41 | * 写数据线程
42 | */
43 | private Thread writerThread;
44 | /**
45 | * 是否停止写数据
46 | */
47 | private boolean isStop;
48 | /**
49 | * 待写入数据
50 | */
51 | private LinkedBlockingDeque packetsToSend = new LinkedBlockingDeque<>();
52 |
53 | public EasyWriter(IConnectionManager connectionManager, ISocketActionDispatch actionDispatch) {
54 | this.connectionManager = connectionManager;
55 | socketOptions = connectionManager.getOptions();
56 | this.actionDispatch = actionDispatch;
57 | }
58 |
59 | @Override
60 | public void openWriter() {
61 | outputStream = connectionManager.getOutStream();
62 | if (writerThread == null) {
63 | isStop = false;
64 | writerThread = new Thread(writerTask, "writer thread");
65 | writerThread.start();
66 | }
67 | }
68 |
69 | @Override
70 | public void setOption(EasySocketOptions socketOptions) {
71 | this.socketOptions = socketOptions;
72 | }
73 |
74 | /**
75 | * 写任务
76 | */
77 | private Runnable writerTask = new Runnable() {
78 | @Override
79 | public void run() {
80 | // 循环写数据
81 | while (!isStop) {
82 | try {
83 | byte[] sender = packetsToSend.take();
84 | write(sender);
85 | } catch (InterruptedException | IOException e) {
86 | e.printStackTrace();
87 | }
88 | }
89 | }
90 | };
91 |
92 | @Override
93 | public void write(byte[] sendBytes) throws IOException {
94 | if (sendBytes != null) {
95 | LogUtil.d("Socket发送数据String-->" + new String(sendBytes, Charset.forName("utf-8")));
96 | LogUtil.d("Socket发送数据byte[]-->" + Arrays.toString(sendBytes));
97 | int packageSize = socketOptions.getMaxWriteBytes(); // 每次可以发送的最大数据
98 | int remainingCount = sendBytes.length;
99 | ByteBuffer writeBuf = ByteBuffer.allocate(packageSize);
100 | writeBuf.order(socketOptions.getReadOrder());
101 | int index = 0;
102 | // 如果发送的数据大于单次可发送的最大数据,则分多次发送
103 | while (remainingCount > 0) {
104 | int realWriteLength = Math.min(packageSize, remainingCount);
105 | writeBuf.clear(); // 清空缓存
106 | writeBuf.rewind(); // 将position位置移到0
107 | writeBuf.put(sendBytes, index, realWriteLength);
108 | writeBuf.flip();
109 | byte[] writeArr = new byte[realWriteLength];
110 | writeBuf.get(writeArr);
111 | outputStream.write(writeArr);
112 | outputStream.flush(); // 强制写入缓存中残留数据
113 | index += realWriteLength;
114 | remainingCount -= realWriteLength;
115 | }
116 | }
117 | }
118 |
119 | @Override
120 | public void offer(byte[] sender) {
121 | if (!isStop)
122 | packetsToSend.offer(sender);
123 | }
124 |
125 | @Override
126 | public void closeWriter() {
127 | try {
128 | if (outputStream != null)
129 | outputStream.close();
130 | shutDownThread();
131 | } catch (IOException | InterruptedException e) {
132 | e.printStackTrace();
133 | } finally {
134 | outputStream = null;
135 | }
136 | }
137 |
138 | private void shutDownThread() throws InterruptedException {
139 | if (writerThread != null && writerThread.isAlive() && !writerThread.isInterrupted()) {
140 | isStop = true;
141 | writerThread.interrupt();
142 | writerThread.join();
143 | writerThread = null;
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/iowork/IOManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.iowork;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 | import com.easysocket.interfaces.config.IOptions;
5 | import com.easysocket.interfaces.conn.IConnectionManager;
6 | import com.easysocket.interfaces.conn.ISocketActionDispatch;
7 | import com.easysocket.interfaces.io.IIOManager;
8 | import com.easysocket.interfaces.io.IReader;
9 | import com.easysocket.interfaces.io.IWriter;
10 |
11 | /**
12 | * Author:Alex
13 | * Date:2019/5/28
14 | * Note:
15 | */
16 | public class IOManager implements IIOManager, IOptions {
17 | /**
18 | * socket行为回调
19 | */
20 | private ISocketActionDispatch actionDispatch;
21 | /**
22 | * 连接管理
23 | */
24 | private IConnectionManager connectionManager;
25 | /**
26 | * 写
27 | */
28 | private IWriter writer;
29 | /**
30 | * 读
31 | */
32 | private IReader reader;
33 |
34 | public IOManager(IConnectionManager connectionManager,
35 | ISocketActionDispatch connActionDispatch) {
36 | this.connectionManager = connectionManager;
37 | this.actionDispatch = connActionDispatch;
38 | initIO();
39 | }
40 |
41 | // 初始化io
42 | private void initIO() {
43 | //makesureHeaderProtocolNotEmpty();
44 | reader = new EasyReader(connectionManager, actionDispatch); // 读
45 | writer = new EasyWriter(connectionManager, actionDispatch); // 写
46 | }
47 |
48 | @Override
49 | public void sendBytes(byte[] bytes) {
50 | if (writer != null)
51 | writer.offer(bytes);
52 | }
53 |
54 | @Override
55 | public void startIO() {
56 | if (writer != null)
57 | writer.openWriter();
58 | if (reader != null)
59 | reader.openReader();
60 | }
61 |
62 | @Override
63 | public void closeIO() {
64 | if (writer != null)
65 | writer.closeWriter();
66 | if (reader != null)
67 | reader.closeReader();
68 | }
69 |
70 | @Override
71 | public Object setOptions(EasySocketOptions socketOptions) {
72 | //makesureHeaderProtocolNotEmpty();
73 | if (writer != null)
74 | writer.setOption(socketOptions);
75 | if (reader != null)
76 | reader.setOption(socketOptions);
77 | return this;
78 | }
79 |
80 | @Override
81 | public EasySocketOptions getOptions() {
82 | return connectionManager.getOptions();
83 | }
84 |
85 | /**
86 | * 确保包结构协议不为空
87 | */
88 | // private void makesureHeaderProtocolNotEmpty() {
89 | // IMessageProtocol protocol = connectionManager.getOptions().getMessageProtocol();
90 | // if (protocol == null) {
91 | // throw new NoNullException("The reader protocol can not be Null.");
92 | // }
93 | //
94 | // if (protocol.getHeaderLength() == 0) {
95 | // throw new NoNullException("The header length can not be zero.");
96 | // }
97 | // }
98 | }
99 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/reconnect/AbsReconnection.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.reconnect;
2 |
3 | import com.easysocket.entity.OriginReadData;
4 | import com.easysocket.entity.SocketAddress;
5 | import com.easysocket.interfaces.conn.IConnectionManager;
6 | import com.easysocket.interfaces.conn.IReconnListener;
7 | import com.easysocket.interfaces.conn.SocketActionListener;
8 |
9 | /**
10 | * Author:Alex
11 | * Date:2019/5/31
12 | * Note:抽象重连器
13 | */
14 | public abstract class AbsReconnection extends SocketActionListener implements IReconnListener {
15 | /**
16 | * 连接管理器
17 | */
18 | protected IConnectionManager connectionManager;
19 | /**
20 | * socket连接管理器是否已销毁
21 | */
22 | protected boolean isDetach;
23 |
24 |
25 | @Override
26 | public synchronized void attach(IConnectionManager iConnectionManager) {
27 | if (!isDetach) {
28 | detach();
29 | }
30 | isDetach = false;
31 | connectionManager = iConnectionManager;
32 | connectionManager.subscribeSocketAction(this); // 监听socket行为
33 | }
34 |
35 | @Override
36 | public synchronized void detach() {
37 | isDetach = true;
38 | if (connectionManager != null)
39 | connectionManager.unSubscribeSocketAction(this);
40 | }
41 |
42 | @Override
43 | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) {
44 | // donothing
45 | }
46 |
47 | /**
48 | * 是否正在重连
49 | * @return
50 | */
51 | public abstract boolean isReconning();
52 | }
53 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/connection/reconnect/DefaultReConnection.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.connection.reconnect;
2 |
3 | import android.os.Handler;
4 | import android.os.HandlerThread;
5 |
6 | import com.easysocket.entity.SocketAddress;
7 | import com.easysocket.interfaces.conn.IConnectionManager;
8 | import com.easysocket.utils.LogUtil;
9 |
10 | /**
11 | * Author:Alex
12 | * Date:2019/5/28
13 | * Note:默认重连器
14 | */
15 | public class DefaultReConnection extends AbsReconnection {
16 | /**
17 | * 最大连接失败次数,超过可以切换到备用的服务器地址
18 | */
19 | private static final int MAX_CONNECTION_FAILED_TIMES = 10;
20 | /**
21 | * 连接失败的次数
22 | */
23 | private int connectionFailedTimes = 0;
24 | /**
25 | * 重连间隔不能小于10秒,为了避免全部客户端socket在同一时间连接服务端,间隔时间需要上下浮动50%
26 | */
27 | private long reconnectTimeDelay = 10 * 1000;
28 | /**
29 | * 重连线程
30 | */
31 | private HandlerThread handlerThread;
32 | /**
33 | * 实现延时任务的 handler
34 | */
35 | private Handler handler;
36 |
37 | public DefaultReConnection() {
38 | }
39 |
40 | @Override
41 | public synchronized void attach(IConnectionManager iConnectionManager) {
42 | super.attach(iConnectionManager);
43 | if (reconnectTimeDelay < connectionManager.getOptions().getConnectTimeout()) {
44 | reconnectTimeDelay = connectionManager.getOptions().getConnectTimeout();
45 | }
46 | }
47 |
48 | /**
49 | * 重连任务
50 | */
51 | private final Runnable RcConnTask = new Runnable() {
52 | @Override
53 | public void run() {
54 | LogUtil.d("---> 执行重连");
55 | if (isDetach) {
56 | shutDown();
57 | return;
58 | }
59 | // 是否可连接的
60 | if (!connectionManager.isConnectViable()) {
61 | LogUtil.d("当前条件不允许连接");
62 | // 尝试再次重连
63 | handler.postDelayed(RcConnTask, (long) (reconnectTimeDelay * (Math.random() + 0.5)));
64 | return;
65 | }
66 | // 重连
67 | connectionManager.connect();
68 | }
69 | };
70 |
71 | /**
72 | * 进行重连
73 | */
74 | private void reconnect() {
75 | if (handlerThread == null) {
76 | handlerThread = new HandlerThread("re_conn");
77 | handlerThread.start();
78 | handler = new Handler(handlerThread.getLooper());
79 | }
80 | LogUtil.d("重连间隔时间-->" + reconnectTimeDelay * (Math.random() + 0.5));
81 | handler.postDelayed(RcConnTask, (long) (reconnectTimeDelay * (Math.random() + 0.5)));
82 | }
83 |
84 |
85 | // 关闭重连线程
86 | private void shutDown() {
87 | if (handlerThread != null && handlerThread.isAlive()) {
88 | handlerThread.quit();
89 | handlerThread = null;
90 | handler = null;
91 | }
92 | }
93 |
94 | @Override
95 | public boolean equals(Object o) {
96 | // getClass返回Class类型的对象,比较它们的类型对象是否==,其实是比较它们是否为同一个Class创建的对象
97 | if (o == null || getClass() != o.getClass()) return false;
98 | return true;
99 | }
100 |
101 | @Override
102 | public void onSocketConnSuccess(SocketAddress socketAddress) {
103 | // 连接成功关闭重连线程
104 | shutDown();
105 | }
106 |
107 | @Override
108 | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) {
109 | // 不需要重连,则关闭重连线程
110 | if (!isNeedReconnect) {
111 | shutDown();
112 | return;
113 | }
114 | connectionFailedTimes++;
115 |
116 | // 如果大于最大连接次数并且有备用host,则轮流切换两个host
117 | if (connectionFailedTimes > MAX_CONNECTION_FAILED_TIMES && socketAddress.getBackupAddress() != null) {
118 | connectionFailedTimes = 0; // 归零
119 | SocketAddress backupAddress = socketAddress.getBackupAddress();
120 | SocketAddress nowAddress = new SocketAddress(socketAddress.getIp(), socketAddress.getPort());
121 | backupAddress.setBackupAddress(nowAddress);
122 | if (connectionManager.isConnectViable()) {
123 | connectionManager.switchHost(backupAddress);
124 | // 切换主机地址,重新连接
125 | reconnect();
126 | }
127 | } else {
128 | reconnect();
129 | }
130 |
131 | }
132 |
133 | @Override
134 | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) {
135 | // 是否需要重连
136 | if (!isNeedReconnect) {
137 | shutDown();
138 | return;
139 | }
140 | reconnect();
141 | }
142 |
143 | @Override
144 | public boolean isReconning() {
145 | return handlerThread != null && handlerThread.isAlive();
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/OriginReadData.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity;
2 |
3 | import com.easysocket.EasySocket;
4 | import com.easysocket.utils.Utils;
5 |
6 | import java.io.Serializable;
7 | import java.nio.charset.Charset;
8 |
9 | /**
10 | * Author:Alex
11 | * Date:2019/6/1
12 | * Note:读到的数据
13 | */
14 | public class OriginReadData implements Serializable {
15 |
16 | /**
17 | * 包头数据
18 | */
19 | private byte[] headerData;
20 | /**
21 | * 包体数据
22 | */
23 | private byte[] bodyData;
24 |
25 | public byte[] getHeaderData() {
26 | return headerData;
27 | }
28 |
29 | public void setHeaderData(byte[] headerData) {
30 | this.headerData = headerData;
31 | }
32 |
33 | public byte[] getBodyBytes() {
34 | return bodyData;
35 | }
36 |
37 | public void setBodyData(byte[] bodyData) {
38 | this.bodyData = bodyData;
39 | }
40 |
41 | /**
42 | * 获取数据body的string
43 | *
44 | * @return
45 | */
46 | public String getBodyString() {
47 | return new String(getBodyBytes(), Charset.forName(EasySocket.getInstance().getDefOptions().getCharsetName()));
48 | }
49 |
50 | /**
51 | * 获取完整的数据,包括包头和包体
52 | *
53 | * @return
54 | */
55 | public byte[] getOriginDataBytes() {
56 | return Utils.concatBytes(getHeaderData(), getBodyBytes());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/SocketAddress.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/5/31
6 | * Note:socket主机地址
7 | */
8 | public class SocketAddress {
9 |
10 | /**
11 | * IPV4地址
12 | */
13 | private String ip;
14 | /**
15 | * 连接服务器端口号
16 | */
17 | private int port;
18 | /**
19 | * 当此IP地址Ping不通时的备用IP
20 | */
21 | private SocketAddress backupAddress;
22 |
23 | /**
24 | * 获取备用的Ip和端口号
25 | *
26 | * @return 备用的端口号和IP地址
27 | */
28 | public SocketAddress getBackupAddress() {
29 | return backupAddress;
30 | }
31 |
32 | /**
33 | * 设置备用的IP和端口号,可以不设置
34 | *
35 | * @param backupAddress 备用的IP和端口号信息
36 | */
37 | public void setBackupAddress(SocketAddress backupAddress) {
38 | this.backupAddress = backupAddress;
39 | }
40 |
41 | public SocketAddress(String ip, int port){
42 | this.ip =ip;
43 | this.port =port;
44 | }
45 |
46 | public String getIp() {
47 | return ip;
48 | }
49 |
50 | public int getPort() {
51 | return port;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/basemsg/IResponse.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity.basemsg;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/12/8
6 | * Note:
7 | */
8 | public interface IResponse {
9 | }
10 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/basemsg/ISender.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity.basemsg;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/1
8 | * Note:发送数据的接口
9 | */
10 | public interface ISender extends Serializable {
11 | }
12 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/basemsg/SuperCallbackResponse.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity.basemsg;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/12/7
6 | */
7 | public abstract class SuperCallbackResponse implements IResponse {
8 |
9 | public abstract String getCallbackId();
10 |
11 | public abstract void setCallbackId(String callbackId);
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/basemsg/SuperCallbackSender.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity.basemsg;
2 |
3 | import com.easysocket.utils.Utils;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/10/19
8 | */
9 | public abstract class SuperCallbackSender extends SuperSender {
10 |
11 | private String callbackId;
12 |
13 | public SuperCallbackSender() {
14 | generateCallbackId();
15 | }
16 |
17 | public String getCallbackId() {
18 | return callbackId;
19 | }
20 |
21 | /**
22 | * 根据自己的协议打包消息
23 | *
24 | * @return
25 | */
26 | public abstract byte[] pack();
27 |
28 | /**
29 | * 随机生成一个回调标识 CallbackId,在消息发送前执行,CallbackId作为消息的唯一标识一起传给服务器,服务器反馈
30 | * 当前消息的时候也是携带同样的CallbackId给客户端,用以识别
31 | */
32 | public void generateCallbackId() {
33 | callbackId= Utils.getRandomChar(20);
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/entity/basemsg/SuperSender.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.entity.basemsg;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/10/19
6 | * Note:基础消息
7 | */
8 | public class SuperSender implements ISender {
9 | }
10 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/InitialExeption.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/5
6 | * Note:初始化异常
7 | */
8 | public class InitialExeption extends RuntimeException{
9 | public InitialExeption(String s){
10 | super(s);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/NotNullException.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/5
6 | * Note:非空异常
7 | */
8 | public class NotNullException extends RuntimeException {
9 | public NotNullException(String e) {
10 | super(e);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/ReadRecoverableExeption.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/5
6 | * Note:可恢复socket读数据异常
7 | */
8 | public class ReadRecoverableExeption extends Exception {
9 |
10 | public ReadRecoverableExeption(String s){
11 | super(s);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/ReadUnrecoverableException.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Mapogo
5 | * Date:2020/12/29
6 | * Note:不可修复的读取错误
7 | */
8 | public class ReadUnrecoverableException extends Exception {
9 | public ReadUnrecoverableException(String s) {
10 | super(s);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/RequestCancelException.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/4
6 | * Note:请求取消异常
7 | */
8 | public class RequestCancelException extends Exception{
9 |
10 | public RequestCancelException(String s){
11 | super(s);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/exception/RequestTimeOutException.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.exception;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/4
6 | * Note:请求超时异常
7 | */
8 | public class RequestTimeOutException extends Exception{
9 |
10 | public RequestTimeOutException(String s){
11 | super(s);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/callback/ICallBack.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.callback;
2 |
3 | import com.easysocket.callback.SuperCallBack;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/5
8 | * Note:
9 | */
10 | public interface ICallBack {
11 | /**
12 | * socket请求回调
13 | * @param callBack
14 | */
15 | void onCallBack(SuperCallBack callBack);
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/callback/IProgressDialog.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 zhouyou(478319399@qq.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.easysocket.interfaces.callback;
18 |
19 | import android.app.Dialog;
20 |
21 | /**
22 | * 描述:自定义对话框的dialog
23 | */
24 | public interface IProgressDialog {
25 | Dialog getDialog();
26 | }
27 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/callback/IType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 zhouyou(478319399@qq.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.easysocket.interfaces.callback;
18 |
19 | import java.lang.reflect.Type;
20 |
21 | /**
22 | * 描述:获取类型接口
23 | */
24 | public interface IType {
25 |
26 | Type getType();
27 |
28 | Class> getGenericityClazz();
29 | }
30 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/callback/ProgressCancelListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 zhouyou(478319399@qq.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.easysocket.interfaces.callback;
18 |
19 | /**
20 | * 描述:进度框取消监听
21 | */
22 | public interface ProgressCancelListener {
23 | void onCancelProgress();
24 | }
25 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/config/IConnectionSwitchListener.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.config;
2 |
3 | import com.easysocket.entity.SocketAddress;
4 | import com.easysocket.interfaces.conn.IConnectionManager;
5 |
6 | /**
7 | * Author:Alex
8 | * Date:2019/6/4
9 | * Note:
10 | */
11 | public interface IConnectionSwitchListener {
12 | void onSwitchConnectionInfo(IConnectionManager manager, SocketAddress oldAddress, SocketAddress newAddress);
13 | }
14 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/config/IMessageProtocol.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.config;
2 |
3 | import java.nio.ByteOrder;
4 |
5 | /**
6 | * 消息数据格式
7 | */
8 | public interface IMessageProtocol {
9 |
10 | /**
11 | * 获取包头的长度
12 | */
13 | int getHeaderLength();
14 |
15 | /**
16 | * 获取数据包体的长度,根据协议这个长度应该写在包头中,在读取数据时用到
17 | */
18 | int getBodyLength(byte[] header, ByteOrder byteOrder);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/config/IOptions.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.config;
2 |
3 | import com.easysocket.config.EasySocketOptions;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/1
8 | * Note:
9 | */
10 | public interface IOptions {
11 | /**
12 | * 设置配置信息
13 | * @param socketOptions
14 | */
15 | T setOptions(EasySocketOptions socketOptions);
16 |
17 | /**
18 | * 获取配置信息
19 | * @return
20 | */
21 | EasySocketOptions getOptions();
22 | }
23 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/IConnectionManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import com.easysocket.entity.SocketAddress;
4 | import com.easysocket.interfaces.callback.ICallBack;
5 | import com.easysocket.interfaces.config.IOptions;
6 |
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 |
10 | /**
11 | * Author:Alex
12 | * Date:2019/5/29
13 | * Note:连接管理的接口规范
14 | */
15 | public interface IConnectionManager extends ISubscribeSocketAction, IOptions,ISend, ICallBack {
16 | /**
17 | * 开始连接
18 | */
19 | void connect();
20 |
21 | /**
22 | * 关闭连接
23 | * @param isNeedReconnect 是否需要重连
24 | */
25 | void disconnect(boolean isNeedReconnect);
26 |
27 |
28 | /**
29 | * 获取socket连接状态
30 | * @return
31 | */
32 | int getConnectionStatus();
33 |
34 | /**
35 | * 是否可连接的
36 | * @return
37 | */
38 | boolean isConnectViable();
39 |
40 | /**
41 | * 切换host
42 | * @param socketAddress
43 | */
44 | void switchHost(SocketAddress socketAddress);
45 |
46 | /**
47 | * 获取输入流
48 | * @return
49 | */
50 | InputStream getInputStream();
51 |
52 | /**
53 | * 获取输出流
54 | * @return
55 | */
56 | OutputStream getOutStream();
57 |
58 | /**
59 | * 获取心跳管理器
60 | * @return
61 | */
62 | IHeartManager getHeartManager();
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/IHeartManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import com.easysocket.connection.heartbeat.HeartManager;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/12/8
8 | * Note:
9 | */
10 | public interface IHeartManager {
11 |
12 | /**
13 | * 开始心跳
14 | * @param clientHeart
15 | */
16 | void startHeartbeat(byte[] clientHeart, HeartManager.HeartbeatListener listener);
17 |
18 | /**
19 | * 停止心跳
20 | */
21 | void stopHeartbeat();
22 |
23 |
24 | /**
25 | * 接收到心跳
26 | */
27 | void onReceiveHeartBeat();
28 | }
29 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/IReconnListener.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IReconnListener {
9 |
10 | /**
11 | * 关联连接器
12 | * @param iConnectionManager
13 | */
14 | void attach(IConnectionManager iConnectionManager);
15 |
16 | /**
17 | * 分离连接器
18 | */
19 | void detach();
20 | }
21 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/ISend.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import com.easysocket.entity.basemsg.SuperCallbackSender;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/5
8 | * Note:发送接口
9 | */
10 | public interface ISend {
11 |
12 | /**
13 | * 发送一个有回调的消息
14 | * @param sender
15 | * @return
16 | */
17 | IConnectionManager upCallbackMessage(SuperCallbackSender sender);
18 |
19 | /**
20 | * 发送bytes
21 | * @param bytes
22 | * @return
23 | */
24 | IConnectionManager upBytes(byte[] bytes);
25 | }
26 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/ISocketActionDispatch.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/1
8 | * Note:socket行为分发接口
9 | */
10 | public interface ISocketActionDispatch {
11 | /**
12 | * 停止分发线程
13 | */
14 | void stopDispatchThread();
15 |
16 | void startDispatchThread();
17 |
18 | void dispatchAction(String action);
19 |
20 | /**
21 | * socket行为的分发
22 | * @param action
23 | * @param serializable
24 | */
25 | void dispatchAction(String action, Serializable serializable);
26 |
27 | /**
28 | * 订阅socket行为
29 | * @param iSocketActionListener
30 | */
31 | void subscribe(ISocketActionListener iSocketActionListener);
32 |
33 | /**
34 | * 解除socket行为的订阅
35 | * @param iSocketActionListener
36 | */
37 | void unsubscribe(ISocketActionListener iSocketActionListener);
38 | }
39 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/ISocketActionListener.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import com.easysocket.entity.OriginReadData;
4 | import com.easysocket.entity.SocketAddress;
5 |
6 | /**
7 | * Author:Alex
8 | * Date:2019/6/1
9 | * Note:socket行为监听接口
10 | */
11 | public interface ISocketActionListener {
12 | /**
13 | * socket连接成功
14 | * @param socketAddress
15 | */
16 | void onSocketConnSuccess(SocketAddress socketAddress);
17 |
18 | /**
19 | * socket连接失败
20 | * @param socketAddress
21 | * @param isNeedReconnect 是否需要重连
22 | */
23 | void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect);
24 |
25 | /**
26 | * 断开socket连接
27 | * @param socketAddress
28 | * @param isNeedReconnect 是否需要重连
29 | */
30 | void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect);
31 |
32 | /**
33 | * socket数据响应
34 | * @param socketAddress
35 | * @param originReadData
36 | */
37 | void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData);
38 |
39 | /**
40 | * socket数据响应
41 | * @param socketAddress
42 | * @param readData
43 | */
44 | void onSocketResponse(SocketAddress socketAddress, String readData);
45 |
46 | /**
47 | * socket数据响应
48 | * @param socketAddress
49 | * @param readData
50 | */
51 | void onSocketResponse(SocketAddress socketAddress, byte[] readData);
52 | }
53 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/ISubscribeSocketAction.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:订阅监听socket
7 | */
8 | public interface ISubscribeSocketAction {
9 | /**
10 | * 注册监听socket的行为
11 | * @param iSocketActionListener
12 | */
13 | void subscribeSocketAction(ISocketActionListener iSocketActionListener);
14 |
15 | /**
16 | * 注销监听socket的行为
17 | * @param iSocketActionListener
18 | */
19 | void unSubscribeSocketAction(ISocketActionListener iSocketActionListener);
20 | }
21 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/conn/SocketActionListener.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.conn;
2 |
3 | import com.easysocket.entity.OriginReadData;
4 | import com.easysocket.entity.SocketAddress;
5 |
6 | /**
7 | * Author:Alex
8 | * Date:2019/6/4
9 | * Note:socket行为监听的抽象类,继承此类可以选择性地重写方法
10 | */
11 | public abstract class SocketActionListener implements ISocketActionListener{
12 | /**
13 | * socket连接成功
14 | * @param socketAddress
15 | */
16 | @Override
17 | public void onSocketConnSuccess(SocketAddress socketAddress) {
18 |
19 | }
20 | /**
21 | * socket连接失败
22 | * @param socketAddress
23 | * @param isNeedReconnect 是否需要重连
24 | */
25 | @Override
26 | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) {
27 |
28 | }
29 | /**
30 | * 断开socket连接
31 | * @param socketAddress
32 | * @param isNeedReconnect 是否需要重连
33 | */
34 | @Override
35 | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) {
36 |
37 | }
38 | /**
39 | * socket读数据反馈
40 | * @param socketAddress
41 | * @param originReadData
42 | */
43 | @Override
44 | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) {
45 |
46 | }
47 |
48 | @Override
49 | public void onSocketResponse(SocketAddress socketAddress, byte[] readData) {
50 |
51 | }
52 |
53 | @Override
54 | public void onSocketResponse(SocketAddress socketAddress, String readData) {
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/io/IIOManager.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.io;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IIOManager {
9 |
10 | /**
11 | * 发送字节流
12 | *
13 | * @param bytes
14 | */
15 | void sendBytes(byte[] bytes);
16 |
17 | /**
18 | * 关闭io管理器
19 | */
20 | void closeIO();
21 |
22 | /**
23 | * 开启io操作
24 | */
25 | void startIO();
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/io/IReader.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.io;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IReader {
9 |
10 | /**
11 | * 读数据
12 | */
13 | void read() throws Exception;
14 |
15 | /**
16 | * 打开数据的读取
17 | */
18 | void openReader();
19 |
20 | /**
21 | * 关闭数据的读取
22 | */
23 | void closeReader();
24 |
25 | /**
26 | * 设置参数
27 | * @param t
28 | */
29 | void setOption(T t);
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/interfaces/io/IWriter.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.interfaces.io;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/1
8 | * Note:
9 | */
10 | public interface IWriter {
11 | /**
12 | * 保存要写的数据
13 | */
14 | void offer(byte[] sender);
15 |
16 | /**
17 | * 写数据
18 | * @param sender
19 | */
20 | void write(byte[] sender) throws IOException;
21 |
22 | /**
23 | * 关闭stream
24 | */
25 | void closeWriter();
26 |
27 | /**
28 | * 开启写数据
29 | */
30 | void openWriter();
31 |
32 | /**
33 | * 设置参数
34 | * @param t
35 | */
36 | void setOption(T t);
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/utils/LogUtil.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.utils;
2 | import android.util.Log;
3 |
4 | import com.easysocket.EasySocket;
5 |
6 |
7 | public class LogUtil {
8 | public static final String LOGTAG = "easysocket";
9 | public static boolean debugEnabled = EasySocket.getInstance().getDefOptions().isDebug();
10 |
11 | public LogUtil() {
12 | }
13 |
14 | private static String getDebugInfo() {
15 | Throwable stack = new Throwable().fillInStackTrace();
16 | StackTraceElement[] trace = stack.getStackTrace();
17 | int n = 2;
18 | return trace[n].getClassName() + " " + trace[n].getMethodName() + "()" + ":" + trace[n].getLineNumber() +
19 | " ";
20 | }
21 |
22 | private static String getLogInfoByArray(String[] infos) {
23 | StringBuilder sb = new StringBuilder();
24 | for (String info : infos) {
25 | sb.append(info);
26 | sb.append(" ");
27 | }
28 | return sb.toString();
29 | }
30 |
31 | public static void i(String... s) {
32 | if (debugEnabled) {
33 | i(LOGTAG, getDebugInfo() + getLogInfoByArray(s));
34 | }
35 | }
36 |
37 | public static void e(Throwable tr) {
38 | if (debugEnabled) {
39 | Log.e(LOGTAG, getDebugInfo() ,tr);
40 | }
41 | }
42 |
43 | public static void e(String... s) {
44 | if (debugEnabled) {
45 | e(LOGTAG, getDebugInfo() + getLogInfoByArray(s));
46 | }
47 | }
48 |
49 | public static void d(String... s) {
50 | if (debugEnabled) {
51 | d(LOGTAG, getDebugInfo() + getLogInfoByArray(s));
52 | }
53 | }
54 |
55 | public static void v(String... s) {
56 | if (debugEnabled) {
57 | v(LOGTAG, getDebugInfo() + getLogInfoByArray(s));
58 | }
59 | }
60 |
61 | public static void w(String... s) {
62 | if (debugEnabled) {
63 | w(LOGTAG, getDebugInfo() + getLogInfoByArray(s));
64 | }
65 | }
66 |
67 | private static void i(String name, String log) {
68 | System.out.println(name + ":" + log);
69 | }
70 |
71 | private static void d(String name, String log) {
72 | System.out.println(name + ":" + log);
73 | }
74 |
75 | private static void v(String name, String log) {
76 | System.out.println(name + ":" + log);
77 | }
78 |
79 | private static void e(String name, String log) {
80 | System.err.println(name + ":" + log);
81 | }
82 |
83 | private static void w(String name, String log) {
84 | System.err.println(name + ":" + log);
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/easysocket/src/main/java/com/easysocket/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.easysocket.utils;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 | import android.os.Handler;
7 | import android.os.Looper;
8 |
9 | import java.lang.reflect.ParameterizedType;
10 | import java.lang.reflect.Type;
11 | import java.lang.reflect.TypeVariable;
12 | import java.util.Random;
13 |
14 | /**
15 | * Created by LXR ON 2018/8/30.
16 | */
17 | public class Utils {
18 |
19 | /**
20 | * 获取泛型参数的类型
21 | *
22 | * @param
23 | * @return
24 | */
25 | public static Type findGenericityType(Class cls) {
26 | Type genType = cls.getGenericSuperclass(); //返回直接继承的父类(包含泛型参数)类型,如果有泛型T,也要包括进去
27 | //getActualTypeArguments 获取泛型中的实际类型,比如Map中的String类型
28 | Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
29 | Type type = params[0]; //泛型的实际类型
30 | Type finalNeedType;
31 | if (type instanceof ParameterizedType) { //二级泛型,这里就处理最多二级吧,形如 A>,两个<>
32 | finalNeedType = ((ParameterizedType) type).getActualTypeArguments()[0];
33 | } else { // 一级泛型,形如A
34 | finalNeedType = type;
35 | }
36 | //如果泛型类型还是变量类型,比如T、V之类的,代表没有填写泛型参数
37 | if (finalNeedType instanceof TypeVariable) throw new IllegalStateException("没有填写泛型参数");
38 | return finalNeedType;
39 | }
40 |
41 | /**
42 | * 字符串是否为空
43 | *
44 | * @param str
45 | * @return
46 | */
47 | public static boolean isStringEmpty(String str) {
48 | return str == null || str.trim().length() == 0;
49 | }
50 |
51 | /**
52 | * 生成随机字符串
53 | *
54 | * @param length
55 | * @return
56 | */
57 | public static String getRandomChar(int length) {
58 | char[] chr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
59 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
60 | Random random = new Random();
61 | StringBuffer buffer = new StringBuffer();
62 | for (int i = 0; i < length; i++) {
63 | buffer.append(chr[random.nextInt(36)]);
64 | }
65 | return buffer.toString();
66 | }
67 |
68 |
69 | /**
70 | * 获取handler对象
71 | *
72 | * @param isMainHandler 是否为主线程的handler,为false时返回的是当前线程handler
73 | * @return
74 | */
75 | public static Handler getHandler(boolean isMainHandler) {
76 | Handler handler;
77 | if (isMainHandler) {
78 | handler = new Handler(Looper.getMainLooper());
79 | } else {
80 | Looper.prepare();
81 | handler = new Handler();
82 | }
83 | return handler;
84 | }
85 |
86 | /**
87 | * 睡眠多少毫秒
88 | *
89 | * @param milliSecond 毫秒
90 | */
91 | public static void sleep(long milliSecond) {
92 | try {
93 | Thread.sleep(milliSecond);
94 | } catch (InterruptedException e) {
95 | e.printStackTrace();
96 | }
97 | }
98 |
99 | /**
100 | * 非空检查
101 | *
102 | * @param object
103 | * @param emsg
104 | * @throws
105 | */
106 | public static void checkNotNull(Object object, String emsg) {
107 | try {
108 | if (object == null) {
109 | throw new Exception(emsg);
110 | }
111 | } catch (Exception e) {
112 | e.printStackTrace();
113 | }
114 | }
115 |
116 | public static void throwNotNull(Object object, String emsg) throws Exception {
117 | if (object == null) {
118 | throw new Exception(emsg);
119 | }
120 | }
121 |
122 | // 判断是否连接网络
123 | public static boolean isNetConnected(Context context) {
124 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
125 | NetworkInfo info = cm.getActiveNetworkInfo();
126 | return info != null && info.isConnected();
127 | }
128 |
129 | /**
130 | * 拼接两个byte[]
131 | *
132 | * @param
133 | * @param
134 | * @return
135 | */
136 | public static byte[] concatBytes(byte[] bt1, byte[] bt2) {
137 | if (bt1 == null) {
138 | return bt2;
139 | }
140 | if (bt2 == null) {
141 | return bt1;
142 | }
143 | byte[] bt3 = new byte[bt1.length + bt2.length];
144 | System.arraycopy(bt1, 0, bt3, 0, bt1.length);
145 | System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
146 | return bt3;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 05 15:47:41 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app',
2 | ':easysocket',
3 | ':socker_server'
4 |
--------------------------------------------------------------------------------
/socker_server/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/socker_server/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 |
3 | dependencies {
4 | implementation fileTree(dir: 'libs', include: ['*.jar'])
5 |
6 | implementation 'com.google.code.gson:gson:2.8.2'
7 | }
8 |
9 |
10 | sourceCompatibility = "7"
11 | targetCompatibility = "7"
12 |
13 |
14 | tasks.withType(JavaCompile) {
15 | options.encoding = "UTF-8"
16 | }
17 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/HandlerIO.java:
--------------------------------------------------------------------------------
1 | package com.socker_server;
2 |
3 | import com.google.gson.Gson;
4 | import com.socker_server.entity.IMessageProtocol;
5 | import com.socker_server.entity.MessageID;
6 | import com.socker_server.entity.message.CallbackResponse;
7 | import com.socker_server.entity.message.DelayResponse;
8 | import com.socker_server.entity.message.ServerHeartBeat;
9 | import com.socker_server.entity.message.TestResponse;
10 | import com.socker_server.entity.message.base.SuperClient;
11 | import com.socker_server.entity.message.base.SuperResponse;
12 | import com.socker_server.iowork.IWriter;
13 |
14 | /**
15 | * Author:Alex
16 | * Date:2019/6/6
17 | * Note:处理io信息
18 | */
19 | public class HandlerIO {
20 |
21 | private IWriter easyWriter;
22 | private IMessageProtocol messageProtocol;
23 |
24 | public HandlerIO(IWriter easyWriter) {
25 | this.easyWriter = easyWriter;
26 | messageProtocol = ServerConfig.getInstance().getMessageProtocol();
27 | }
28 |
29 | /**
30 | * 处理接收的信息
31 | *
32 | * @param receiver
33 | */
34 | public void handReceiveMsg(String receiver) {
35 | try {
36 | System.out.println("receive message:" + receiver);
37 | SuperClient clientMsg = new Gson().fromJson(receiver, SuperClient.class);
38 | String id = clientMsg.getMsgId(); //消息ID
39 | String callbackId = clientMsg.getCallbackId(); //回调ID
40 | SuperResponse superResponse = null;
41 |
42 | switch (id) {
43 | case MessageID.CALLBACK_MSG: //回调消息
44 | superResponse = new CallbackResponse();
45 | (superResponse).setCallbackId(callbackId);
46 | superResponse.setMsgId(MessageID.CALLBACK_MSG);
47 | ((CallbackResponse) superResponse).setFrom("我来自server");
48 | break;
49 |
50 | case MessageID.TEST_MSG: //测试消息
51 | superResponse = new TestResponse();
52 | superResponse.setMsgId(MessageID.TEST_MSG);
53 | ((TestResponse) superResponse).setFrom("server");
54 | break;
55 | case MessageID.HEARTBEAT: //心跳包
56 | superResponse = new ServerHeartBeat();
57 | ((ServerHeartBeat) superResponse).setFrom("server");
58 | superResponse.setMsgId(MessageID.HEARTBEAT);
59 | break;
60 |
61 | case MessageID.DELAY_MSG: //延时消息
62 | superResponse = new DelayResponse();
63 | (superResponse).setCallbackId(callbackId);
64 | superResponse.setMsgId(MessageID.DELAY_MSG);
65 | ((DelayResponse) superResponse).setFrom("server");
66 | try {
67 | Thread.sleep(1000 * 5);
68 | } catch (InterruptedException e) {
69 | e.printStackTrace();
70 | }
71 | break;
72 | }
73 |
74 | if (superResponse == null) return;
75 | System.out.println("send message:" + convertObjectToJson(superResponse));
76 | byte[] bytes = convertObjectToJson(superResponse).getBytes();
77 | System.out.println("send message:" + bytes.length);
78 | // 自定义消息协议
79 | if (messageProtocol != null) {
80 | bytes = messageProtocol.pack(bytes);
81 | }
82 | easyWriter.offer(bytes);
83 | } catch (Exception e) {
84 | System.out.println("可能收到非Json格式的数据");
85 | e.printStackTrace();
86 | }
87 | }
88 |
89 |
90 | private String convertObjectToJson(Object object) {
91 | Gson gson = new Gson();
92 | String json = gson.toJson(object);
93 | return json;
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/MainClass.java:
--------------------------------------------------------------------------------
1 | package com.socker_server;
2 |
3 | import com.socker_server.entity.DefaultMessageProtocol;
4 | import com.socker_server.iowork.ServerIOManager;
5 |
6 | import java.net.ServerSocket;
7 | import java.net.Socket;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.concurrent.ExecutorService;
11 | import java.util.concurrent.Executors;
12 |
13 | public class MainClass {
14 |
15 | private static final int PORT1 = 9999;
16 | private static final int PORT2 = 9998;
17 | private List mList = new ArrayList();
18 | // private ServerSocket server9999 = null;
19 | // private ServerSocket server9998 = null;
20 | private ExecutorService mExecutorService = null;
21 | private ExecutorService launchService;
22 |
23 | public static void main(String[] args) {
24 | new MainClass();
25 | System.out.println("java running");
26 | }
27 |
28 | public MainClass() {
29 | mExecutorService = Executors.newCachedThreadPool();
30 | launchService = Executors.newFixedThreadPool(2);
31 | // 开启了两个端口的socket服务
32 | launchService.execute(new Runnable() {
33 | @Override
34 | public void run() {
35 | initServerSocket(PORT1);
36 | }
37 | });
38 | launchService.execute(new Runnable() {
39 | @Override
40 | public void run() {
41 | initServerSocket(PORT2);
42 | }
43 | });
44 | }
45 |
46 | private void initServerSocket(int port) {
47 | try {
48 | ServerSocket server = new ServerSocket(port);
49 | initConfig(); // 配置信息
50 | System.out.println("端口" + port + "server is running");
51 | Socket client;
52 | while (true) {
53 | client = server.accept();
54 | mList.add(client);
55 | mExecutorService.execute(new Service(client));
56 | }
57 | } catch (Exception e) {
58 | e.printStackTrace();
59 | }
60 | }
61 |
62 | private void initConfig() {
63 | // 默认的消息协议
64 | ServerConfig.getInstance().setMessageProtocol(new DefaultMessageProtocol());
65 | }
66 |
67 | class Service implements Runnable {
68 | private Socket socket;
69 | ServerIOManager serverIoManager;
70 |
71 | public Service(Socket socket) {
72 | this.socket = socket;
73 | System.out.println("connect server sucessful: " + socket.getInetAddress().getHostAddress());
74 | }
75 |
76 | @Override
77 | public void run() {
78 | serverIoManager = new ServerIOManager(socket);
79 | serverIoManager.startIO();
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/ServerConfig.java:
--------------------------------------------------------------------------------
1 | package com.socker_server;
2 |
3 | import com.socker_server.entity.IMessageProtocol;
4 |
5 | /**
6 | * Author:Mapogo
7 | * Date:2020/9/6
8 | * Note:服务端的相关配置信息
9 | */
10 | public class ServerConfig {
11 |
12 | // 消息协议
13 | private IMessageProtocol messageProtocol;
14 | // 单例
15 | private static ServerConfig instance = new ServerConfig();
16 |
17 | public static ServerConfig getInstance() {
18 | return instance;
19 | }
20 |
21 | private ServerConfig() {
22 | }
23 |
24 | public IMessageProtocol getMessageProtocol() {
25 | return messageProtocol;
26 | }
27 |
28 | public void setMessageProtocol(IMessageProtocol messageProtocol) {
29 | this.messageProtocol = messageProtocol;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/DefaultMessageProtocol.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 |
6 | /**
7 | * Author:Alex
8 | * Date:2019/5/31
9 | * Note:读取io数据时,默认的包头数据格式
10 | */
11 | public class DefaultMessageProtocol implements IMessageProtocol {
12 | @Override
13 | public int getHeaderLength() {
14 | return 4;
15 | }
16 |
17 | @Override
18 | public int getBodyLength(byte[] header, ByteOrder byteOrder) {
19 | if (header == null || header.length < getHeaderLength()) {
20 | return 0;
21 | }
22 | ByteBuffer bb = ByteBuffer.wrap(header);
23 | bb.order(byteOrder);
24 | return bb.getInt(); //body的长度以int的形式写在了header那里
25 | }
26 |
27 | @Override
28 | public byte[] pack(byte[] body) {
29 | // 消息头的长度,指多少个byte
30 | int headerLength = getHeaderLength();
31 | ByteBuffer bb = ByteBuffer.allocate(headerLength + body.length);
32 | bb.order(ByteOrder.BIG_ENDIAN);
33 | bb.putInt(body.length); // header,保存body的length
34 | bb.put(body); // body
35 | return bb.array();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/IMessageProtocol.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity;
2 |
3 | import java.nio.ByteOrder;
4 |
5 | /**
6 | * 包头数据格式
7 | */
8 | public interface IMessageProtocol {
9 | /**
10 | * 获取包头的长度
11 | */
12 | int getHeaderLength();
13 |
14 | /**
15 | * 获取数据包体的长度,根据协议这个长度应该写在包头中,在读取数据的时候会用到
16 | */
17 | int getBodyLength(byte[] header, ByteOrder byteOrder);
18 |
19 | byte[] pack(byte[] body);
20 | }
21 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/MessageID.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/6
6 | * Note:
7 | */
8 | public class MessageID {
9 |
10 | public static final String CALLBACK_MSG ="callback_msg"; //回调消息
11 |
12 | public static final String TEST_MSG ="test_msg"; //测试消息
13 |
14 | public static final String HEARTBEAT="heart_beat"; //心跳包
15 |
16 | public static final String HANDLE_SHAKE="handle_shake"; //握手
17 |
18 | public static final String DELAY_MSG ="delay_msg"; //延时消息
19 | }
20 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/OriginReadData.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity;
2 |
3 | import java.io.Serializable;
4 | import java.nio.charset.Charset;
5 |
6 | /**
7 | * Author:Alex
8 | * Date:2019/6/1
9 | * Note:读的原始数据
10 | */
11 | public class OriginReadData implements Serializable {
12 |
13 | /**
14 | * 包头数据
15 | */
16 | private byte[] headerData;
17 | /**
18 | * 包体数据
19 | */
20 | private byte[] bodyData;
21 |
22 | public byte[] getHeaderData() {
23 | return headerData;
24 | }
25 |
26 | public void setHeaderData(byte[] headerData) {
27 | this.headerData = headerData;
28 | }
29 |
30 | public byte[] getBodyData() {
31 | return bodyData;
32 | }
33 |
34 | public void setBodyData(byte[] bodyData) {
35 | this.bodyData = bodyData;
36 | }
37 |
38 | /**
39 | * 获取原始数据body的string形式
40 | * @return
41 | */
42 | public String getBodyString(){
43 | return new String(getBodyData(), Charset.forName("utf-8"));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/CallbackResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message;
2 |
3 | import com.socker_server.entity.message.base.SuperResponse;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/6
8 | * Note:回调消息
9 | */
10 | public class CallbackResponse extends SuperResponse {
11 |
12 | private String from;
13 |
14 | public String getFrom() {
15 | return from;
16 | }
17 |
18 | public void setFrom(String from) {
19 | this.from = from;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/DelayResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message;
2 |
3 | import com.socker_server.entity.message.base.SuperResponse;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/11
8 | * Note:
9 | */
10 | public class DelayResponse extends SuperResponse {
11 |
12 | private String from;
13 |
14 | public String getFrom() {
15 | return from;
16 | }
17 |
18 | public void setFrom(String from) {
19 | this.from = from;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/ServerHeartBeat.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message;
2 |
3 | import com.socker_server.entity.message.base.SuperResponse;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/6
8 | * Note:
9 | */
10 | public class ServerHeartBeat extends SuperResponse {
11 |
12 | private String from;
13 |
14 | public String getFrom() {
15 | return from;
16 | }
17 |
18 | public void setFrom(String from) {
19 | this.from = from;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/TestResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message;
2 |
3 | import com.socker_server.entity.message.base.SuperResponse;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/12/6
8 | * Note:服务器返回的测试消息
9 | */
10 | public class TestResponse extends SuperResponse {
11 |
12 | private String from;
13 |
14 | public String getFrom() {
15 | return from;
16 | }
17 |
18 | public void setFrom(String from) {
19 | this.from = from;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/base/BaseResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message.base;
2 |
3 | /**
4 | * Author:枪花
5 | * Date:2020/3/20
6 | * Note:
7 | */
8 | public class BaseResponse implements IResponse {
9 | /**
10 | * 消息ID
11 | */
12 | private String msgId;
13 |
14 | public String getMsgId() {
15 | return msgId;
16 | }
17 |
18 | public void setMsgId(String msgId) {
19 | this.msgId = msgId;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/base/IClient.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message.base;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/12/8
6 | * Note:
7 | */
8 | public interface IClient {
9 | }
10 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/base/IResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message.base;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Author:Alex
7 | * Date:2019/6/1
8 | * Note:发送数据的接口
9 | */
10 | public interface IResponse extends Serializable {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/base/SuperClient.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message.base;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/12/6
6 | * Note:
7 | */
8 | public class SuperClient implements IClient {
9 |
10 | /**
11 | * 消息ID
12 | */
13 | private String msgId;
14 | /**
15 | * 回调标识
16 | */
17 | private String callbackId;
18 |
19 | public String getMsgId() {
20 | return msgId;
21 | }
22 |
23 | public void setMsgId(String msgId) {
24 | this.msgId = msgId;
25 | }
26 |
27 |
28 | public String getCallbackId() {
29 | return callbackId;
30 | }
31 |
32 | public void setCallbackId(String callbakcId) {
33 | this.callbackId = callbakcId;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/entity/message/base/SuperResponse.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.entity.message.base;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/10/19
6 | * Note:基础消息
7 | */
8 | public class SuperResponse implements IResponse {
9 |
10 |
11 | /**
12 | * 消息ID
13 | */
14 | private String msgId;
15 |
16 | private String callbackId;
17 |
18 | public String getCallbackId() {
19 | return callbackId;
20 | }
21 |
22 | public void setCallbackId(String callbackId) {
23 | this.callbackId = callbackId;
24 | }
25 |
26 | public String getMsgId() {
27 | return msgId;
28 | }
29 |
30 | public void setMsgId(String msgId) {
31 | this.msgId = msgId;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/IIOManager.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.iowork;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IIOManager {
9 |
10 | /**
11 | * 发送字节序列的数据
12 | * @param buffer
13 | */
14 | void sendBuffer(byte[] buffer);
15 |
16 | /**
17 | * 关闭io管理器
18 | */
19 | void closeIO();
20 |
21 | /**
22 | * 开启io操作
23 | */
24 | void startIO();
25 | }
26 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/IReader.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.iowork;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IReader {
9 |
10 | /**
11 | * 读数据
12 | */
13 | void read();
14 |
15 | /**
16 | * 打开数据的读取
17 | */
18 | void openReader();
19 |
20 | /**
21 | * 关闭数据的读取
22 | */
23 | void closeReader();
24 |
25 | /**
26 | * 设置参数
27 | * @param t
28 | */
29 | void setOption(T t);
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/IWriter.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.iowork;
2 |
3 | /**
4 | * Author:Alex
5 | * Date:2019/6/1
6 | * Note:
7 | */
8 | public interface IWriter {
9 | /**
10 | * 保存需要写入的数据
11 | */
12 | void offer(byte[] sender);
13 |
14 | /**
15 | * 写入数据
16 | * @param sender
17 | */
18 | void write(byte[] sender);
19 |
20 | /**
21 | * 关闭stream
22 | */
23 | void closeWriter();
24 |
25 | /**
26 | * 打开读取数据
27 | */
28 | void openWriter();
29 |
30 | /**
31 | * 设置参数
32 | * @param t
33 | */
34 | void setOption(T t);
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/ServerIOManager.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiusetian/EasySocket/6f738c6f89aeaa18a0d618ed75bf43701ad26178/socker_server/src/main/java/com/socker_server/iowork/ServerIOManager.java
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/ServerReader.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.iowork;
2 |
3 | import com.socker_server.HandlerIO;
4 | import com.socker_server.ServerConfig;
5 | import com.socker_server.entity.IMessageProtocol;
6 | import com.socker_server.entity.OriginReadData;
7 |
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.net.Socket;
11 | import java.nio.ByteBuffer;
12 | import java.nio.ByteOrder;
13 |
14 | /**
15 | * Author:Alex
16 | * Date:2019/6/1
17 | * Note:
18 | */
19 | public class ServerReader implements IReader {
20 | /**
21 | * 输入流
22 | */
23 | private InputStream inputStream;
24 |
25 | /**
26 | * 读取数据时,没读完的残留数据缓存
27 | */
28 | private ByteBuffer remainingBuf;
29 | /**
30 | * 读数据线程
31 | */
32 | private Thread readerThread;
33 |
34 | private Socket socket;
35 |
36 | private boolean isShutdown;
37 | /**
38 | * 处理接收到数据
39 | */
40 | private HandlerIO handlerIO;
41 |
42 | public ServerReader(InputStream inputStream, Socket socket, HandlerIO handlerIO) {
43 | this.inputStream = inputStream;
44 | this.socket = socket;
45 | this.handlerIO = handlerIO;
46 | }
47 |
48 | @Override
49 | public void read() {
50 | OriginReadData originalData = new OriginReadData();
51 | IMessageProtocol readerProtocol = ServerConfig.getInstance().getMessageProtocol();
52 | try {
53 | // 如果消息协议为null,则直接读取原始消息,不建议这样使用,因为会发生黏包、分包的问题
54 | if (readerProtocol == null) {
55 | readOriginDataFromSteam(originalData);
56 | return;
57 | }
58 |
59 | int headerLength = readerProtocol.getHeaderLength(); //默认的包头长度是4个字节
60 | ByteBuffer headBuf = ByteBuffer.allocate(headerLength); //读取数据包头的缓存
61 | headBuf.order(ByteOrder.BIG_ENDIAN);
62 |
63 | //首先读取数据的header=====>>>
64 | //有余留数据
65 | if (remainingBuf != null) {
66 | //flip方法:buffer的容量不变,将limit移动到buffer的数据大小索引位置,将position置为0,这样就可以从0这个位置开始读取数据,直到limit大小的位置,limit就是buffer保存数据的大小
67 | remainingBuf.flip();
68 | int length = Math.min(remainingBuf.remaining(), headerLength);
69 | //将长度为length的数据写入headBuf中
70 | headBuf.put(remainingBuf.array(), 0, length);
71 | if (length < headerLength) { //不够一个header
72 | //there are no data left
73 | remainingBuf = null;
74 | //再从stream中读取header剩下的长度
75 | readHeaderFromSteam(headBuf, headerLength - length);
76 | } else { //读取header之后还有余留缓存
77 | remainingBuf.position(headerLength); //移动指针位置
78 | }
79 | }
80 | //没有余留数据
81 | else {
82 | readHeaderFromSteam(headBuf, headBuf.capacity());
83 | }
84 | //将header赋值到原始数据中
85 | originalData.setHeaderData(headBuf.array());
86 |
87 | // 开始读取body数据=====>>>
88 | int bodyLength = readerProtocol.getBodyLength(originalData.getHeaderData(), ByteOrder.BIG_ENDIAN);
89 |
90 | if (bodyLength > 0) {
91 | if (bodyLength > 5 * 1024 * 1024) { //是否大于最大的读取数
92 | throw new Exception("服务器返回的单次数据的大小已经超过了规定的最大值,为了防止内存溢出,请规范好相关协议");
93 | }
94 | ByteBuffer byteBuffer = ByteBuffer.allocate(bodyLength);
95 | byteBuffer.order(ByteOrder.BIG_ENDIAN);
96 | //有余留数据未读取
97 | if (remainingBuf != null) {
98 | int bodyStartPosition = remainingBuf.position();
99 | int length = Math.min(remainingBuf.remaining(), bodyLength);
100 | //从余留的数据中读取
101 | byteBuffer.put(remainingBuf.array(), bodyStartPosition, length);
102 | //移动position位置
103 | remainingBuf.position(bodyStartPosition + length);
104 |
105 | //表示余留数据的大于大于或等于body数据的大小
106 | if (length == bodyLength) {
107 | if (remainingBuf.remaining() > 0) { //还有数据余留
108 | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining());
109 | temp.order(ByteOrder.BIG_ENDIAN);
110 | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining());
111 | remainingBuf = temp;
112 | } else { //there are no data left
113 | remainingBuf = null;
114 | }
115 |
116 | //将读取到body数据赋值给原始数据
117 | originalData.setBodyData(byteBuffer.array());
118 | //分发数据
119 | handlerIO.handReceiveMsg(originalData.getBodyString());
120 | //actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData);
121 | //此次读取结束,return
122 | return;
123 | }
124 | //没有数据余留了
125 | else { //there are no data left in buffer and some data pieces in channel
126 | remainingBuf = null;
127 | }
128 | }
129 | //继续从stream中读
130 | readBodyFromStream(byteBuffer);
131 | originalData.setBodyData(byteBuffer.array()); //将body数据赋值
132 | }
133 | //数据body长度为0
134 | else if (bodyLength == 0) {
135 | originalData.setBodyData(new byte[0]);
136 | if (remainingBuf != null) {
137 | //the body is empty so header remaining buf need set null
138 | if (remainingBuf.hasRemaining()) {
139 | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining());
140 | temp.order(ByteOrder.BIG_ENDIAN);
141 | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining());
142 | remainingBuf = temp;
143 | } else {
144 | remainingBuf = null;
145 | }
146 | }
147 | } else if (bodyLength < 0) {
148 | throw new Exception("读取失败,读取到的数据长度小于0,可能是读取的过程中跟socket跟服务器断开了连接");
149 | }
150 | //将读取到一个完整数据发布出去
151 | handlerIO.handReceiveMsg(originalData.getBodyString());
152 | //actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData);
153 | } catch (Exception e) {
154 | e.printStackTrace();
155 | // 关闭
156 | isShutdown = true;
157 | }
158 | }
159 |
160 | @Override
161 | public void openReader() {
162 | isShutdown = false;
163 | readerThread = new Thread(readerTask, "reader thread");
164 | readerThread.start();
165 | }
166 |
167 | /**
168 | * 读取数据任务类
169 | */
170 | private Runnable readerTask = new Runnable() {
171 | @Override
172 | public void run() {
173 | while (socket.isConnected() && !isShutdown && !socket.isClosed()) {
174 | read();
175 | }
176 | }
177 | };
178 |
179 | // 直接读取原始数据,适合于所有数据格式
180 | private void readOriginDataFromSteam(OriginReadData readData) throws Exception {
181 |
182 | byte[] bufArray = new byte[4096]; // 从服务器单次读取的最大数据
183 | int len = inputStream.read(bufArray);
184 | if (len == -1) { // no more data
185 | throw new Exception("可能客户端断开了连接");
186 | }
187 | ByteBuffer data = ByteBuffer.allocate(len);
188 | data.put(bufArray, 0, len);
189 | readData.setBodyData(data.array());
190 | System.out.println("字符串大小" + readData.getBodyString().length());
191 | // 分发数据
192 | handlerIO.handReceiveMsg(readData.getBodyString().trim());
193 | }
194 |
195 | private void readHeaderFromSteam(ByteBuffer headBuf, int readLength) throws Exception {
196 | for (int i = 0; i < readLength; i++) {
197 | byte[] bytes = new byte[1];
198 | int value = inputStream.read(bytes); //从输入流中读取相应长度的数据
199 | if (value == -1) {
200 | throw new Exception("可能客户端断开了连接");
201 | }
202 | headBuf.put(bytes);
203 | }
204 | }
205 |
206 | private void readBodyFromStream(ByteBuffer byteBuffer) throws Exception {
207 | //body大小是缓存buffer是否还有剩余空间
208 | while (byteBuffer.hasRemaining()) {
209 |
210 | byte[] bufArray = new byte[50]; //从服务器中单次读取的缓存数据大小
211 | int len = inputStream.read(bufArray);
212 | if (len == -1) {
213 | throw new Exception("可能客户端断开了连接");
214 | }
215 | int remaining = byteBuffer.remaining();
216 | if (len > remaining) { //读多了
217 | byteBuffer.put(bufArray, 0, remaining);
218 | remainingBuf = ByteBuffer.allocate(len - remaining);
219 | remainingBuf.order(ByteOrder.BIG_ENDIAN);
220 | remainingBuf.put(bufArray, remaining, len - remaining);
221 | } else { //还没够或者刚刚好
222 | byteBuffer.put(bufArray, 0, len);
223 | }
224 | }
225 | }
226 |
227 | @Override
228 | public void closeReader() {
229 | try {
230 | if (inputStream != null)
231 | inputStream.close();
232 | shutDownThread();
233 | } catch (IOException e) {
234 | e.printStackTrace();
235 | }
236 | }
237 |
238 | @Override
239 | public void setOption(Object o) {
240 | }
241 |
242 | private void shutDownThread() {
243 | isShutdown = true;
244 | if (readerThread != null && readerThread.isAlive() && !readerThread.isInterrupted()) {
245 | readerThread.interrupt();
246 | }
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/socker_server/src/main/java/com/socker_server/iowork/ServerWriter.java:
--------------------------------------------------------------------------------
1 | package com.socker_server.iowork;
2 |
3 | import java.io.IOException;
4 | import java.io.OutputStream;
5 | import java.net.Socket;
6 | import java.nio.ByteBuffer;
7 | import java.nio.ByteOrder;
8 | import java.util.concurrent.LinkedBlockingDeque;
9 |
10 | /**
11 | * Author:Alex
12 | * Date:2019/6/1
13 | * Note:
14 | */
15 | public class ServerWriter implements IWriter {
16 |
17 | /**
18 | * 输出流
19 | */
20 | private OutputStream outputStream;
21 |
22 | /**
23 | * 写入数据的线程
24 | */
25 | private Thread writerThread;
26 | /**
27 | * 需要写入的数据
28 | */
29 | private LinkedBlockingDeque packetsToSend = new LinkedBlockingDeque<>();
30 | /**
31 | * 是否关闭线程
32 | */
33 | private boolean isShutdown;
34 |
35 | private Socket socket;
36 |
37 | public ServerWriter(OutputStream outputStream, Socket socket) {
38 | this.outputStream = outputStream;
39 | this.socket = socket;
40 | }
41 |
42 | @Override
43 | public void openWriter() {
44 | writerThread = new Thread(writerTask, "writer thread");
45 | isShutdown = false;
46 | writerThread.start();
47 | }
48 |
49 | @Override
50 | public void setOption(Object o) {
51 |
52 | }
53 |
54 |
55 | /**
56 | * io写任务
57 | */
58 | private Runnable writerTask = new Runnable() {
59 | @Override
60 | public void run() {
61 | //只要socket处于连接的状态,就一直活动
62 | while (socket.isConnected() && !isShutdown && !socket.isClosed()) {
63 | try {
64 | byte[] sender = packetsToSend.take();
65 | write(sender);
66 | } catch (InterruptedException e) {
67 | //取数据异常
68 | e.printStackTrace();
69 | isShutdown = true;
70 | }
71 | }
72 | }
73 | };
74 |
75 | @Override
76 | public void write(byte[] sendBytes) {
77 | if (sendBytes != null) {
78 | try {
79 | int packageSize = 100; //每次发送的数据包的大小
80 | int remainingCount = sendBytes.length;
81 | ByteBuffer writeBuf = ByteBuffer.allocate(packageSize); //分配一个内存缓存
82 | writeBuf.order(ByteOrder.BIG_ENDIAN);
83 | int index = 0;
84 | //如果要发送的数据大小大于每次发送的数据包的大小, 则要分多次将数据发出去
85 | while (remainingCount > 0) {
86 | int realWriteLength = Math.min(packageSize, remainingCount);
87 | writeBuf.clear(); //清空缓存
88 | writeBuf.rewind(); //将position位置移到0
89 | writeBuf.put(sendBytes, index, realWriteLength);
90 | writeBuf.flip(); //将position赋为0,limit赋为数据大小
91 | byte[] writeArr = new byte[realWriteLength];
92 | writeBuf.get(writeArr);
93 | outputStream.write(writeArr);
94 | outputStream.flush(); //强制缓存中残留的数据写入清空
95 | index += realWriteLength;
96 | remainingCount -= realWriteLength;
97 | }
98 | } catch (Exception e) {
99 | //写数据异常
100 | e.printStackTrace();
101 | isShutdown = true;
102 | }
103 | }
104 | }
105 |
106 | @Override
107 | public void offer(byte[] sender) {
108 | packetsToSend.offer(sender);
109 | }
110 |
111 | @Override
112 | public void closeWriter() {
113 | try {
114 | if (outputStream != null)
115 | outputStream.close();
116 | shutDownThread();
117 | } catch (IOException e) {
118 | e.printStackTrace();
119 | }
120 | }
121 |
122 | private void shutDownThread() {
123 | isShutdown = true;
124 | if (writerThread != null && writerThread.isAlive() && !writerThread.isInterrupted()) {
125 | writerThread.interrupt();
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------