├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── AndroidSocketClient.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── vilyever │ │ └── androidsocketclient │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── vilyever │ │ └── androidsocketclient │ │ ├── App.java │ │ ├── FileUtil.java │ │ ├── MainActivity.java │ │ ├── TestClient.java │ │ └── TestServer.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── socketclient ├── .gitignore ├── build.gradle ├── build_library_ext.gradle ├── proguard-rules.pro ├── socketclient.iml └── src ├── androidTest └── java │ └── com │ └── vilyever │ └── socketclient │ └── ApplicationTest.java └── main ├── AndroidManifest.xml ├── java └── com │ └── vilyever │ └── socketclient │ ├── SocketClient.java │ ├── helper │ ├── SocketClientAddress.java │ ├── SocketClientDelegate.java │ ├── SocketClientReceivingDelegate.java │ ├── SocketClientSendingDelegate.java │ ├── SocketConfigure.java │ ├── SocketHeartBeatHelper.java │ ├── SocketInputReader.java │ ├── SocketPacket.java │ ├── SocketPacketHelper.java │ └── SocketResponsePacket.java │ ├── server │ ├── SocketServer.java │ ├── SocketServerClient.java │ └── SocketServerDelegate.java │ └── util │ ├── BytesWrapper.java │ ├── CharsetUtil.java │ ├── IPUtil.java │ └── StringValidation.java └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Android Studio 2 | *.iml 3 | .idea 4 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 5 | .gradle 6 | build/ 7 | /local.properties 8 | /captures 9 | 10 | # OSX files 11 | .DS_Store 12 | 13 | # Windows thumbnail db 14 | Thumbs.db -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | AndroidSocketClient -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 18 | 19 | 20 | 32 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Android Lint 52 | 53 | 54 | Internationalization issues 55 | 56 | 57 | Internationalization issuesJava 58 | 59 | 60 | Java 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 1.7 87 | 88 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AndroidSocketClient.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidSocketClient 2 | socket client server简易封装 3 | 4 | ## Import 5 | [JitPack](https://jitpack.io/) 6 | 7 | Add it in your project's build.gradle at the end of repositories: 8 | 9 | ```gradle 10 | repositories { 11 | // ... 12 | maven { url "https://jitpack.io" } 13 | } 14 | ``` 15 | 16 | Step 2. Add the dependency in the form 17 | 18 | ```gradle 19 | dependencies { 20 | compile 'com.github.vilyever:AndroidSocketClient:3.0.3' 21 | } 22 | ``` 23 | 24 | ## Updates 25 | * 3.0.3 26 |
27 | 提升readByLength按长度读取的速度 [issue #26](/../../issues/26) 28 | 29 | * 3.0.2 30 |
31 | 修复初次连接失败时,因CountDownTimer导致崩溃的问题 [issue #24](/../../issues/24) 32 | 33 | * 3.0.1 34 |
35 | 修复包头验证bug,by zzdwuliang 36 |
37 | 增加地址检测的详细log 38 |
39 | 40 | * 3.0.0 41 |
42 | 支持ReadToData和ReadToLength自动读取以下两种结构 43 |
44 | 常见包结构1:【包头(可选)】【正文】【包尾】 45 |
46 | 常见包结构2:【包头(可选)】【余下包长度(正文加包尾长度)(此部分也可做包头)(此部分长度固定)】【正文】【包尾(可选)】 47 | 48 | ## Usage 49 | ### app模块下包含简单的使用demo 50 | ### 请一定设置读取策略socketClient.getSocketPacketHelper().setReadStrategy(); 51 | 52 | ### 远程端连接信息配置 53 | ```java 54 | socketClient.getAddress().setRemoteIP(IPUtil.getLocalIPAddress(true)); // 远程端IP地址 55 | socketClient.getAddress().setRemotePort("21998"); // 远程端端口号 56 | socketClient.getAddress().setConnectionTimeout(15 * 1000); // 连接超时时长,单位毫秒 57 | ``` 58 | 59 | ### 默认String编码配置 60 | ```java 61 | /** 62 | * 设置自动转换String类型到byte[]类型的编码 63 | * 如未设置(默认为null),将不能使用{@link SocketClient#sendString(String)}发送消息 64 | * 如设置为非null(如UTF-8),在接受消息时会自动尝试在接收线程(非主线程)将接收的byte[]数据依照编码转换为String,在{@link SocketResponsePacket#getMessage()}读取 65 | */ 66 | socketClient.setCharsetName(CharsetUtil.UTF_8); // 设置编码为UTF-8 67 | ``` 68 | 69 | ### 固定心跳包配置 70 | ```java 71 | /** 72 | * 设置自动发送的心跳包信息 73 | */ 74 | socketClient.getHeartBeatHelper().setDefaultSendData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 75 | 76 | /** 77 | * 设置远程端发送到本地的心跳包信息内容,用于判断接收到的数据包是否是心跳包 78 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 79 | */ 80 | socketClient.getHeartBeatHelper().setDefaultReceiveData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 81 | 82 | socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 83 | socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 84 | ``` 85 | 86 | ### 动态变化心跳包配置 87 | ```java 88 | /** 89 | * 设置自动发送的心跳包信息 90 | * 此信息动态生成 91 | * 92 | * 每次发送心跳包时自动调用 93 | */ 94 | socketClient.getHeartBeatHelper().setSendDataBuilder(new SocketHeartBeatHelper.SendDataBuilder() { 95 | @Override 96 | public byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper) { 97 | /** 98 | * 使用当前日期作为心跳包 99 | */ 100 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 101 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 102 | 103 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 104 | byte[] heartBeatInfo = CharsetUtil.stringToData(sdf.format(new Date()), CharsetUtil.UTF_8); 105 | 106 | byte[] data = new byte[heartBeatPrefix.length + heartBeatSuffix.length + heartBeatInfo.length]; 107 | System.arraycopy(heartBeatPrefix, 0, data, 0, heartBeatPrefix.length); 108 | System.arraycopy(heartBeatInfo, 0, data, heartBeatPrefix.length, heartBeatInfo.length); 109 | System.arraycopy(heartBeatSuffix, 0, data, heartBeatPrefix.length + heartBeatInfo.length, heartBeatSuffix.length); 110 | 111 | return data; 112 | } 113 | }); 114 | 115 | /** 116 | * 设置远程端发送到本地的心跳包信息的检测器,用于判断接收到的数据包是否是心跳包 117 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 118 | */ 119 | socketClient.getHeartBeatHelper().setReceiveHeartBeatPacketChecker(new SocketHeartBeatHelper.ReceiveHeartBeatPacketChecker() { 120 | @Override 121 | public boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet) { 122 | /** 123 | * 判断数据包信息是否含有指定的心跳包前缀和后缀 124 | */ 125 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 126 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 127 | 128 | if (Arrays.equals(heartBeatPrefix, Arrays.copyOfRange(packet.getData(), 0, heartBeatPrefix.length)) 129 | && Arrays.equals(heartBeatSuffix, Arrays.copyOfRange(packet.getData(), packet.getData().length - heartBeatSuffix.length, packet.getData().length))) { 130 | return true; 131 | } 132 | 133 | return false; 134 | } 135 | }); 136 | 137 | socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 138 | socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 139 | ``` 140 | 141 | ### 自动按包尾分割信息读取数据的发送配置 142 | ```java 143 | /** 144 | * 根据连接双方协议设置自动发送的包尾数据 145 | * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 146 | * 147 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 148 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 149 | * 2. socketClient向远程端发送正文数据{0x01, 0x02} 150 | * 3. socketClient向远程端发送包尾 151 | * 152 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 153 | * 用于分隔多条消息 154 | */ 155 | socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 156 | 157 | /** 158 | * 根据连接双方协议设置自动发送的包头数据 159 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 160 | * 161 | * 若无需包头可删除此行 162 | */ 163 | socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 164 | 165 | /** 166 | * 设置分段发送数据长度 167 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 168 | * 169 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 170 | */ 171 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 172 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 173 | 174 | /** 175 | * 设置发送超时时长 176 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 177 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 178 | * 179 | * 若无需限制发送时长可删除此二行 180 | */ 181 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 182 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 183 | ``` 184 | 185 | ### 自动按包尾分割信息读取数据的接收配置 186 | ```java 187 | /** 188 | * 设置读取策略为自动读取到指定的包尾 189 | */ 190 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadToTrailer); 191 | 192 | /** 193 | * 根据连接双方协议设置的包尾数据 194 | * 每次接收数据包(包括心跳包)都会在检测接收到与包尾数据相同的byte[]时回调一个数据包 195 | * 196 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 197 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 198 | * 2. socketClient接收正文和包尾(接收方式为一直读取到与尾相同的byte[]) 199 | * 3. socketClient回调数据包 200 | * 201 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 202 | * 用于分隔多条消息 203 | */ 204 | socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 205 | 206 | /** 207 | * 根据连接双方协议设置的包头数据 208 | * 每次接收数据包(包括心跳包)都会先接收此包头 209 | * 210 | * 若无需包头可删除此行 211 | */ 212 | socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 213 | 214 | /** 215 | * 设置接收超时时长 216 | * 在指定时长内没有数据到达本地自动断开 217 | * 218 | * 若无需限制接收时长可删除此二行 219 | */ 220 | socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 221 | socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 222 | ``` 223 | 224 | ### 自动按包长度信息读取的发送配置 225 | ```java 226 | /** 227 | * 设置包长度转换器 228 | * 即每次发送数据时,将包头以外的数据长度转换为特定的byte[]发送到远程端用于解析还需要读取多少长度的数据 229 | * 230 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 231 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 232 | * 2. socketClient要发送的数据为{0x01, 0x02},长度为2(若设置了包尾,还需加上包尾的字节长度),通过此转换器将int类型的2转换为4字节的byte[],远程端也照此算法将4字节的byte[]转换为int值 233 | * 3. socketClient向远程端发送转换后的长度信息byte[] 234 | * 4. socketClient向远程端发送正文数据{0x01, 0x02} 235 | * 5. socketClient向远程端发送包尾(如果设置了包尾信息) 236 | * 237 | * 此转换器用于第二步 238 | * 239 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 240 | * 用于分隔多条消息 241 | */ 242 | socketClient.getSocketPacketHelper().setSendPacketLengthDataConvertor(new SocketPacketHelper.SendPacketLengthDataConvertor() { 243 | @Override 244 | public byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength) { 245 | /** 246 | * 简单将int转换为byte[] 247 | */ 248 | byte[] data = new byte[4]; 249 | data[3] = (byte) (packetLength & 0xFF); 250 | data[2] = (byte) ((packetLength >> 8) & 0xFF); 251 | data[1] = (byte) ((packetLength >> 16) & 0xFF); 252 | data[0] = (byte) ((packetLength >> 24) & 0xFF); 253 | return data; 254 | } 255 | }); 256 | 257 | /** 258 | * 根据连接双方协议设置自动发送的包头数据 259 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 260 | * 261 | * 若无需包头可删除此行 262 | */ 263 | socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 264 | 265 | /** 266 | * 根据连接双方协议设置自动发送的包尾数据 267 | * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 268 | * 269 | * 若无需包尾可删除此行 270 | * 注意: 271 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 272 | */ 273 | socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 274 | 275 | /** 276 | * 设置分段发送数据长度 277 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 278 | * 279 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 280 | */ 281 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 282 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 283 | 284 | /** 285 | * 设置发送超时时长 286 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 287 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 288 | * 289 | * 若无需限制发送时长可删除此二行 290 | */ 291 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 292 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 293 | ``` 294 | 295 | ### 自动按包长度信息读取的接收配置 296 | ```java 297 | /** 298 | * 设置读取策略为自动读取指定长度 299 | */ 300 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadByLength); 301 | 302 | /** 303 | * 设置包长度转换器 304 | * 即每次接收数据时,将远程端发送到本地的长度信息byte[]转换为int,然后读取相应长度的值 305 | * 306 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 307 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 308 | * 2. socketClient接收长度为{@link SocketPacketHelper#getReceivePacketLengthDataLength()}(此处设置为4)的byte[],通过下面设置的转换器,将byte[]转换为int值,此int值暂时称为X 309 | * 3. socketClient接收长度为X的byte[] 310 | * 4. socketClient接收包尾(如果设置了包尾信息)(接收方式为一直读取到与包尾相同的byte[],如无意外情况,此处不会读取到多余的信息) 311 | * 5. socketClient回调数据包 312 | * 313 | * 此转换器用于第二步 314 | * 315 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 316 | * 用于分隔多条消息 317 | */ 318 | socketClient.getSocketPacketHelper().setReceivePacketLengthDataLength(4); 319 | socketClient.getSocketPacketHelper().setReceivePacketDataLengthConvertor(new SocketPacketHelper.ReceivePacketDataLengthConvertor() { 320 | @Override 321 | public int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData) { 322 | /** 323 | * 简单将byte[]转换为int 324 | */ 325 | int length = (packetLengthData[3] & 0xFF) + ((packetLengthData[2] & 0xFF) << 8) + ((packetLengthData[1] & 0xFF) << 16) + ((packetLengthData[0] & 0xFF) << 24); 326 | 327 | return length; 328 | } 329 | }); 330 | 331 | /** 332 | * 根据连接双方协议设置的包头数据 333 | * 每次接收数据包(包括心跳包)都会先接收此包头 334 | * 335 | * 若无需包头可删除此行 336 | */ 337 | socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 338 | 339 | /** 340 | * 根据连接双方协议设置的包尾数据 341 | * 342 | * 若无需包尾可删除此行 343 | * 注意: 344 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 345 | */ 346 | socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 347 | 348 | /** 349 | * 设置接收超时时长 350 | * 在指定时长内没有数据到达本地自动断开 351 | * 352 | * 若无需限制接收时长可删除此二行 353 | */ 354 | socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 355 | socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 356 | ``` 357 | 358 | ### 手动读取的发送配置 359 | ```java 360 | /** 361 | * 设置分段发送数据长度 362 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 363 | * 364 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 365 | */ 366 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 367 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 368 | 369 | /** 370 | * 设置发送超时时长 371 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 372 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 373 | * 374 | * 若无需限制发送时长可删除此二行 375 | */ 376 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 377 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 378 | ``` 379 | 380 | ### 手动读取的接收配置 381 | ```java 382 | /** 383 | * 设置读取策略为手动读取 384 | * 手动读取有两种方法 385 | * 1. {@link SocketClient#readDataToData(byte[], boolean)} )} 读取到与指定字节相同的字节序列后回调数据包 386 | * 2. {@link SocketClient#readDataToLength(int)} 读取指定长度的字节后回调数据包 387 | * 388 | * 此时SocketPacketHelper中其他读取相关设置将会无效化 389 | */ 390 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.Manually); 391 | ``` 392 | 393 | ### 常用回调配置 394 | ```java 395 | // 对应removeSocketClientDelegate 396 | socketClient.registerSocketClientDelegate(new SocketClientDelegate() { 397 | /** 398 | * 连接上远程端时的回调 399 | */ 400 | @Override 401 | public void onConnected(SocketClient client) { 402 | SocketPacket packet = socketClient.sendData(new byte[]{0x02}); // 发送消息 403 | packet = socketClient.sendString("sy hi!"); // 发送消息 404 | 405 | socketClient.cancelSend(packet); // 取消发送,若在等待发送队列中则从队列中移除,若正在发送则无法取消 406 | } 407 | 408 | /** 409 | * 与远程端断开连接时的回调 410 | */ 411 | @Override 412 | public void onDisconnected(SocketClient client) { 413 | // 可在此实现自动重连 414 | socketClient.connect(); 415 | } 416 | 417 | /** 418 | * 接收到数据包时的回调 419 | */ 420 | @Override 421 | public void onResponse(final SocketClient client, @NonNull SocketResponsePacket responsePacket) { 422 | byte[] data = responsePacket.getData(); // 获取接收的byte数组,不为null 423 | String message = responsePacket.getMessage(); // 获取按默认设置的编码转化的String,可能为null 424 | } 425 | }); 426 | ``` 427 | 428 | ### 发送状态回调配置 429 | ```java 430 | // 对应removeSocketClientSendingDelegate 431 | socketClient.registerSocketClientSendingDelegate(new SocketClientSendingDelegate() { 432 | /** 433 | * 数据包开始发送时的回调 434 | */ 435 | @Override 436 | public void onSendPacketBegin(SocketClient client, SocketPacket packet) { 437 | } 438 | 439 | /** 440 | * 数据包取消发送时的回调 441 | * 取消发送回调有以下情况: 442 | * 1. 手动cancel仍在排队,还未发送过的packet 443 | * 2. 断开连接时,正在发送的packet和所有在排队的packet都会被取消 444 | */ 445 | @Override 446 | public void onSendPacketCancel(SocketClient client, SocketPacket packet) { 447 | } 448 | 449 | /** 450 | * 数据包发送的进度回调 451 | * progress值为[0.0f, 1.0f] 452 | * 通常配合分段发送使用 453 | * 可用于显示文件等大数据的发送进度 454 | */ 455 | @Override 456 | public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) { 457 | } 458 | 459 | /** 460 | * 数据包完成发送时的回调 461 | */ 462 | @Override 463 | public void onSendPacketEnd(SocketClient client, SocketPacket packet) { 464 | } 465 | }); 466 | ``` 467 | 468 | ### 接收状态回调配置 469 | ```java 470 | // 对应removeSocketClientReceiveDelegate 471 | socketClient.registerSocketClientReceiveDelegate(new SocketClientReceivingDelegate() { 472 | /** 473 | * 开始接受一个新的数据包时的回调 474 | */ 475 | @Override 476 | public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) { 477 | } 478 | 479 | /** 480 | * 完成接受一个新的数据包时的回调 481 | */ 482 | @Override 483 | public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) { 484 | } 485 | 486 | /** 487 | * 取消接受一个新的数据包时的回调 488 | * 在断开连接时会触发 489 | */ 490 | @Override 491 | public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) { 492 | } 493 | 494 | /** 495 | * 接受一个新的数据包的进度回调 496 | * progress值为[0.0f, 1.0f] 497 | * 仅作用于ReadStrategy为AutoReadByLength的自动读取 498 | * 因AutoReadByLength可以首先接受到剩下的数据包长度 499 | */ 500 | @Override 501 | public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) { 502 | } 503 | }); 504 | ``` 505 | 506 | ## License 507 | [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 508 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.vilyever.androidsocketclient" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | 26 | compile project(":socketclient") 27 | compile 'com.github.vilyever:AndroidLogger:1.0.9' 28 | compile 'com.github.vilyever:AndroidJsonModel:1.2.0' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Development\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/vilyever/androidsocketclient/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/vilyever/androidsocketclient/App.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import android.app.Application; 4 | 5 | import com.vilyever.logger.Logger; 6 | import com.vilyever.logger.LoggerDisplay; 7 | 8 | /** 9 | * App 10 | * AndroidSocketClient 11 | * Created by vilyever on 2016/3/30. 12 | * Feature: 13 | */ 14 | public class App extends Application { 15 | final App self = this; 16 | 17 | 18 | /* Constructors */ 19 | 20 | 21 | /* Public Methods */ 22 | 23 | 24 | /* Properties */ 25 | 26 | 27 | /* Overrides */ 28 | 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | 33 | LoggerDisplay.initialize(this); 34 | LoggerDisplay.setDisplayLogTag(Logger.DefaultTag); 35 | } 36 | 37 | /* Delegates */ 38 | 39 | 40 | /* Private Methods */ 41 | 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vilyever/androidsocketclient/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import com.vilyever.contextholder.ContextHolder; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * FileUtil 9 | * Created by vilyever on 2016/6/1. 10 | * Feature: 11 | */ 12 | public class FileUtil { 13 | final FileUtil self = this; 14 | 15 | 16 | /* Constructors */ 17 | 18 | 19 | /* Public Methods */ 20 | public static File getCacheDir() { 21 | File dir = null; 22 | dir = ContextHolder.getContext().getExternalCacheDir(); 23 | if (dir != null && dir.isDirectory()) { 24 | return dir; 25 | } 26 | 27 | dir = ContextHolder.getContext().getCacheDir(); 28 | if (dir != null && dir.isDirectory()) { 29 | return dir; 30 | } 31 | 32 | return dir; 33 | } 34 | 35 | /* Properties */ 36 | 37 | 38 | /* Overrides */ 39 | 40 | 41 | /* Delegates */ 42 | 43 | 44 | /* Private Methods */ 45 | 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vilyever/androidsocketclient/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.widget.ImageView; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | final MainActivity self = this; 12 | 13 | private ImageView imageView; 14 | protected ImageView getImageView() { if (this.imageView == null) { this.imageView = (ImageView) findViewById(R.id.imageView); } return this.imageView; } 15 | 16 | private TestServer testServer; 17 | protected TestServer getTestServer() { 18 | if (this.testServer == null) { 19 | this.testServer = new TestServer(); 20 | } 21 | return this.testServer; 22 | } 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | 29 | Log.d("logger", "ts " + bytesToHex(encodeUINT16(18005))); 30 | Log.d("logger", "ts " + bytesToHex(encodeUINT16(18005))); 31 | 32 | getTestServer().beginListen(); 33 | } 34 | 35 | public byte[] encodeUINT16(int param) { 36 | byte[] bytes = ByteBuffer.allocate(4).putInt(param ^ 0xADAD).array(); 37 | 38 | byte[] encodedBytes = new byte[2]; 39 | encodedBytes[0] = bytes[3]; 40 | encodedBytes[1] = bytes[2]; 41 | return encodedBytes; 42 | } 43 | 44 | final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); 45 | public static String bytesToHex(byte[] bytes) { 46 | char[] hexChars = new char[bytes.length * 2]; 47 | for ( int j = 0; j < bytes.length; j++ ) { 48 | int v = bytes[j] & 0xFF; 49 | hexChars[j * 2] = hexArray[v >>> 4]; 50 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 51 | } 52 | return new String(hexChars); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/vilyever/androidsocketclient/TestClient.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import android.os.AsyncTask; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.vilyever.logger.Logger; 7 | import com.vilyever.socketclient.SocketClient; 8 | import com.vilyever.socketclient.helper.SocketClientDelegate; 9 | import com.vilyever.socketclient.helper.SocketClientReceivingDelegate; 10 | import com.vilyever.socketclient.helper.SocketClientSendingDelegate; 11 | import com.vilyever.socketclient.helper.SocketHeartBeatHelper; 12 | import com.vilyever.socketclient.helper.SocketPacket; 13 | import com.vilyever.socketclient.helper.SocketPacketHelper; 14 | import com.vilyever.socketclient.helper.SocketResponsePacket; 15 | import com.vilyever.socketclient.util.CharsetUtil; 16 | import com.vilyever.socketclient.util.IPUtil; 17 | 18 | import java.text.SimpleDateFormat; 19 | import java.util.Arrays; 20 | import java.util.Date; 21 | 22 | /** 23 | * TestClient 24 | * Created by vilyever on 2016/7/26. 25 | * Feature: 26 | */ 27 | public class TestClient { 28 | final TestClient self = this; 29 | 30 | 31 | /* Constructors */ 32 | 33 | 34 | /* Public Methods */ 35 | public void connect() { 36 | self.getLocalSocketClient().connect(); 37 | } 38 | 39 | /* Properties */ 40 | private SocketClient localSocketClient; 41 | public SocketClient getLocalSocketClient() { 42 | if (this.localSocketClient == null) { 43 | this.localSocketClient = new SocketClient(); 44 | 45 | __i__setupAddress(this.localSocketClient); 46 | __i__setupEncoding(this.localSocketClient); 47 | 48 | __i__setupConstantHeartBeat(this.localSocketClient); 49 | 50 | // __i__setupVariableHeartBeat(this.localSocketClient); 51 | 52 | // __i__setupReadToTrailerForSender(this.localSocketClient); 53 | // __i__setupReadToTrailerForReceiver(this.localSocketClient); 54 | 55 | __i__setupReadByLengthForSender(this.localSocketClient); 56 | __i__setupReadByLengthForReceiver(this.localSocketClient); 57 | 58 | // __i__setupReadManuallyForSender(this.localSocketClient); 59 | // __i__setupReadManuallyForReceiver(this.localSocketClient); 60 | 61 | this.localSocketClient.registerSocketClientDelegate(new SocketClientDelegate() { 62 | @Override 63 | public void onConnected(SocketClient client) { 64 | Logger.log("onConnected", "SocketClient: onConnected"); 65 | 66 | if (client.getSocketPacketHelper().getReadStrategy() == SocketPacketHelper.ReadStrategy.Manually) { 67 | client.readDataToLength(CharsetUtil.stringToData("Server accepted", CharsetUtil.UTF_8).length); 68 | } 69 | } 70 | 71 | @Override 72 | public void onDisconnected(final SocketClient client) { 73 | Logger.log("onDisconnected", "SocketClient: onDisconnected"); 74 | 75 | new AsyncTask() { 76 | @Override 77 | protected Void doInBackground(Void... params) { 78 | try { 79 | Thread.sleep(3 * 1000); 80 | } 81 | catch (InterruptedException e) { 82 | e.printStackTrace(); 83 | } 84 | 85 | client.connect(); 86 | 87 | return null; 88 | } 89 | 90 | @Override 91 | protected void onPostExecute(Void aVoid) { 92 | super.onPostExecute(aVoid); 93 | 94 | } 95 | }.execute(); 96 | } 97 | 98 | @Override 99 | public void onResponse(final SocketClient client, @NonNull SocketResponsePacket responsePacket) { 100 | Logger.log("onResponse", "SocketClient: onResponse: " + responsePacket.hashCode() + " 【" + responsePacket.getMessage() + "】 " + " isHeartBeat: " + responsePacket.isHeartBeat() + " " + Arrays.toString(responsePacket.getData())); 101 | if (responsePacket.isHeartBeat()) { 102 | return; 103 | } 104 | new AsyncTask() { 105 | @Override 106 | protected Void doInBackground(Void... params) { 107 | try { 108 | Thread.sleep(3 * 1000); 109 | } 110 | catch (InterruptedException e) { 111 | e.printStackTrace(); 112 | } 113 | 114 | client.sendString("client on " + System.currentTimeMillis()); 115 | 116 | try { 117 | Thread.sleep(3 * 1000); 118 | } 119 | catch (InterruptedException e) { 120 | e.printStackTrace(); 121 | } 122 | 123 | client.disconnect(); 124 | 125 | return null; 126 | } 127 | 128 | @Override 129 | protected void onPostExecute(Void aVoid) { 130 | super.onPostExecute(aVoid); 131 | 132 | } 133 | }.execute(); 134 | } 135 | }); 136 | this.localSocketClient.registerSocketClientSendingDelegate(new SocketClientSendingDelegate() { 137 | 138 | @Override 139 | public void onSendPacketBegin(SocketClient client, SocketPacket packet) { 140 | Logger.log("onSend", "SocketClient: onSendPacketBegin: " + packet.hashCode() + " " + Arrays.toString(packet.getData())); 141 | } 142 | 143 | @Override 144 | public void onSendPacketCancel(SocketClient client, SocketPacket packet) { 145 | Logger.log("onSend", "SocketClient: onSendPacketCancel: " + packet.hashCode()); 146 | } 147 | 148 | @Override 149 | public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) { 150 | Logger.log("onSend", "SocketClient: onSendingPacketInProgress: " + packet.hashCode() + " : " + progress + " : " + sendedLength); 151 | } 152 | 153 | @Override 154 | public void onSendPacketEnd(SocketClient client, SocketPacket packet) { 155 | Logger.log("onSend", "SocketClient: onSendPacketEnd: " + packet.hashCode()); 156 | } 157 | }); 158 | this.localSocketClient.registerSocketClientReceiveDelegate(new SocketClientReceivingDelegate() { 159 | @Override 160 | public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) { 161 | Logger.log("onReceive", "SocketClient: onReceivePacketBegin: " + packet.hashCode()); 162 | } 163 | 164 | @Override 165 | public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) { 166 | Logger.log("onReceive", "SocketClient: onReceivePacketEnd: " + packet.hashCode()); 167 | } 168 | 169 | @Override 170 | public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) { 171 | Logger.log("onReceive", "SocketClient: onReceivePacketCancel: " + packet.hashCode()); 172 | } 173 | 174 | @Override 175 | public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) { 176 | Logger.log("onReceive", "SocketClient: onReceivingPacketInProgress: " + packet.hashCode() + " : " + progress + " : " + receivedLength); 177 | } 178 | }); 179 | } 180 | return this.localSocketClient; 181 | } 182 | 183 | /* Overrides */ 184 | 185 | 186 | /* Delegates */ 187 | 188 | 189 | /* Private Methods */ 190 | /** 191 | * 设置远程端地址信息 192 | */ 193 | private void __i__setupAddress(SocketClient socketClient) { 194 | socketClient.getAddress().setRemoteIP(IPUtil.getLocalIPAddress(true)); // 远程端IP地址 195 | socketClient.getAddress().setRemotePort("21998"); // 远程端端口号 196 | socketClient.getAddress().setConnectionTimeout(15 * 1000); // 连接超时时长,单位毫秒 197 | } 198 | 199 | /** 200 | * 设置自动转换String类型到byte[]类型的编码 201 | * 如未设置(默认为null),将不能使用{@link SocketClient#sendString(String)}发送消息 202 | * 如设置为非null(如UTF-8),在接受消息时会自动尝试在接收线程(非主线程)将接收的byte[]数据依照编码转换为String,在{@link SocketResponsePacket#getMessage()}读取 203 | */ 204 | private void __i__setupEncoding(SocketClient socketClient) { 205 | socketClient.setCharsetName(CharsetUtil.UTF_8); // 设置编码为UTF-8 206 | } 207 | 208 | private void __i__setupConstantHeartBeat(SocketClient socketClient) { 209 | /** 210 | * 设置自动发送的心跳包信息 211 | */ 212 | socketClient.getHeartBeatHelper().setDefaultSendData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 213 | 214 | /** 215 | * 设置远程端发送到本地的心跳包信息内容,用于判断接收到的数据包是否是心跳包 216 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 217 | */ 218 | socketClient.getHeartBeatHelper().setDefaultReceiveData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 219 | socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 220 | socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 221 | } 222 | 223 | private void __i__setupVariableHeartBeat(SocketClient socketClient) { 224 | /** 225 | * 设置自动发送的心跳包信息 226 | * 此信息动态生成 227 | * 228 | * 每次发送心跳包时自动调用 229 | */ 230 | socketClient.getHeartBeatHelper().setSendDataBuilder(new SocketHeartBeatHelper.SendDataBuilder() { 231 | @Override 232 | public byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper) { 233 | /** 234 | * 使用当前日期作为心跳包 235 | */ 236 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 237 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 238 | 239 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 240 | byte[] heartBeatInfo = CharsetUtil.stringToData(sdf.format(new Date()), CharsetUtil.UTF_8); 241 | 242 | byte[] data = new byte[heartBeatPrefix.length + heartBeatSuffix.length + heartBeatInfo.length]; 243 | System.arraycopy(heartBeatPrefix, 0, data, 0, heartBeatPrefix.length); 244 | System.arraycopy(heartBeatInfo, 0, data, heartBeatPrefix.length, heartBeatInfo.length); 245 | System.arraycopy(heartBeatSuffix, 0, data, heartBeatPrefix.length + heartBeatInfo.length, heartBeatSuffix.length); 246 | 247 | return data; 248 | } 249 | }); 250 | 251 | /** 252 | * 设置远程端发送到本地的心跳包信息的检测器,用于判断接收到的数据包是否是心跳包 253 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 254 | */ 255 | socketClient.getHeartBeatHelper().setReceiveHeartBeatPacketChecker(new SocketHeartBeatHelper.ReceiveHeartBeatPacketChecker() { 256 | @Override 257 | public boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet) { 258 | /** 259 | * 判断数据包信息是否含有指定的心跳包前缀和后缀 260 | */ 261 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 262 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 263 | 264 | if (Arrays.equals(heartBeatPrefix, Arrays.copyOfRange(packet.getData(), 0, heartBeatPrefix.length)) 265 | && Arrays.equals(heartBeatSuffix, Arrays.copyOfRange(packet.getData(), packet.getData().length - heartBeatSuffix.length, packet.getData().length))) { 266 | return true; 267 | } 268 | 269 | return false; 270 | } 271 | }); 272 | 273 | socketClient.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 274 | socketClient.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 275 | } 276 | 277 | private void __i__setupReadToTrailerForSender(SocketClient socketClient) { 278 | /** 279 | * 根据连接双方协议设置自动发送的包尾数据 280 | * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 281 | * 282 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 283 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 284 | * 2. socketClient向远程端发送正文数据{0x01, 0x02} 285 | * 3. socketClient向远程端发送包尾 286 | * 287 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 288 | * 用于分隔多条消息 289 | */ 290 | socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 291 | 292 | /** 293 | * 根据连接双方协议设置自动发送的包头数据 294 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 295 | * 296 | * 若无需包头可删除此行 297 | */ 298 | socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 299 | 300 | /** 301 | * 设置分段发送数据长度 302 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 303 | * 注意:回调过于频繁可能导致设置UI过于频繁从而导致主线程卡顿 304 | * 305 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 306 | */ 307 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 308 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 309 | 310 | /** 311 | * 设置发送超时时长 312 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 313 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 314 | * 315 | * 若无需限制发送时长可删除此二行 316 | */ 317 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 318 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 319 | } 320 | 321 | private void __i__setupReadToTrailerForReceiver(SocketClient socketClient) { 322 | /** 323 | * 设置读取策略为自动读取到指定的包尾 324 | */ 325 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadToTrailer); 326 | 327 | /** 328 | * 根据连接双方协议设置的包尾数据 329 | * 每次接收数据包(包括心跳包)都会在检测接收到与包尾数据相同的byte[]时回调一个数据包 330 | * 331 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 332 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 333 | * 2. socketClient接收正文和包尾(接收方式为一直读取到与尾相同的byte[]) 334 | * 3. socketClient回调数据包 335 | * 336 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 337 | * 用于分隔多条消息 338 | */ 339 | socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 340 | 341 | /** 342 | * 根据连接双方协议设置的包头数据 343 | * 每次接收数据包(包括心跳包)都会先接收此包头 344 | * 345 | * 若无需包头可删除此行 346 | */ 347 | socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 348 | 349 | /** 350 | * 设置接收超时时长 351 | * 在指定时长内没有数据到达本地自动断开 352 | * 353 | * 若无需限制接收时长可删除此二行 354 | */ 355 | socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 356 | socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 357 | } 358 | 359 | private void __i__setupReadByLengthForSender(SocketClient socketClient) { 360 | /** 361 | * 设置包长度转换器 362 | * 即每次发送数据时,将包头以外的数据长度转换为特定的byte[]发送个远程端用于解析还需要读取多少长度的数据 363 | * 364 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 365 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 366 | * 2. socketClient要发送的数据为{0x01, 0x02},长度为2(若设置了包尾,还需加上包尾的字节长度),通过此转换器将int类型的2转换为4字节的byte[],远程端也照此算法将4字节的byte[]转换为int值 367 | * 3. socketClient向远程端发送转换后的长度信息byte[] 368 | * 4. socketClient向远程端发送正文数据{0x01, 0x02} 369 | * 5. socketClient向远程端发送包尾(如果设置了包尾信息) 370 | * 371 | * 此转换器用于第二步 372 | * 373 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 374 | * 用于分隔多条消息 375 | */ 376 | socketClient.getSocketPacketHelper().setSendPacketLengthDataConvertor(new SocketPacketHelper.SendPacketLengthDataConvertor() { 377 | @Override 378 | public byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength) { 379 | /** 380 | * 简单将int转换为byte[] 381 | */ 382 | byte[] data = new byte[4]; 383 | data[3] = (byte) (packetLength & 0xFF); 384 | data[2] = (byte) ((packetLength >> 8) & 0xFF); 385 | data[1] = (byte) ((packetLength >> 16) & 0xFF); 386 | data[0] = (byte) ((packetLength >> 24) & 0xFF); 387 | return data; 388 | } 389 | }); 390 | 391 | /** 392 | * 根据连接双方协议设置自动发送的包头数据 393 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 394 | * 395 | * 若无需包头可删除此行 396 | */ 397 | socketClient.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 398 | 399 | /** 400 | * 根据连接双方协议设置自动发送的包尾数据 401 | * 每次发送数据包(包括心跳包)都会在发送包内容后自动发送此包尾 402 | * 403 | * 若无需包尾可删除此行 404 | * 注意: 405 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 406 | */ 407 | socketClient.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 408 | 409 | /** 410 | * 设置分段发送数据长度 411 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 412 | * 注意:回调过于频繁可能导致设置UI过于频繁从而导致主线程卡顿 413 | * 414 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 415 | */ 416 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 417 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 418 | 419 | /** 420 | * 设置发送超时时长 421 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 422 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 423 | * 424 | * 若无需限制发送时长可删除此二行 425 | */ 426 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 427 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 428 | } 429 | 430 | private void __i__setupReadByLengthForReceiver(SocketClient socketClient) { 431 | /** 432 | * 设置读取策略为自动读取指定长度 433 | */ 434 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadByLength); 435 | 436 | /** 437 | * 设置包长度转换器 438 | * 即每次接收数据时,将远程端发送到本地的长度信息byte[]转换为int,然后读取相应长度的值 439 | * 440 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 441 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 442 | * 2. socketClient接收长度为{@link SocketPacketHelper#getReceivePacketLengthDataLength()}(此处设置为4)的byte[],通过下面设置的转换器,将byte[]转换为int值,此int值暂时称为X 443 | * 3. socketClient接收长度为X的byte[] 444 | * 4. socketClient接收包尾(如果设置了包尾信息)(接收方式为一直读取到与包尾相同的byte[],如无意外情况,此处不会读取到多余的信息) 445 | * 5. socketClient回调数据包 446 | * 447 | * 此转换器用于第二步 448 | * 449 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 450 | * 用于分隔多条消息 451 | */ 452 | socketClient.getSocketPacketHelper().setReceivePacketLengthDataLength(4); 453 | socketClient.getSocketPacketHelper().setReceivePacketDataLengthConvertor(new SocketPacketHelper.ReceivePacketDataLengthConvertor() { 454 | @Override 455 | public int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData) { 456 | /** 457 | * 简单将byte[]转换为int 458 | */ 459 | int length = (packetLengthData[3] & 0xFF) + ((packetLengthData[2] & 0xFF) << 8) + ((packetLengthData[1] & 0xFF) << 16) + ((packetLengthData[0] & 0xFF) << 24); 460 | 461 | return length; 462 | } 463 | }); 464 | 465 | /** 466 | * 根据连接双方协议设置的包头数据 467 | * 每次接收数据包(包括心跳包)都会先接收此包头 468 | * 469 | * 若无需包头可删除此行 470 | */ 471 | socketClient.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 472 | 473 | /** 474 | * 根据连接双方协议设置的包尾数据 475 | * 476 | * 若无需包尾可删除此行 477 | * 注意: 478 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 479 | */ 480 | socketClient.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 481 | 482 | /** 483 | * 设置接收超时时长 484 | * 在指定时长内没有数据到达本地自动断开 485 | * 486 | * 若无需限制接收时长可删除此二行 487 | */ 488 | socketClient.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 489 | socketClient.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 490 | } 491 | 492 | private void __i__setupReadManuallyForSender(SocketClient socketClient) { 493 | /** 494 | * 设置分段发送数据长度 495 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 496 | * 注意:回调过于频繁可能导致设置UI过于频繁从而导致主线程卡顿 497 | * 498 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 499 | */ 500 | socketClient.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 501 | socketClient.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 502 | 503 | /** 504 | * 设置发送超时时长 505 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 506 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 507 | * 508 | * 若无需限制发送时长可删除此二行 509 | */ 510 | socketClient.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 511 | socketClient.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 512 | } 513 | 514 | private void __i__setupReadManuallyForReceiver(SocketClient socketClient) { 515 | /** 516 | * 设置读取策略为手动读取 517 | * 手动读取有两种方法 518 | * 1. {@link SocketClient#readDataToData(byte[], boolean)} )} 读取到与指定字节相同的字节序列后回调数据包 519 | * 2. {@link SocketClient#readDataToLength(int)} 读取指定长度的字节后回调数据包 520 | * 521 | * 此时SocketPacketHelper中其他读取相关设置将会无效化 522 | */ 523 | socketClient.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.Manually); 524 | } 525 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vilyever/androidsocketclient/TestServer.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.androidsocketclient; 2 | 3 | import android.os.AsyncTask; 4 | import android.support.annotation.NonNull; 5 | import android.widget.Toast; 6 | 7 | import com.vilyever.contextholder.ContextHolder; 8 | import com.vilyever.logger.Logger; 9 | import com.vilyever.socketclient.SocketClient; 10 | import com.vilyever.socketclient.helper.SocketClientDelegate; 11 | import com.vilyever.socketclient.helper.SocketClientReceivingDelegate; 12 | import com.vilyever.socketclient.helper.SocketClientSendingDelegate; 13 | import com.vilyever.socketclient.helper.SocketHeartBeatHelper; 14 | import com.vilyever.socketclient.helper.SocketPacket; 15 | import com.vilyever.socketclient.helper.SocketPacketHelper; 16 | import com.vilyever.socketclient.helper.SocketResponsePacket; 17 | import com.vilyever.socketclient.server.SocketServer; 18 | import com.vilyever.socketclient.server.SocketServerClient; 19 | import com.vilyever.socketclient.server.SocketServerDelegate; 20 | import com.vilyever.socketclient.util.CharsetUtil; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Arrays; 24 | import java.util.Date; 25 | 26 | /** 27 | * TestServer 28 | * Created by vilyever on 2016/7/26. 29 | * Feature: 30 | */ 31 | public class TestServer { 32 | final TestServer self = this; 33 | 34 | 35 | /* Constructors */ 36 | public TestServer() { 37 | 38 | } 39 | 40 | /* Public Methods */ 41 | public void beginListen() { 42 | int port = getSocketServer().beginListenFromPort(21998); 43 | Toast.makeText(ContextHolder.getContext(), "port " + port, Toast.LENGTH_LONG).show(); 44 | } 45 | 46 | /* Properties */ 47 | private SocketServer socketServer; 48 | protected SocketServer getSocketServer() { 49 | if (this.socketServer == null) { 50 | this.socketServer = new SocketServer(); 51 | 52 | __i__setupEncoding(this.socketServer); 53 | 54 | __i__setupConstantHeartBeat(this.socketServer); 55 | 56 | // __i__setupVariableHeartBeat(this.socketServer); 57 | 58 | // __i__setupReadToTrailerForSender(this.socketServer); 59 | // __i__setupReadToTrailerForReceiver(this.socketServer); 60 | 61 | __i__setupReadByLengthForSender(this.socketServer); 62 | __i__setupReadByLengthForReceiver(this.socketServer); 63 | 64 | // __i__setupReadManuallyForSender(this.socketServer); 65 | // __i__setupReadManuallyForReceiver(this.socketServer); 66 | 67 | this.socketServer.registerSocketServerDelegate(new SocketServerDelegate() { 68 | @Override 69 | public void onServerBeginListen(SocketServer socketServer, int port) { 70 | Logger.log("onServer", "SocketServer: begin listen " + port); 71 | 72 | getTestClient().connect(); 73 | } 74 | 75 | @Override 76 | public void onServerStopListen(SocketServer socketServer, int port) { 77 | Logger.log("onServer", "SocketServer: stop listen " + port); 78 | } 79 | 80 | @Override 81 | public void onClientConnected(SocketServer socketServer, SocketServerClient socketServerClient) { 82 | Logger.log("onServer", "SocketServer: onClientConnected"); 83 | 84 | self.setServerListeningSocketServerClient(socketServerClient); 85 | socketServerClient.sendString("Server accepted"); 86 | } 87 | 88 | @Override 89 | public void onClientDisconnected(SocketServer socketServer, SocketServerClient socketServerClient) { 90 | Logger.log("onServer", "SocketServer: onClientDisconnected"); 91 | 92 | self.setServerListeningSocketServerClient(null); 93 | } 94 | }); 95 | } 96 | return this.socketServer; 97 | } 98 | 99 | private SocketServerClient serverListeningSocketServerClient; 100 | protected TestServer setServerListeningSocketServerClient(SocketServerClient serverListeningSocketServerClient) { 101 | this.serverListeningSocketServerClient = serverListeningSocketServerClient; 102 | if (serverListeningSocketServerClient == null) { 103 | return this; 104 | } 105 | 106 | this.serverListeningSocketServerClient.registerSocketClientDelegate(new SocketClientDelegate() { 107 | @Override 108 | public void onConnected(SocketClient client) { 109 | Logger.log("onConnected", "SocketServerClient: onConnected"); 110 | /** 111 | * 此处永不回调 112 | * 在{@link SocketServerDelegate#onClientConnected(SocketServer, SocketServerClient)} 处处理新client接入时的操作 113 | */ 114 | } 115 | 116 | @Override 117 | public void onDisconnected(SocketClient client) { 118 | Logger.log("onDisconnected", "SocketServerClient: onDisconnected"); 119 | } 120 | 121 | @Override 122 | public void onResponse(final SocketClient client, @NonNull SocketResponsePacket responsePacket) { 123 | Logger.log("onResponse", "SocketServerClient: onResponse: " + responsePacket.hashCode() + " 【" + responsePacket.getMessage() + "】 " + " isHeartBeat: " + responsePacket.isHeartBeat() + " " + Arrays.toString(responsePacket.getData())); 124 | if (responsePacket.isHeartBeat()) { 125 | return; 126 | } 127 | new AsyncTask() { 128 | @Override 129 | protected Void doInBackground(Void... params) { 130 | try { 131 | Thread.sleep(3 * 1000); 132 | } 133 | catch (InterruptedException e) { 134 | e.printStackTrace(); 135 | } 136 | 137 | client.sendString("server on " + System.currentTimeMillis()); 138 | 139 | return null; 140 | } 141 | 142 | @Override 143 | protected void onPostExecute(Void aVoid) { 144 | super.onPostExecute(aVoid); 145 | 146 | } 147 | }.execute(); 148 | } 149 | }); 150 | this.serverListeningSocketServerClient.registerSocketClientSendingDelegate(new SocketClientSendingDelegate() { 151 | 152 | @Override 153 | public void onSendPacketBegin(SocketClient client, SocketPacket packet) { 154 | Logger.log("onSend", "SocketServerClient: onSendPacketBegin: " + packet.hashCode() + " " + Arrays.toString(packet.getData())); 155 | } 156 | 157 | @Override 158 | public void onSendPacketCancel(SocketClient client, SocketPacket packet) { 159 | Logger.log("onSend", "SocketServerClient: onSendPacketCancel: " + packet.hashCode()); 160 | } 161 | 162 | @Override 163 | public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) { 164 | Logger.log("onSend", "SocketServerClient: onSendingPacketInProgress: " + packet.hashCode() + " : " + progress + " : " + sendedLength); 165 | } 166 | 167 | @Override 168 | public void onSendPacketEnd(SocketClient client, SocketPacket packet) { 169 | Logger.log("onSend", "SocketServerClient: onSendPacketEnd: " + packet.hashCode()); 170 | } 171 | 172 | }); 173 | this.serverListeningSocketServerClient.registerSocketClientReceiveDelegate(new SocketClientReceivingDelegate() { 174 | @Override 175 | public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) { 176 | Logger.log("onReceive", "SocketServerClient: onReceivePacketBegin: " + packet.hashCode()); 177 | } 178 | 179 | @Override 180 | public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) { 181 | Logger.log("onReceive", "SocketServerClient: onReceivePacketEnd: " + packet.hashCode()); 182 | } 183 | 184 | @Override 185 | public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) { 186 | Logger.log("onReceive", "SocketServerClient: onReceivePacketCancel: " + packet.hashCode()); 187 | } 188 | 189 | @Override 190 | public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) { 191 | Logger.log("onReceive", "SocketServerClient: onReceivingPacketInProgress: " + packet.hashCode() + " : " + progress + " : " + receivedLength); 192 | } 193 | }); 194 | return this; 195 | } 196 | protected SocketClient getServerListeningSocketServerClient() { 197 | return this.serverListeningSocketServerClient; 198 | } 199 | 200 | private TestClient testClient; 201 | protected TestClient getTestClient() { 202 | if (this.testClient == null) { 203 | this.testClient = new TestClient(); 204 | } 205 | return this.testClient; 206 | } 207 | 208 | /* Overrides */ 209 | 210 | 211 | /* Delegates */ 212 | 213 | 214 | /* Private Methods */ 215 | /** 216 | * 设置自动转换String类型到byte[]类型的编码 217 | * 如未设置(默认为null),将不能使用{@link SocketClient#sendString(String)}发送消息 218 | * 如设置为非null(如UTF-8),在接受消息时会自动尝试在接收线程(非主线程)将接收的byte[]数据依照编码转换为String,在{@link SocketResponsePacket#getMessage()}读取 219 | */ 220 | private void __i__setupEncoding(SocketServer socketServer) { 221 | socketServer.setCharsetName(CharsetUtil.UTF_8); // 设置编码为UTF-8 222 | } 223 | 224 | private void __i__setupConstantHeartBeat(SocketServer socketServer) { 225 | /** 226 | * 设置自动发送的心跳包信息 227 | */ 228 | socketServer.getHeartBeatHelper().setDefaultSendData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 229 | 230 | /** 231 | * 设置远程端发送到本地的心跳包信息内容,用于判断接收到的数据包是否是心跳包 232 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 233 | */ 234 | socketServer.getHeartBeatHelper().setDefaultReceiveData(CharsetUtil.stringToData("HeartBeat", CharsetUtil.UTF_8)); 235 | socketServer.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 236 | socketServer.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 237 | } 238 | 239 | private void __i__setupVariableHeartBeat(SocketServer socketServer) { 240 | /** 241 | * 设置自动发送的心跳包信息 242 | * 此信息动态生成 243 | * 244 | * 每次发送心跳包时自动调用 245 | */ 246 | socketServer.getHeartBeatHelper().setSendDataBuilder(new SocketHeartBeatHelper.SendDataBuilder() { 247 | @Override 248 | public byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper) { 249 | /** 250 | * 使用当前日期作为心跳包 251 | */ 252 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 253 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 254 | 255 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 256 | byte[] heartBeatInfo = CharsetUtil.stringToData(sdf.format(new Date()), CharsetUtil.UTF_8); 257 | 258 | byte[] data = new byte[heartBeatPrefix.length + heartBeatSuffix.length + heartBeatInfo.length]; 259 | System.arraycopy(heartBeatPrefix, 0, data, 0, heartBeatPrefix.length); 260 | System.arraycopy(heartBeatInfo, 0, data, heartBeatPrefix.length, heartBeatInfo.length); 261 | System.arraycopy(heartBeatSuffix, 0, data, heartBeatPrefix.length + heartBeatInfo.length, heartBeatSuffix.length); 262 | 263 | return data; 264 | } 265 | }); 266 | 267 | /** 268 | * 设置远程端发送到本地的心跳包信息的检测器,用于判断接收到的数据包是否是心跳包 269 | * 通过{@link SocketResponsePacket#isHeartBeat()} 查看数据包是否是心跳包 270 | */ 271 | socketServer.getHeartBeatHelper().setReceiveHeartBeatPacketChecker(new SocketHeartBeatHelper.ReceiveHeartBeatPacketChecker() { 272 | @Override 273 | public boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet) { 274 | /** 275 | * 判断数据包信息是否含有指定的心跳包前缀和后缀 276 | */ 277 | byte[] heartBeatPrefix = new byte[]{0x1F, 0x1F}; 278 | byte[] heartBeatSuffix = new byte[]{0x1F, 0x1F}; 279 | 280 | if (Arrays.equals(heartBeatPrefix, Arrays.copyOfRange(packet.getData(), 0, heartBeatPrefix.length)) 281 | && Arrays.equals(heartBeatSuffix, Arrays.copyOfRange(packet.getData(), packet.getData().length - heartBeatSuffix.length, packet.getData().length))) { 282 | return true; 283 | } 284 | 285 | return false; 286 | } 287 | }); 288 | 289 | socketServer.getHeartBeatHelper().setHeartBeatInterval(10 * 1000); // 设置自动发送心跳包的间隔时长,单位毫秒 290 | socketServer.getHeartBeatHelper().setSendHeartBeatEnabled(true); // 设置允许自动发送心跳包,此值默认为false 291 | } 292 | 293 | private void __i__setupReadToTrailerForSender(SocketServer socketServer) { 294 | /** 295 | * 根据连接双方协议设置自动发送的包尾数据 296 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包尾 297 | * 298 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 299 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 300 | * 2. socketClient向远程端发送正文数据{0x01, 0x02} 301 | * 3. socketClient向远程端发送包尾 302 | * 303 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 304 | * 用于分隔多条消息 305 | */ 306 | socketServer.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 307 | 308 | /** 309 | * 根据连接双方协议设置自动发送的包头数据 310 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 311 | * 312 | * 若无需包头可删除此行 313 | */ 314 | socketServer.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 315 | 316 | /** 317 | * 设置分段发送数据长度 318 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 319 | * 注意:无论设置的分段长度为多小,回调的频率最高为1秒30次,防止因此产生主线程的卡顿 320 | * 321 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 322 | */ 323 | socketServer.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 324 | socketServer.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 325 | 326 | /** 327 | * 设置发送超时时长 328 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 329 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 330 | * 331 | * 若无需限制发送时长可删除此二行 332 | */ 333 | socketServer.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 334 | socketServer.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 335 | } 336 | 337 | private void __i__setupReadToTrailerForReceiver(SocketServer socketServer) { 338 | /** 339 | * 设置读取策略为自动读取到指定的包尾 340 | */ 341 | socketServer.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadToTrailer); 342 | 343 | /** 344 | * 根据连接双方协议设置的包尾数据 345 | * 每次接收数据包(包括心跳包)都会在检测接收到与包尾数据相同的byte[]时回调一个数据包 346 | * 347 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 348 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 349 | * 2. socketClient接收正文和包尾(接收方式为一直读取到与尾相同的byte[]) 350 | * 3. socketClient回调数据包 351 | * 352 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 353 | * 用于分隔多条消息 354 | */ 355 | socketServer.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 356 | 357 | /** 358 | * 根据连接双方协议设置的包头数据 359 | * 每次接收数据包(包括心跳包)都会在先接收此包头 360 | * 361 | * 若无需包头可删除此行 362 | */ 363 | socketServer.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 364 | 365 | /** 366 | * 设置接收超时时长 367 | * 在指定时长内没有数据到达本地自动断开 368 | * 369 | * 若无需限制接收时长可删除此二行 370 | */ 371 | socketServer.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 372 | socketServer.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 373 | } 374 | 375 | private void __i__setupReadByLengthForSender(SocketServer socketServer) { 376 | /** 377 | * 设置包长度转换器 378 | * 即每次发送数据时,将包头以外的数据长度转换为特定的byte[]发送个远程端用于解析还需要读取多少长度的数据 379 | * 380 | * 例:socketClient.sendData(new byte[]{0x01, 0x02})的步骤为 381 | * 1. socketClient向远程端发送包头(如果设置了包头信息) 382 | * 2. socketClient要发送的数据为{0x01, 0x02},长度为2(若设置了包尾,还需加上包尾的字节长度),通过此转换器将int类型的2转换为4字节的byte[],远程端也照此算法将4字节的byte[]转换为int值 383 | * 3. socketClient向远程端发送转换后的长度信息byte[] 384 | * 4. socketClient向远程端发送正文数据{0x01, 0x02} 385 | * 5. socketClient向远程端发送包尾(如果设置了包尾信息) 386 | * 387 | * 此转换器用于第二步 388 | * 389 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 390 | * 用于分隔多条消息 391 | */ 392 | socketServer.getSocketPacketHelper().setSendPacketLengthDataConvertor(new SocketPacketHelper.SendPacketLengthDataConvertor() { 393 | @Override 394 | public byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength) { 395 | /** 396 | * 简单将int转换为byte[] 397 | */ 398 | byte[] data = new byte[4]; 399 | data[3] = (byte) (packetLength & 0xFF); 400 | data[2] = (byte) ((packetLength >> 8) & 0xFF); 401 | data[1] = (byte) ((packetLength >> 16) & 0xFF); 402 | data[0] = (byte) ((packetLength >> 24) & 0xFF); 403 | return data; 404 | } 405 | }); 406 | 407 | /** 408 | * 根据连接双方协议设置自动发送的包头数据 409 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包头 410 | * 411 | * 若无需包头可删除此行 412 | */ 413 | socketServer.getSocketPacketHelper().setSendHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 414 | 415 | /** 416 | * 根据连接双方协议设置自动发送的包尾数据 417 | * 每次发送数据包(包括心跳包)都会在发送包内容前自动发送此包尾 418 | * 419 | * 若无需包尾可删除此行 420 | * 注意: 421 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}时不依赖包尾读取数据 422 | */ 423 | socketServer.getSocketPacketHelper().setSendTrailerData(new byte[]{0x13, 0x10}); 424 | 425 | /** 426 | * 设置分段发送数据长度 427 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 428 | * 注意:无论设置的分段长度为多小,回调的频率最高为1秒30次,防止因此产生主线程的卡顿 429 | * 430 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 431 | */ 432 | socketServer.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 433 | socketServer.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 434 | 435 | /** 436 | * 设置发送超时时长 437 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 438 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 439 | * 440 | * 若无需限制发送时长可删除此二行 441 | */ 442 | socketServer.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 443 | socketServer.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 444 | } 445 | 446 | private void __i__setupReadByLengthForReceiver(SocketServer socketServer) { 447 | /** 448 | * 设置读取策略为自动读取指定长度 449 | */ 450 | socketServer.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.AutoReadByLength); 451 | 452 | /** 453 | * 设置包长度转换器 454 | * 即每次接收数据时,将远程端发送到本地的长度信息byte[]转换为int,然后读取相应长度的值 455 | * 456 | * 例:自动接收远程端所发送的socketClient.sendData(new byte[]{0x01, 0x02})【{0x01, 0x02}为将要接收的数据】的步骤为 457 | * 1. socketClient接收包头(如果设置了包头信息)(接收方式为一直读取到与包头相同的byte[],即可能过滤掉包头前的多余信息) 458 | * 2. socketClient接收长度为{@link SocketPacketHelper#getReceivePacketLengthDataLength()}(此处设置为4)的byte[],通过下面设置的转换器,将byte[]转换为int值,此int值暂时称为X 459 | * 3. socketClient接收长度为X的byte[] 460 | * 4. socketClient接收包尾(如果设置了包尾信息)(接收方式为一直读取到与包尾相同的byte[],如无意外情况,此处不会读取到多余的信息) 461 | * 5. socketClient回调数据包 462 | * 463 | * 此转换器用于第二步 464 | * 465 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadByLength}必须设置此项 466 | * 用于分隔多条消息 467 | */ 468 | socketServer.getSocketPacketHelper().setReceivePacketLengthDataLength(4); 469 | socketServer.getSocketPacketHelper().setReceivePacketDataLengthConvertor(new SocketPacketHelper.ReceivePacketDataLengthConvertor() { 470 | @Override 471 | public int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData) { 472 | /** 473 | * 简单将byte[]转换为int 474 | */ 475 | int length = (packetLengthData[3] & 0xFF) + ((packetLengthData[2] & 0xFF) << 8) + ((packetLengthData[1] & 0xFF) << 16) + ((packetLengthData[0] & 0xFF) << 24); 476 | 477 | return length; 478 | } 479 | }); 480 | 481 | /** 482 | * 根据连接双方协议设置的包头数据 483 | * 每次接收数据包(包括心跳包)都会在先接收此包头 484 | * 485 | * 若无需包头可删除此行 486 | */ 487 | socketServer.getSocketPacketHelper().setReceiveHeaderData(CharsetUtil.stringToData("SocketClient:", CharsetUtil.UTF_8)); 488 | 489 | /** 490 | * 根据连接双方协议设置的包尾数据 491 | * 每次接收数据包(包括心跳包)都会在检测接收到与包尾数据相同的byte[]时回调一个数据包 492 | * 493 | * 使用{@link com.vilyever.socketclient.helper.SocketPacketHelper.ReadStrategy.AutoReadToTrailer}必须设置此项 494 | * 用于分隔多条消息 495 | */ 496 | socketServer.getSocketPacketHelper().setReceiveTrailerData(new byte[]{0x13, 0x10}); 497 | 498 | /** 499 | * 设置接收超时时长 500 | * 在指定时长内没有数据到达本地自动断开 501 | * 502 | * 若无需限制接收时长可删除此二行 503 | */ 504 | socketServer.getSocketPacketHelper().setReceiveTimeout(120 * 1000); // 设置接收超时时长,单位毫秒 505 | socketServer.getSocketPacketHelper().setReceiveTimeoutEnabled(true); // 设置允许使用接收超时时长,此值默认为false 506 | } 507 | 508 | private void __i__setupReadManuallyForSender(SocketServer socketServer) { 509 | /** 510 | * 设置分段发送数据长度 511 | * 即在发送指定长度后通过 {@link SocketClientSendingDelegate#onSendingPacketInProgress(SocketClient, SocketPacket, float, int)}回调当前发送进度 512 | * 注意:无论设置的分段长度为多小,回调的频率最高为1秒30次,防止因此产生主线程的卡顿 513 | * 514 | * 若无需进度回调可删除此二行,删除后仍有【发送开始】【发送结束】的回调 515 | */ 516 | socketServer.getSocketPacketHelper().setSendSegmentLength(8); // 设置发送分段长度,单位byte 517 | socketServer.getSocketPacketHelper().setSendSegmentEnabled(true); // 设置允许使用分段发送,此值默认为false 518 | 519 | /** 520 | * 设置发送超时时长 521 | * 在发送每个数据包时,发送每段数据的最长时间,超过后自动断开socket连接 522 | * 通过设置分段发送{@link SocketPacketHelper#setSendSegmentEnabled(boolean)} 可避免发送大数据包时因超时断开, 523 | * 524 | * 若无需限制发送时长可删除此二行 525 | */ 526 | socketServer.getSocketPacketHelper().setSendTimeout(30 * 1000); // 设置发送超时时长,单位毫秒 527 | socketServer.getSocketPacketHelper().setSendTimeoutEnabled(true); // 设置允许使用发送超时时长,此值默认为false 528 | } 529 | 530 | private void __i__setupReadManuallyForReceiver(SocketServer socketServer) { 531 | /** 532 | * 设置读取策略为手动读取 533 | * 手动读取有两种方法 534 | * 1. {@link SocketClient#readDataToData(byte[], boolean)} )} 读取到与指定字节相同的字节序列后回调数据包 535 | * 2. {@link SocketClient#readDataToLength(int)} 读取指定长度的字节后回调数据包 536 | * 537 | * 此时SocketPacketHelper中其他读取相关设置将会无效化 538 | */ 539 | socketServer.getSocketPacketHelper().setReadStrategy(SocketPacketHelper.ReadStrategy.Manually); 540 | } 541 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vilyever/AndroidSocketClient/a93613154993bcd9f7dbeb1e9ea19cc5ccfd7d73/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vilyever/AndroidSocketClient/a93613154993bcd9f7dbeb1e9ea19cc5ccfd7d73/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vilyever/AndroidSocketClient/a93613154993bcd9f7dbeb1e9ea19cc5ccfd7d73/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vilyever/AndroidSocketClient/a93613154993bcd9f7dbeb1e9ea19cc5ccfd7d73/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidSocketClient 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | maven { url "https://jitpack.io" } 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Fri Dec 11 13:43:13 CST 2015 16 | systemProp.http.proxyHost=127.0.0.1 17 | systemProp.http.nonProxyHosts=192.168.* 18 | systemProp.http.proxyPort=1080 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vilyever/AndroidSocketClient/a93613154993bcd9f7dbeb1e9ea19cc5ccfd7d73/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 18 13:33:11 CST 2016 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':socketclient' 2 | -------------------------------------------------------------------------------- /socketclient/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /socketclient/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'build_library_ext.gradle' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | compile fileTree(dir: 'libs', include: ['*.jar']) 22 | compile 'com.android.support:appcompat-v7:23.1.1' 23 | 24 | compile 'com.github.vilyever:AndroidResource:1.2.0' 25 | } -------------------------------------------------------------------------------- /socketclient/build_library_ext.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | buildVersionName = { 3 | "3.0.3" 4 | } 5 | 6 | /** 7 | * Builds an Android version code from the version of the project. 8 | * This is designed to handle the -SNAPSHOT and -RC format. 9 | * 10 | * I.e. during development the version ends with -SNAPSHOT. As the code stabilizes and release nears 11 | * one or many Release Candidates are tagged. These all end with "-RC1", "-RC2" etc. 12 | * And the final release is without any suffix. 13 | * @return 14 | */ 15 | buildVersionCode = { 16 | //The rules is as follows: 17 | //-SNAPSHOT counts as 0 18 | //-RC* counts as the RC number, i.e. 1 to 98 19 | //final release counts as 99. 20 | //Thus you can only have 98 Release Candidates, which ought to be enough for everyone 21 | 22 | def candidate = "0" 23 | def (major, minor, patch) = buildVersionName().toLowerCase().replaceAll('-', '').tokenize('.') 24 | if (patch.endsWith("snapshot")) { 25 | candidate = "0" 26 | patch = patch.replaceAll("[^0-9]","") 27 | } else { 28 | def rc 29 | (patch, rc) = patch.tokenize("rc") 30 | if (rc) { 31 | candidate = rc 32 | } 33 | } 34 | 35 | (major, minor, patch, candidate) = [major, minor, patch, candidate].collect{it.toInteger()} 36 | 37 | (major * 1000 * 1000 * 1000) + (minor * 1000 * 1000) + (patch * 1000) + candidate; 38 | } 39 | } 40 | 41 | android { 42 | defaultConfig { 43 | versionCode buildVersionCode() 44 | versionName buildVersionName() 45 | } 46 | } 47 | 48 | apply plugin: 'com.github.dcendents.android-maven' 49 | 50 | group = 'com.github.vilyever' 51 | version = buildVersionName() 52 | 53 | 54 | // build a jar with source files 55 | task sourceJar(type: Jar) { 56 | from android.sourceSets.main.java.srcDirs 57 | classifier = 'sources' 58 | } 59 | 60 | artifacts { 61 | archives sourceJar 62 | } 63 | 64 | configurations { 65 | published 66 | } 67 | 68 | configure(install.repositories.mavenInstaller) { 69 | pom.project { 70 | licenses { 71 | license { 72 | name 'The Apache Software License, Version 2.0' 73 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 74 | distribution 'repo' 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /socketclient/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Development\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /socketclient/socketclient.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /socketclient/src/androidTest/java/com/vilyever/socketclient/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /socketclient/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketClientAddress.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import com.vilyever.socketclient.util.StringValidation; 4 | 5 | import java.net.InetSocketAddress; 6 | 7 | /** 8 | * SocketClientAddress 9 | * Created by vilyever on 2016/5/31. 10 | * Feature: 11 | */ 12 | public class SocketClientAddress { 13 | final SocketClientAddress self = this; 14 | 15 | public static final int DefaultConnectionTimeout = 1000 * 15; 16 | 17 | /* Constructors */ 18 | public SocketClientAddress() { 19 | this(null, null); 20 | } 21 | 22 | 23 | public SocketClientAddress(String remoteIP, int remotePort) { 24 | this(remoteIP, "" + remotePort); 25 | } 26 | 27 | public SocketClientAddress(String remoteIP, int remotePort, int connectionTimeout) { 28 | this(remoteIP, "" + remotePort, connectionTimeout); 29 | } 30 | 31 | public SocketClientAddress(String remoteIP, String remotePort) { 32 | this(remoteIP, remotePort, DefaultConnectionTimeout); 33 | } 34 | 35 | public SocketClientAddress(String remoteIP, String remotePort, int connectionTimeout) { 36 | this.remoteIP = remoteIP; 37 | this.remotePort = remotePort; 38 | this.connectionTimeout = connectionTimeout; 39 | } 40 | 41 | public SocketClientAddress copy() { 42 | SocketClientAddress address = new SocketClientAddress(getRemoteIP(), getRemotePort(), getConnectionTimeout()); 43 | address.setOriginal(this); 44 | return address; 45 | } 46 | 47 | /* Public Methods */ 48 | public void checkValidation() { 49 | if (!StringValidation.validateRegex(getRemoteIP(), StringValidation.RegexIP)) { 50 | throw new IllegalArgumentException("we need a correct remote IP to connect. Current is " + getRemoteIP()); 51 | } 52 | 53 | if (!StringValidation.validateRegex(getRemotePort(), StringValidation.RegexPort)) { 54 | throw new IllegalArgumentException("we need a correct remote port to connect. Current is " + getRemotePort()); 55 | } 56 | 57 | if (getConnectionTimeout() < 0) { 58 | throw new IllegalArgumentException("we need connectionTimeout > 0. Current is " + getConnectionTimeout()); 59 | } 60 | } 61 | 62 | public SocketClientAddress setRemotePortWithInteger(int port) { 63 | setRemotePort("" + port); 64 | return this; 65 | } 66 | 67 | public int getRemotePortIntegerValue() { 68 | if (getRemotePort() == null) { 69 | return 0; 70 | } 71 | 72 | return Integer.valueOf(getRemotePort()); 73 | } 74 | 75 | public InetSocketAddress getInetSocketAddress() { 76 | return new InetSocketAddress(getRemoteIP(), getRemotePortIntegerValue()); 77 | } 78 | 79 | /* Properties */ 80 | private SocketClientAddress original; 81 | protected SocketClientAddress setOriginal(SocketClientAddress original) { 82 | this.original = original; 83 | return this; 84 | } 85 | public SocketClientAddress getOriginal() { 86 | if (this.original == null) { 87 | return this; 88 | } 89 | return this.original; 90 | } 91 | 92 | /** 93 | * 远程IP 94 | */ 95 | private String remoteIP; 96 | public SocketClientAddress setRemoteIP(String remoteIP) { 97 | this.remoteIP = remoteIP; 98 | return this; 99 | } 100 | public String getRemoteIP() { 101 | return this.remoteIP; 102 | } 103 | 104 | /** 105 | * 远程端口 106 | */ 107 | private String remotePort; 108 | public SocketClientAddress setRemotePort(String remotePort) { 109 | this.remotePort = remotePort; 110 | return this; 111 | } 112 | public String getRemotePort() { 113 | return this.remotePort; 114 | } 115 | 116 | /** 117 | * 连接超时时间 118 | */ 119 | private int connectionTimeout; 120 | public SocketClientAddress setConnectionTimeout(int connectionTimeout) { 121 | this.connectionTimeout = connectionTimeout; 122 | return this; 123 | } 124 | public int getConnectionTimeout() { 125 | return this.connectionTimeout; 126 | } 127 | 128 | 129 | /* Overrides */ 130 | 131 | 132 | /* Delegates */ 133 | 134 | 135 | /* Private Methods */ 136 | 137 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketClientDelegate.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.vilyever.socketclient.SocketClient; 6 | 7 | /** 8 | * SocketClientDelegate 9 | * Created by vilyever on 2016/5/30. 10 | * Feature: 11 | */ 12 | public interface SocketClientDelegate { 13 | void onConnected(SocketClient client); 14 | void onDisconnected(SocketClient client); 15 | void onResponse(SocketClient client, @NonNull SocketResponsePacket responsePacket); 16 | 17 | class SimpleSocketClientDelegate implements SocketClientDelegate { 18 | @Override 19 | public void onConnected(SocketClient client) { 20 | 21 | } 22 | 23 | @Override 24 | public void onDisconnected(SocketClient client) { 25 | 26 | } 27 | 28 | @Override 29 | public void onResponse(SocketClient client, @NonNull SocketResponsePacket responsePacket) { 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketClientReceivingDelegate.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import com.vilyever.socketclient.SocketClient; 4 | 5 | /** 6 | * SocketClientReceivingDelegate 7 | * Created by vilyever on 2016/5/30. 8 | * Feature: 9 | */ 10 | public interface SocketClientReceivingDelegate { 11 | void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet); 12 | void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet); 13 | void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet); 14 | void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength); 15 | 16 | class SimpleSocketClientReceiveDelegate implements SocketClientReceivingDelegate { 17 | @Override 18 | public void onReceivePacketBegin(SocketClient client, SocketResponsePacket packet) { 19 | 20 | } 21 | 22 | @Override 23 | public void onReceivePacketEnd(SocketClient client, SocketResponsePacket packet) { 24 | 25 | } 26 | 27 | @Override 28 | public void onReceivePacketCancel(SocketClient client, SocketResponsePacket packet) { 29 | 30 | } 31 | 32 | @Override 33 | public void onReceivingPacketInProgress(SocketClient client, SocketResponsePacket packet, float progress, int receivedLength) { 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketClientSendingDelegate.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import com.vilyever.socketclient.SocketClient; 4 | 5 | /** 6 | * SocketClientSendingDelegate 7 | * Created by vilyever on 2016/5/30. 8 | * Feature: 9 | */ 10 | public interface SocketClientSendingDelegate { 11 | void onSendPacketBegin(SocketClient client, SocketPacket packet); 12 | void onSendPacketEnd(SocketClient client, SocketPacket packet); 13 | void onSendPacketCancel(SocketClient client, SocketPacket packet); 14 | 15 | /** 16 | * 发送进度回调 17 | * @param client 18 | * @param packet 正在发送的packet 19 | * @param progress 0.0f-1.0f 20 | * @param sendedLength 已发送的字节数 21 | */ 22 | void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength); 23 | 24 | class SimpleSocketClientSendingDelegate implements SocketClientSendingDelegate { 25 | @Override 26 | public void onSendPacketBegin(SocketClient client, SocketPacket packet) { 27 | 28 | } 29 | 30 | @Override 31 | public void onSendPacketEnd(SocketClient client, SocketPacket packet) { 32 | 33 | } 34 | 35 | @Override 36 | public void onSendPacketCancel(SocketClient client, SocketPacket packet) { 37 | 38 | } 39 | 40 | @Override 41 | public void onSendingPacketInProgress(SocketClient client, SocketPacket packet, float progress, int sendedLength) { 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketConfigure.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | /** 4 | * SocketConfigure 5 | * Created by vilyever on 2016/5/31. 6 | * Feature: 7 | */ 8 | public class SocketConfigure { 9 | final SocketConfigure self = this; 10 | 11 | 12 | /* Constructors */ 13 | 14 | 15 | /* Public Methods */ 16 | 17 | 18 | /* Properties */ 19 | private String charsetName; 20 | public SocketConfigure setCharsetName(String charsetName) { 21 | this.charsetName = charsetName; 22 | return this; 23 | } 24 | public String getCharsetName() { 25 | return this.charsetName; 26 | } 27 | 28 | private SocketClientAddress address; 29 | public SocketConfigure setAddress(SocketClientAddress address) { 30 | this.address = address.copy(); 31 | return this; 32 | } 33 | public SocketClientAddress getAddress() { 34 | return this.address; 35 | } 36 | 37 | private SocketPacketHelper socketPacketHelper; 38 | public SocketConfigure setSocketPacketHelper(SocketPacketHelper socketPacketHelper) { 39 | this.socketPacketHelper = socketPacketHelper.copy(); 40 | return this; 41 | } 42 | public SocketPacketHelper getSocketPacketHelper() { 43 | return this.socketPacketHelper; 44 | } 45 | 46 | private SocketHeartBeatHelper heartBeatHelper; 47 | public SocketConfigure setHeartBeatHelper(SocketHeartBeatHelper heartBeatHelper) { 48 | this.heartBeatHelper = heartBeatHelper.copy(); 49 | return this; 50 | } 51 | public SocketHeartBeatHelper getHeartBeatHelper() { 52 | return this.heartBeatHelper; 53 | } 54 | 55 | /* Overrides */ 56 | 57 | 58 | /* Delegates */ 59 | 60 | 61 | /* Private Methods */ 62 | 63 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketHeartBeatHelper.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * SocketHeartBeatHelper 7 | * Created by vilyever on 2016/5/19. 8 | * Feature: 9 | */ 10 | public class SocketHeartBeatHelper { 11 | final SocketHeartBeatHelper self = this; 12 | 13 | /* Constructors */ 14 | public SocketHeartBeatHelper() { 15 | } 16 | 17 | public SocketHeartBeatHelper copy() { 18 | SocketHeartBeatHelper helper = new SocketHeartBeatHelper(); 19 | helper.setOriginal(this); 20 | 21 | helper.setDefaultSendData(getDefaultSendData()); 22 | helper.setSendDataBuilder(getSendDataBuilder()); 23 | 24 | helper.setDefaultReceiveData(getDefaultReceiveData()); 25 | helper.setReceiveHeartBeatPacketChecker(getReceiveHeartBeatPacketChecker()); 26 | 27 | helper.setHeartBeatInterval(getHeartBeatInterval()); 28 | helper.setSendHeartBeatEnabled(isSendHeartBeatEnabled()); 29 | 30 | return helper; 31 | } 32 | 33 | /* Public Methods */ 34 | public byte[] getSendData() { 35 | if (getSendDataBuilder() != null) { 36 | return getSendDataBuilder().obtainSendHeartBeatData(getOriginal()); 37 | } 38 | 39 | return getDefaultSendData(); 40 | } 41 | 42 | public boolean isReceiveHeartBeatPacket(SocketResponsePacket packet) { 43 | if (getReceiveHeartBeatPacketChecker() != null) { 44 | return getReceiveHeartBeatPacketChecker().isReceiveHeartBeatPacket(getOriginal(), packet); 45 | } 46 | 47 | if (getDefaultReceiveData() != null) { 48 | return packet.isDataEqual(getDefaultReceiveData()); 49 | } 50 | 51 | return false; 52 | } 53 | 54 | 55 | /* Properties */ 56 | private SocketHeartBeatHelper original; 57 | protected SocketHeartBeatHelper setOriginal(SocketHeartBeatHelper original) { 58 | this.original = original; 59 | return this; 60 | } 61 | public SocketHeartBeatHelper getOriginal() { 62 | if (this.original == null) { 63 | return this; 64 | } 65 | return this.original; 66 | } 67 | 68 | /** 69 | * 发送心跳包的数据 70 | */ 71 | private byte[] defaultSendData; 72 | public SocketHeartBeatHelper setDefaultSendData(byte[] defaultSendData) { 73 | if (defaultSendData != null) { 74 | this.defaultSendData = Arrays.copyOf(defaultSendData, defaultSendData.length); 75 | } 76 | else { 77 | this.defaultSendData = null; 78 | } 79 | return this; 80 | } 81 | public byte[] getDefaultSendData() { 82 | return this.defaultSendData; 83 | } 84 | 85 | private SendDataBuilder sendDataBuilder; 86 | public SocketHeartBeatHelper setSendDataBuilder(SendDataBuilder sendDataBuilder) { 87 | this.sendDataBuilder = sendDataBuilder; 88 | return this; 89 | } 90 | public SendDataBuilder getSendDataBuilder() { 91 | return this.sendDataBuilder; 92 | } 93 | public interface SendDataBuilder { 94 | byte[] obtainSendHeartBeatData(SocketHeartBeatHelper helper); 95 | } 96 | 97 | /** 98 | * 接收心跳包的数据,用于过滤远程心跳包 99 | */ 100 | private byte[] defaultReceiveData; 101 | public SocketHeartBeatHelper setDefaultReceiveData(byte[] defaultReceiveData) { 102 | if (defaultReceiveData != null) { 103 | this.defaultReceiveData = Arrays.copyOf(defaultReceiveData, defaultReceiveData.length); 104 | } 105 | else { 106 | this.defaultReceiveData = null; 107 | } 108 | return this; 109 | } 110 | public byte[] getDefaultReceiveData() { 111 | return this.defaultReceiveData; 112 | } 113 | 114 | private ReceiveHeartBeatPacketChecker receiveHeartBeatPacketChecker; 115 | public SocketHeartBeatHelper setReceiveHeartBeatPacketChecker(ReceiveHeartBeatPacketChecker receiveHeartBeatPacketChecker) { 116 | this.receiveHeartBeatPacketChecker = receiveHeartBeatPacketChecker; 117 | return this; 118 | } 119 | public ReceiveHeartBeatPacketChecker getReceiveHeartBeatPacketChecker() { 120 | return this.receiveHeartBeatPacketChecker; 121 | } 122 | public interface ReceiveHeartBeatPacketChecker { 123 | boolean isReceiveHeartBeatPacket(SocketHeartBeatHelper helper, SocketResponsePacket packet); 124 | } 125 | 126 | /** 127 | * 心跳包发送间隔 128 | */ 129 | private long heartBeatInterval; 130 | public SocketHeartBeatHelper setHeartBeatInterval(long heartBeatInterval) { 131 | this.heartBeatInterval = heartBeatInterval; 132 | return this; 133 | } 134 | public long getHeartBeatInterval() { 135 | return this.heartBeatInterval; 136 | } 137 | 138 | /** 139 | * 是否发送心跳包 140 | * heartBeatInterval不大于0,返回false 141 | */ 142 | private boolean sendHeartBeatEnabled; 143 | public SocketHeartBeatHelper setSendHeartBeatEnabled(boolean sendHeartBeatEnabled) { 144 | this.sendHeartBeatEnabled = sendHeartBeatEnabled; 145 | return this; 146 | } 147 | public boolean isSendHeartBeatEnabled() { 148 | if ((getDefaultSendData() == null 149 | && getSendDataBuilder() == null) 150 | || getHeartBeatInterval() <= 0) { 151 | return false; 152 | } 153 | return this.sendHeartBeatEnabled; 154 | } 155 | 156 | /* Overrides */ 157 | 158 | 159 | /* Delegates */ 160 | 161 | 162 | /* Private Methods */ 163 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketInputReader.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.Reader; 6 | import java.util.ArrayList; 7 | import java.util.Iterator; 8 | 9 | /** 10 | * SocketInputReader 11 | * AndroidSocketClient 12 | * Created by vilyever on 2016/4/11. 13 | * Feature: 14 | */ 15 | public class SocketInputReader extends Reader { 16 | final SocketInputReader self = this; 17 | 18 | private InputStream inputStream; 19 | 20 | /* Constructors */ 21 | public SocketInputReader(InputStream inputStream) { 22 | super(inputStream); 23 | this.inputStream = inputStream; 24 | } 25 | 26 | /* Public Methods */ 27 | 28 | 29 | /* Properties */ 30 | 31 | /* Overrides */ 32 | @Override 33 | public void close() throws IOException { 34 | synchronized (lock) { 35 | if (this.inputStream != null) { 36 | this.inputStream.close(); 37 | this.inputStream = null; 38 | } 39 | } 40 | } 41 | 42 | @Override 43 | public int read(char[] buffer, int offset, int count) throws IOException { 44 | throw new IOException("read() is not support for SocketInputReader, try readBytes()."); 45 | } 46 | 47 | public byte[] readToLength(int length) throws IOException { 48 | if (length <= 0) { 49 | return null; 50 | } 51 | 52 | synchronized (lock) { 53 | if (!__i__isOpen()) { 54 | throw new IOException("InputStreamReader is closed"); 55 | } 56 | 57 | try { 58 | byte[] buffer = new byte[length]; 59 | int index = 0; 60 | int readCount = 0; 61 | 62 | do { 63 | readCount = this.inputStream.read(buffer, index, length - index); 64 | index += readCount; 65 | } while (readCount != -1 && index < length); 66 | 67 | if (index != length) { 68 | return null; 69 | } 70 | 71 | return buffer; 72 | } 73 | catch (IOException e) { 74 | return null; 75 | } 76 | } 77 | } 78 | 79 | public byte[] readToData(byte[] data, boolean includeData) throws IOException { 80 | if (data == null 81 | || data.length <= 0) { 82 | return null; 83 | } 84 | 85 | synchronized (lock) { 86 | if (!__i__isOpen()) { 87 | throw new IOException("InputStreamReader is closed"); 88 | } 89 | 90 | try { 91 | ArrayList list = new ArrayList<>(); 92 | int c; 93 | 94 | int matchIndex = 0; 95 | 96 | while (-1 != (c = this.inputStream.read())) { 97 | list.add((byte) c); 98 | if (c == (0xff & data[matchIndex])) { 99 | matchIndex++; 100 | } 101 | else { 102 | matchIndex = 0; 103 | } 104 | 105 | if (matchIndex == data.length) { 106 | break; 107 | } 108 | } 109 | 110 | if (list.size() == 0) { 111 | return null; 112 | } 113 | 114 | int resultLength = list.size() - (includeData ? 0 : data.length); 115 | byte[] result = new byte[resultLength]; 116 | Iterator iterator = list.iterator(); 117 | for (int i = 0; i < resultLength; i++) { 118 | result[i] = iterator.next(); 119 | } 120 | 121 | return result; 122 | } 123 | catch (IOException e) { 124 | return null; 125 | } 126 | } 127 | } 128 | 129 | @Override 130 | public boolean ready() throws IOException { 131 | synchronized (lock) { 132 | if (this.inputStream == null) { 133 | throw new IOException("InputStreamReader is closed"); 134 | } 135 | try { 136 | return this.inputStream.available() > 0; 137 | } catch (IOException e) { 138 | return false; 139 | } 140 | } 141 | } 142 | 143 | /* Delegates */ 144 | 145 | 146 | /* Private Methods */ 147 | public static void __i__checkOffsetAndCount(int arrayLength, int offset, int count) { 148 | if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { 149 | throw new ArrayIndexOutOfBoundsException("arrayLength=" + arrayLength + "; offset=" + offset 150 | + "; count=" + count); 151 | } 152 | } 153 | 154 | private boolean __i__isOpen() { 155 | return this.inputStream != null; 156 | } 157 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketPacket.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import com.vilyever.socketclient.util.CharsetUtil; 4 | 5 | import java.util.Arrays; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | /** 9 | * SocketPacket 10 | * AndroidSocketClient 11 | * Created by vilyever on 2015/9/15. 12 | * Feature: 13 | */ 14 | public class SocketPacket { 15 | private final SocketPacket self = this; 16 | 17 | private static final AtomicInteger IDAtomic = new AtomicInteger(); 18 | 19 | /* Constructors */ 20 | public SocketPacket(byte[] data) { 21 | this(data, false); 22 | } 23 | 24 | public SocketPacket(byte[] data, boolean isHeartBeat) { 25 | this.ID = IDAtomic.getAndIncrement(); 26 | this.data = Arrays.copyOf(data, data.length); 27 | this.heartBeat = isHeartBeat; 28 | } 29 | 30 | public SocketPacket(String message) { 31 | this.ID = IDAtomic.getAndIncrement(); 32 | this.message = message; 33 | } 34 | 35 | /* Public Methods */ 36 | public void buildDataWithCharsetName(String charsetName) { 37 | if (getMessage() != null) { 38 | this.data = CharsetUtil.stringToData(getMessage(), charsetName); 39 | } 40 | } 41 | 42 | /* Properties */ 43 | /** 44 | * ID, unique 45 | */ 46 | private final int ID; 47 | public int getID() { 48 | return this.ID; 49 | } 50 | 51 | /** 52 | * bytes data 53 | */ 54 | private byte[] data; 55 | public byte[] getData() { 56 | return this.data; 57 | } 58 | 59 | /** 60 | * string data 61 | */ 62 | private String message; 63 | public String getMessage() { 64 | return this.message; 65 | } 66 | 67 | private boolean heartBeat; 68 | public boolean isHeartBeat() { 69 | return this.heartBeat; 70 | } 71 | 72 | private byte[] headerData; 73 | public SocketPacket setHeaderData(byte[] headerData) { 74 | this.headerData = headerData; 75 | return this; 76 | } 77 | public byte[] getHeaderData() { 78 | return this.headerData; 79 | } 80 | 81 | private byte[] packetLengthData; 82 | public SocketPacket setPacketLengthData(byte[] packetLengthData) { 83 | this.packetLengthData = packetLengthData; 84 | return this; 85 | } 86 | public byte[] getPacketLengthData() { 87 | return this.packetLengthData; 88 | } 89 | 90 | private byte[] trailerData; 91 | public SocketPacket setTrailerData(byte[] trailerData) { 92 | this.trailerData = trailerData; 93 | return this; 94 | } 95 | public byte[] getTrailerData() { 96 | return this.trailerData; 97 | } 98 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketPacketHelper.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * SocketPacketHelper 7 | * Created by vilyever on 2016/5/19. 8 | * Feature: 9 | */ 10 | public class SocketPacketHelper { 11 | final SocketPacketHelper self = this; 12 | 13 | 14 | /* Constructors */ 15 | public SocketPacketHelper() { 16 | } 17 | 18 | public SocketPacketHelper copy() { 19 | SocketPacketHelper helper = new SocketPacketHelper(); 20 | helper.setOriginal(this); 21 | 22 | helper.setSendHeaderData(getSendHeaderData()); 23 | helper.setSendPacketLengthDataConvertor(getSendPacketLengthDataConvertor()); 24 | helper.setSendTrailerData(getSendTrailerData()); 25 | helper.setSendSegmentLength(getSendSegmentLength()); 26 | helper.setSendSegmentEnabled(isSendSegmentEnabled()); 27 | helper.setSendTimeout(getSendTimeout()); 28 | helper.setSendTimeoutEnabled(isSendTimeoutEnabled()); 29 | 30 | helper.setReadStrategy(getReadStrategy()); 31 | 32 | helper.setReceiveHeaderData(getReceiveHeaderData()); 33 | helper.setReceivePacketLengthDataLength(getReceivePacketLengthDataLength()); 34 | helper.setReceivePacketDataLengthConvertor(getReceivePacketDataLengthConvertor()); 35 | helper.setReceiveTrailerData(getReceiveTrailerData()); 36 | helper.setReceiveSegmentLength(getReceiveSegmentLength()); 37 | helper.setReceiveSegmentEnabled(isReceiveSegmentEnabled()); 38 | helper.setReceiveTimeout(getReceiveTimeout()); 39 | helper.setReceiveTimeoutEnabled(isReceiveTimeoutEnabled()); 40 | 41 | return helper; 42 | } 43 | 44 | /* Public Methods */ 45 | public void checkValidation() { 46 | switch (getReadStrategy()) { 47 | case Manually: 48 | return; 49 | case AutoReadToTrailer: 50 | if (getReceiveTrailerData() == null 51 | || getReceiveTrailerData().length <= 0) { 52 | throw new IllegalArgumentException("we need ReceiveTrailerData for AutoReadToTrailer"); 53 | } 54 | return; 55 | case AutoReadByLength: 56 | if (getReceivePacketLengthDataLength() <= 0 57 | || getReceivePacketDataLengthConvertor() == null) { 58 | throw new IllegalArgumentException("we need ReceivePacketLengthDataLength and ReceivePacketDataLengthConvertor for AutoReadByLength"); 59 | } 60 | return; 61 | } 62 | 63 | throw new IllegalArgumentException("we need a correct ReadStrategy"); 64 | } 65 | 66 | public byte[] getSendPacketLengthData(int packetLength) { 67 | if (getSendPacketLengthDataConvertor() != null) { 68 | return getSendPacketLengthDataConvertor().obtainSendPacketLengthDataForPacketLength(getOriginal(), packetLength); 69 | } 70 | 71 | return null; 72 | } 73 | 74 | public int getReceivePacketDataLength(byte[] packetLengthData) { 75 | if (getReadStrategy() == ReadStrategy.AutoReadByLength) { 76 | if (getReceivePacketDataLengthConvertor() != null) { 77 | return getReceivePacketDataLengthConvertor().obtainReceivePacketDataLength(getOriginal(), packetLengthData); 78 | } 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | /* Properties */ 85 | private SocketPacketHelper original; 86 | protected SocketPacketHelper setOriginal(SocketPacketHelper original) { 87 | this.original = original; 88 | return this; 89 | } 90 | public SocketPacketHelper getOriginal() { 91 | if (this.original == null) { 92 | return this; 93 | } 94 | return this.original; 95 | } 96 | 97 | /** 98 | * 发送消息时自动添加的包头 99 | */ 100 | private byte[] sendHeaderData; 101 | public SocketPacketHelper setSendHeaderData(byte[] sendHeaderData) { 102 | if (sendHeaderData != null) { 103 | this.sendHeaderData = Arrays.copyOf(sendHeaderData, sendHeaderData.length); 104 | } 105 | else { 106 | this.sendHeaderData = null; 107 | } 108 | return this; 109 | } 110 | public byte[] getSendHeaderData() { 111 | return this.sendHeaderData; 112 | } 113 | 114 | private SendPacketLengthDataConvertor sendPacketLengthDataConvertor; 115 | public SocketPacketHelper setSendPacketLengthDataConvertor(SendPacketLengthDataConvertor sendPacketLengthDataConvertor) { 116 | this.sendPacketLengthDataConvertor = sendPacketLengthDataConvertor; 117 | return this; 118 | } 119 | public SendPacketLengthDataConvertor getSendPacketLengthDataConvertor() { 120 | return this.sendPacketLengthDataConvertor; 121 | } 122 | public interface SendPacketLengthDataConvertor { 123 | byte[] obtainSendPacketLengthDataForPacketLength(SocketPacketHelper helper, int packetLength); 124 | } 125 | 126 | /** 127 | * 发送消息时自动添加的包尾 128 | */ 129 | private byte[] sendTrailerData; 130 | public SocketPacketHelper setSendTrailerData(byte[] sendTrailerData) { 131 | if (sendTrailerData != null) { 132 | this.sendTrailerData = Arrays.copyOf(sendTrailerData, sendTrailerData.length); 133 | } 134 | else { 135 | this.sendTrailerData = null; 136 | } 137 | return this; 138 | } 139 | public byte[] getSendTrailerData() { 140 | return this.sendTrailerData; 141 | } 142 | 143 | /** 144 | * 发送消息时分段发送的每段大小 145 | * 分段发送可以回调进度 146 | * 此数值表示每次发送byte的长度 147 | * 不大于0表示不分段 148 | */ 149 | private int sendSegmentLength; 150 | public SocketPacketHelper setSendSegmentLength(int sendSegmentLength) { 151 | this.sendSegmentLength = sendSegmentLength; 152 | return this; 153 | } 154 | public int getSendSegmentLength() { 155 | return this.sendSegmentLength; 156 | } 157 | 158 | /** 159 | * 若sendSegmentLength不大于0,返回false 160 | */ 161 | private boolean sendSegmentEnabled; 162 | public SocketPacketHelper setSendSegmentEnabled(boolean sendSegmentEnabled) { 163 | this.sendSegmentEnabled = sendSegmentEnabled; 164 | return this; 165 | } 166 | public boolean isSendSegmentEnabled() { 167 | if (getSendSegmentLength() <= 0) { 168 | return false; 169 | } 170 | return this.sendSegmentEnabled; 171 | } 172 | 173 | /** 174 | * 发送超时时长,超过时长无法写出自动断开连接 175 | * 仅在每个发送包开始发送时计时,结束后重置计时 176 | */ 177 | private long sendTimeout; 178 | public SocketPacketHelper setSendTimeout(long sendTimeout) { 179 | this.sendTimeout = sendTimeout; 180 | return this; 181 | } 182 | public long getSendTimeout() { 183 | return this.sendTimeout; 184 | } 185 | 186 | private boolean sendTimeoutEnabled; 187 | public SocketPacketHelper setSendTimeoutEnabled(boolean sendTimeoutEnabled) { 188 | this.sendTimeoutEnabled = sendTimeoutEnabled; 189 | return this; 190 | } 191 | public boolean isSendTimeoutEnabled() { 192 | return this.sendTimeoutEnabled; 193 | } 194 | 195 | private ReadStrategy readStrategy = ReadStrategy.Manually; 196 | public SocketPacketHelper setReadStrategy(ReadStrategy readStrategy) { 197 | this.readStrategy = readStrategy; 198 | return this; 199 | } 200 | public ReadStrategy getReadStrategy() { 201 | return this.readStrategy; 202 | } 203 | public enum ReadStrategy { 204 | /** 205 | * 手动读取 206 | * 手动调用{@link com.vilyever.socketclient.SocketClient#readDataToData(byte[])}或{@link com.vilyever.socketclient.SocketClient#readDataToLength(int)}读取 207 | */ 208 | Manually, 209 | /** 210 | * 自动读取到包尾 211 | * 需设置包尾相关信息 212 | * 自动读取信息直到读取到与包尾相同的数据后,回调接收包 213 | */ 214 | AutoReadToTrailer, 215 | /** 216 | * 自动按长度读取 217 | * 需设置长度相关信息 218 | * 自动读取包长度信息,转换成包长度后读取该长度字节后,回调接收包 219 | */ 220 | AutoReadByLength, 221 | } 222 | 223 | /** 224 | * 接收消息时每一条消息的头部信息 225 | * 若不为null,每一条接收消息都必须带有此头部信息,否则将无法读取 226 | */ 227 | private byte[] receiveHeaderData; 228 | public SocketPacketHelper setReceiveHeaderData(byte[] receiveHeaderData) { 229 | if (receiveHeaderData != null) { 230 | this.receiveHeaderData = Arrays.copyOf(receiveHeaderData, receiveHeaderData.length); 231 | } 232 | else { 233 | this.receiveHeaderData = null; 234 | } 235 | return this; 236 | } 237 | public byte[] getReceiveHeaderData() { 238 | return this.receiveHeaderData; 239 | } 240 | 241 | /** 242 | * 接收时,包长度data的固定字节数 243 | */ 244 | private int receivePacketLengthDataLength; 245 | public SocketPacketHelper setReceivePacketLengthDataLength(int receivePacketLengthDataLength) { 246 | this.receivePacketLengthDataLength = receivePacketLengthDataLength; 247 | return this; 248 | } 249 | public int getReceivePacketLengthDataLength() { 250 | return this.receivePacketLengthDataLength; 251 | } 252 | 253 | private ReceivePacketDataLengthConvertor receivePacketDataLengthConvertor; 254 | public SocketPacketHelper setReceivePacketDataLengthConvertor(ReceivePacketDataLengthConvertor receivePacketDataLengthConvertor) { 255 | this.receivePacketDataLengthConvertor = receivePacketDataLengthConvertor; 256 | return this; 257 | } 258 | public ReceivePacketDataLengthConvertor getReceivePacketDataLengthConvertor() { 259 | return this.receivePacketDataLengthConvertor; 260 | } 261 | public interface ReceivePacketDataLengthConvertor { 262 | int obtainReceivePacketDataLength(SocketPacketHelper helper, byte[] packetLengthData); 263 | } 264 | 265 | /** 266 | * 接收消息时每一条消息的尾部信息 267 | * 若不为null,每一条接收消息都必须带有此尾部信息,否则将与下一次输入流合并 268 | */ 269 | private byte[] receiveTrailerData; 270 | public SocketPacketHelper setReceiveTrailerData(byte[] receiveTrailerData) { 271 | if (receiveTrailerData != null) { 272 | this.receiveTrailerData = Arrays.copyOf(receiveTrailerData, receiveTrailerData.length); 273 | } 274 | else { 275 | this.receiveTrailerData = null; 276 | } 277 | return this; 278 | } 279 | public byte[] getReceiveTrailerData() { 280 | return this.receiveTrailerData; 281 | } 282 | 283 | /** 284 | * 分段接收消息,每段长度,仅在按长度读取时有效 285 | * 若设置大于0时,receiveSegmentEnabled自动变更为true,反之亦然 286 | * 设置后可手动变更receiveSegmentEnabled 287 | */ 288 | private int receiveSegmentLength; 289 | public SocketPacketHelper setReceiveSegmentLength(int receiveSegmentLength) { 290 | this.receiveSegmentLength = receiveSegmentLength; 291 | return this; 292 | } 293 | public int getReceiveSegmentLength() { 294 | return this.receiveSegmentLength; 295 | } 296 | 297 | /** 298 | * 若receiveSegmentLength不大于0,返回false 299 | */ 300 | private boolean receiveSegmentEnabled; 301 | public SocketPacketHelper setReceiveSegmentEnabled(boolean receiveSegmentEnabled) { 302 | this.receiveSegmentEnabled = receiveSegmentEnabled; 303 | return this; 304 | } 305 | public boolean isReceiveSegmentEnabled() { 306 | if (getReceiveSegmentLength() <= 0) { 307 | return false; 308 | } 309 | return this.receiveSegmentEnabled; 310 | } 311 | 312 | /** 313 | * 读取超时时长,超过时长没有读取到任何消息自动断开连接 314 | */ 315 | private long receiveTimeout; 316 | public SocketPacketHelper setReceiveTimeout(long receiveTimeout) { 317 | this.receiveTimeout = receiveTimeout; 318 | return this; 319 | } 320 | public long getReceiveTimeout() { 321 | return this.receiveTimeout; 322 | } 323 | 324 | private boolean receiveTimeoutEnabled; 325 | public SocketPacketHelper setReceiveTimeoutEnabled(boolean receiveTimeoutEnabled) { 326 | this.receiveTimeoutEnabled = receiveTimeoutEnabled; 327 | return this; 328 | } 329 | public boolean isReceiveTimeoutEnabled() { 330 | return this.receiveTimeoutEnabled; 331 | } 332 | 333 | /* Overrides */ 334 | 335 | 336 | /* Delegates */ 337 | 338 | 339 | /* Private Methods */ 340 | 341 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/helper/SocketResponsePacket.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.helper; 2 | 3 | import com.vilyever.socketclient.util.CharsetUtil; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * SocketResponsePacket 9 | * AndroidSocketClient 10 | * Created by vilyever on 2016/4/11. 11 | * Feature: 12 | */ 13 | public class SocketResponsePacket { 14 | final SocketResponsePacket self = this; 15 | 16 | 17 | /* Constructors */ 18 | public SocketResponsePacket() { 19 | } 20 | 21 | 22 | /* Public Methods */ 23 | public boolean isDataEqual(byte[] data) { 24 | return Arrays.equals(getData(), data); 25 | } 26 | 27 | public void buildStringWithCharsetName(String charsetName) { 28 | if (getData() != null) { 29 | setMessage(CharsetUtil.dataToString(getData(), charsetName)); 30 | } 31 | } 32 | 33 | /* Properties */ 34 | private byte[] data; 35 | public SocketResponsePacket setData(byte[] data) { 36 | this.data = data; 37 | return this; 38 | } 39 | public byte[] getData() { 40 | return this.data; 41 | } 42 | 43 | private String message; 44 | public SocketResponsePacket setMessage(String message) { 45 | this.message = message; 46 | return this; 47 | } 48 | public String getMessage() { 49 | return this.message; 50 | } 51 | 52 | private byte[] headerData; 53 | public SocketResponsePacket setHeaderData(byte[] headerData) { 54 | this.headerData = headerData; 55 | return this; 56 | } 57 | public byte[] getHeaderData() { 58 | return this.headerData; 59 | } 60 | 61 | private byte[] packetLengthData; 62 | public SocketResponsePacket setPacketLengthData(byte[] packetLengthData) { 63 | this.packetLengthData = packetLengthData; 64 | return this; 65 | } 66 | public byte[] getPacketLengthData() { 67 | return this.packetLengthData; 68 | } 69 | 70 | private byte[] trailerData; 71 | public SocketResponsePacket setTrailerData(byte[] trailerData) { 72 | this.trailerData = trailerData; 73 | return this; 74 | } 75 | public byte[] getTrailerData() { 76 | return this.trailerData; 77 | } 78 | 79 | private boolean heartBeat; 80 | public SocketResponsePacket setHeartBeat(boolean heartBeat) { 81 | this.heartBeat = heartBeat; 82 | return this; 83 | } 84 | public boolean isHeartBeat() { 85 | return this.heartBeat; 86 | } 87 | 88 | 89 | /* Overrides */ 90 | 91 | 92 | /* Delegates */ 93 | 94 | 95 | /* Private Methods */ 96 | 97 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/server/SocketServer.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.server; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.WorkerThread; 8 | 9 | import com.vilyever.socketclient.SocketClient; 10 | import com.vilyever.socketclient.helper.SocketClientAddress; 11 | import com.vilyever.socketclient.helper.SocketClientDelegate; 12 | import com.vilyever.socketclient.helper.SocketConfigure; 13 | import com.vilyever.socketclient.helper.SocketHeartBeatHelper; 14 | import com.vilyever.socketclient.helper.SocketPacketHelper; 15 | import com.vilyever.socketclient.helper.SocketResponsePacket; 16 | import com.vilyever.socketclient.util.IPUtil; 17 | import com.vilyever.socketclient.util.StringValidation; 18 | 19 | import java.io.IOException; 20 | import java.lang.ref.WeakReference; 21 | import java.net.ServerSocket; 22 | import java.net.Socket; 23 | import java.util.ArrayList; 24 | 25 | /** 26 | * SocketServer 27 | * AndroidSocketClient 28 | * Created by vilyever on 2016/3/18. 29 | * Feature: 30 | */ 31 | public class SocketServer implements SocketClientDelegate { 32 | final SocketServer self = this; 33 | 34 | public static final int NoPort = -1; 35 | public static final int MaxPort = 65535; 36 | 37 | 38 | /* Constructors */ 39 | public SocketServer() { 40 | } 41 | 42 | /* Public Methods */ 43 | public boolean beginListen(int port) { 44 | if (isListening()) { 45 | return false; 46 | } 47 | 48 | setPort(port); 49 | 50 | getSocketConfigure().setCharsetName(getCharsetName()).setAddress(new SocketClientAddress(IPUtil.getLocalIPAddress(true), "" + port)).setHeartBeatHelper(getHeartBeatHelper()).setSocketPacketHelper(getSocketPacketHelper()); 51 | 52 | if (getRunningServerSocket() == null) { 53 | return false; 54 | } 55 | 56 | 57 | setListening(true); 58 | __i__onSocketServerBeginListen(); 59 | 60 | return true; 61 | } 62 | 63 | public int beginListenFromPort(int port) { 64 | if (isListening()) { 65 | return NoPort; 66 | } 67 | 68 | while (port <= MaxPort) { 69 | if (beginListen(port)) { 70 | return port; 71 | } 72 | port++; 73 | } 74 | 75 | return NoPort; 76 | } 77 | 78 | public void stopListen() { 79 | if (isListening()) { 80 | getListenThread().interrupt(); 81 | try { 82 | getRunningServerSocket().close(); 83 | } 84 | catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | } 89 | 90 | public String getIP() { 91 | return getRunningServerSocket().getLocalSocketAddress().toString().substring(1); 92 | } 93 | 94 | public SocketClientAddress getListeningAddress() { 95 | return getSocketConfigure().getAddress(); 96 | } 97 | 98 | /** 99 | * 注册监听回调 100 | * @param delegate 回调接收者 101 | */ 102 | public SocketServer registerSocketServerDelegate(SocketServerDelegate delegate) { 103 | if (!getSocketServerDelegates().contains(delegate)) { 104 | getSocketServerDelegates().add(delegate); 105 | } 106 | return this; 107 | } 108 | 109 | /** 110 | * 取消注册监听回调 111 | * @param delegate 回调接收者 112 | */ 113 | public SocketServer removeSocketServerDelegate(SocketServerDelegate delegate) { 114 | getSocketServerDelegates().remove(delegate); 115 | return this; 116 | } 117 | 118 | /* Properties */ 119 | private ServerSocket runningServerSocket; 120 | protected SocketServer setRunningServerSocket(ServerSocket runningServerSocket) { 121 | this.runningServerSocket = runningServerSocket; 122 | return this; 123 | } 124 | protected ServerSocket getRunningServerSocket() { 125 | if (this.runningServerSocket == null) { 126 | try { 127 | this.runningServerSocket = new ServerSocket(getPort()); 128 | } 129 | catch (IOException e) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | return this.runningServerSocket; 134 | } 135 | 136 | private int port = NoPort; 137 | public int getPort() { 138 | return this.port; 139 | } 140 | protected SocketServer setPort(int port) { 141 | if (!StringValidation.validateRegex("" + port, StringValidation.RegexPort)) { 142 | throw new IllegalArgumentException("we need a correct remote port to listen"); 143 | } 144 | 145 | if (isListening()) { 146 | return this; 147 | } 148 | 149 | this.port = port; 150 | return this; 151 | } 152 | 153 | private boolean listening; 154 | protected SocketServer setListening(boolean listening) { 155 | this.listening = listening; 156 | return this; 157 | } 158 | public boolean isListening() { 159 | return this.listening; 160 | } 161 | 162 | private ListenThread listenThread; 163 | protected SocketServer setListenThread(ListenThread listenThread) { 164 | this.listenThread = listenThread; 165 | return this; 166 | } 167 | protected ListenThread getListenThread() { 168 | if (this.listenThread == null) { 169 | this.listenThread = new ListenThread(); 170 | } 171 | return this.listenThread; 172 | } 173 | 174 | /** 175 | * 统一配置默认的编码格式 176 | */ 177 | private String charsetName; 178 | public SocketServer setCharsetName(String charsetName) { 179 | this.charsetName = charsetName; 180 | return this; 181 | } 182 | public String getCharsetName() { 183 | return this.charsetName; 184 | } 185 | 186 | private SocketPacketHelper socketPacketHelper; 187 | public SocketServer setSocketPacketHelper(SocketPacketHelper socketPacketHelper) { 188 | this.socketPacketHelper = socketPacketHelper; 189 | return this; 190 | } 191 | public SocketPacketHelper getSocketPacketHelper() { 192 | if (this.socketPacketHelper == null) { 193 | this.socketPacketHelper = new SocketPacketHelper(); 194 | } 195 | return this.socketPacketHelper; 196 | } 197 | 198 | private SocketHeartBeatHelper heartBeatHelper; 199 | public SocketServer setHeartBeatHelper(SocketHeartBeatHelper heartBeatHelper) { 200 | this.heartBeatHelper = heartBeatHelper; 201 | return this; 202 | } 203 | public SocketHeartBeatHelper getHeartBeatHelper() { 204 | if (this.heartBeatHelper == null) { 205 | this.heartBeatHelper = new SocketHeartBeatHelper(); 206 | } 207 | return this.heartBeatHelper; 208 | } 209 | 210 | private SocketConfigure socketConfigure; 211 | protected SocketConfigure getSocketConfigure() { 212 | if (this.socketConfigure == null) { 213 | this.socketConfigure = new SocketConfigure(); 214 | } 215 | return this.socketConfigure; 216 | } 217 | 218 | private ArrayList runningSocketServerClients; 219 | protected ArrayList getRunningSocketServerClients() { 220 | if (this.runningSocketServerClients == null) { 221 | this.runningSocketServerClients = new ArrayList(); 222 | } 223 | return this.runningSocketServerClients; 224 | } 225 | 226 | private ArrayList socketServerDelegates; 227 | protected ArrayList getSocketServerDelegates() { 228 | if (this.socketServerDelegates == null) { 229 | this.socketServerDelegates = new ArrayList(); 230 | } 231 | return this.socketServerDelegates; 232 | } 233 | 234 | private UIHandler uiHandler; 235 | protected UIHandler getUiHandler() { 236 | if (this.uiHandler == null) { 237 | this.uiHandler = new UIHandler(this); 238 | } 239 | return this.uiHandler; 240 | } 241 | private static class UIHandler extends Handler { 242 | private WeakReference referenceSocketServer; 243 | 244 | public UIHandler(@NonNull SocketServer referenceSocketServer) { 245 | super(Looper.getMainLooper()); 246 | 247 | this.referenceSocketServer = new WeakReference(referenceSocketServer); 248 | } 249 | 250 | @Override 251 | public void handleMessage(Message msg) { 252 | super.handleMessage(msg); 253 | } 254 | } 255 | 256 | /* Overrides */ 257 | 258 | 259 | /* Delegates */ 260 | @Override 261 | public void onConnected(SocketClient client) { 262 | 263 | } 264 | 265 | @Override 266 | public void onDisconnected(SocketClient client) { 267 | getRunningSocketServerClients().remove(client); 268 | __i__onSocketServerClientDisconnected((SocketServerClient) client); 269 | } 270 | 271 | @Override 272 | public void onResponse(SocketClient client, @NonNull SocketResponsePacket responsePacket) { 273 | 274 | } 275 | 276 | 277 | /* Protected Methods */ 278 | @WorkerThread 279 | protected SocketServerClient internalGetSocketServerClient(Socket socket) { 280 | return new SocketServerClient(socket, getSocketConfigure()); 281 | } 282 | 283 | /* Private Methods */ 284 | private boolean __i__checkServerSocketAvailable() { 285 | return getRunningServerSocket() != null && !getRunningServerSocket().isClosed(); 286 | } 287 | 288 | private void __i__disconnectAllClients() { 289 | if (Looper.myLooper() != Looper.getMainLooper()) { 290 | getUiHandler().post(new Runnable() { 291 | @Override 292 | public void run() { 293 | self.__i__disconnectAllClients(); 294 | } 295 | }); 296 | return; 297 | } 298 | 299 | while (getRunningSocketServerClients().size() > 0) { 300 | SocketServerClient client = getRunningSocketServerClients().get(0); 301 | getRunningSocketServerClients().remove(client); 302 | client.disconnect(); 303 | } 304 | } 305 | 306 | private void __i__onSocketServerBeginListen() { 307 | if (Looper.myLooper() != Looper.getMainLooper()) { 308 | getUiHandler().post(new Runnable() { 309 | @Override 310 | public void run() { 311 | self.__i__onSocketServerBeginListen(); 312 | } 313 | }); 314 | return; 315 | } 316 | 317 | ArrayList copyList = 318 | (ArrayList) getSocketServerDelegates().clone(); 319 | int count = copyList.size(); 320 | for (int i = 0; i < count; ++i) { 321 | copyList.get(i).onServerBeginListen(this, getPort()); 322 | } 323 | 324 | getListenThread().start(); 325 | } 326 | 327 | private void __i__onSocketServerStopListen() { 328 | if (Looper.myLooper() != Looper.getMainLooper()) { 329 | getUiHandler().post(new Runnable() { 330 | @Override 331 | public void run() { 332 | self.__i__onSocketServerStopListen(); 333 | } 334 | }); 335 | return; 336 | } 337 | 338 | ArrayList copyList = 339 | (ArrayList) getSocketServerDelegates().clone(); 340 | int count = copyList.size(); 341 | for (int i = 0; i < count; ++i) { 342 | copyList.get(i).onServerStopListen(this, getPort()); 343 | } 344 | } 345 | 346 | private void __i__onSocketServerClientConnected(final SocketServerClient socketServerClient) { 347 | if (Looper.myLooper() != Looper.getMainLooper()) { 348 | getUiHandler().post(new Runnable() { 349 | @Override 350 | public void run() { 351 | self.__i__onSocketServerClientConnected(socketServerClient); 352 | } 353 | }); 354 | return; 355 | } 356 | 357 | ArrayList copyList = 358 | (ArrayList) getSocketServerDelegates().clone(); 359 | int count = copyList.size(); 360 | for (int i = 0; i < count; ++i) { 361 | copyList.get(i).onClientConnected(this, socketServerClient); 362 | } 363 | } 364 | 365 | private void __i__onSocketServerClientDisconnected(final SocketServerClient socketServerClient) { 366 | if (Looper.myLooper() != Looper.getMainLooper()) { 367 | getUiHandler().post(new Runnable() { 368 | @Override 369 | public void run() { 370 | self.__i__onSocketServerClientDisconnected(socketServerClient); 371 | } 372 | }); 373 | return; 374 | } 375 | 376 | ArrayList copyList = 377 | (ArrayList) getSocketServerDelegates().clone(); 378 | int count = copyList.size(); 379 | for (int i = 0; i < count; ++i) { 380 | copyList.get(i).onClientDisconnected(this, socketServerClient); 381 | } 382 | } 383 | 384 | /* Inner Classes */ 385 | private class ListenThread extends Thread { 386 | private boolean running; 387 | protected ListenThread setRunning(boolean running) { 388 | this.running = running; 389 | return this; 390 | } 391 | protected boolean isRunning() { 392 | return this.running; 393 | } 394 | 395 | @Override 396 | public void run() { 397 | super.run(); 398 | setRunning(true); 399 | while (!Thread.interrupted() 400 | && self.__i__checkServerSocketAvailable()) { 401 | Socket socket = null; 402 | try { 403 | socket = self.getRunningServerSocket().accept(); 404 | 405 | 406 | SocketServerClient socketServerClient = self.internalGetSocketServerClient(socket); 407 | getRunningSocketServerClients().add(socketServerClient); 408 | socketServerClient.registerSocketClientDelegate(self); 409 | self.__i__onSocketServerClientConnected(socketServerClient); 410 | } 411 | catch (IOException e) { 412 | // e.printStackTrace(); 413 | } 414 | } 415 | 416 | setRunning(false); 417 | 418 | self.setListening(false); 419 | self.setListenThread(null); 420 | self.setRunningServerSocket(null); 421 | 422 | self. __i__disconnectAllClients(); 423 | self.__i__onSocketServerStopListen(); 424 | } 425 | } 426 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/server/SocketServerClient.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.server; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.vilyever.socketclient.SocketClient; 6 | import com.vilyever.socketclient.helper.SocketClientAddress; 7 | import com.vilyever.socketclient.helper.SocketConfigure; 8 | 9 | import java.net.Socket; 10 | 11 | /** 12 | * SocketServerClient 13 | * AndroidSocketClient 14 | * Created by vilyever on 2016/3/23. 15 | * Feature: 16 | */ 17 | public class SocketServerClient extends SocketClient { 18 | final SocketServerClient self = this; 19 | 20 | 21 | /* Constructors */ 22 | public SocketServerClient(@NonNull Socket socket, SocketConfigure configure) { 23 | super(new SocketClientAddress(socket.getLocalAddress().toString().substring(1), "" + socket.getLocalPort())); 24 | 25 | setRunningSocket(socket); 26 | getSocketConfigure().setCharsetName(configure.getCharsetName()).setAddress(getAddress()).setHeartBeatHelper(configure.getHeartBeatHelper()).setSocketPacketHelper(configure.getSocketPacketHelper()); 27 | 28 | internalOnConnected(); 29 | } 30 | 31 | /* Public Methods */ 32 | 33 | 34 | /* Properties */ 35 | 36 | 37 | /* Overrides */ 38 | 39 | 40 | /* Delegates */ 41 | 42 | 43 | /* Private Methods */ 44 | 45 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/server/SocketServerDelegate.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.server; 2 | 3 | /** 4 | * SocketServerDelegate 5 | * Created by vilyever on 2016/5/31. 6 | * Feature: 7 | */ 8 | public interface SocketServerDelegate { 9 | void onServerBeginListen(SocketServer socketServer, int port); 10 | void onServerStopListen(SocketServer socketServer, int port); 11 | void onClientConnected(SocketServer socketServer, SocketServerClient socketServerClient); 12 | void onClientDisconnected(SocketServer socketServer, SocketServerClient socketServerClient); 13 | 14 | class SimpleSocketServerDelegate implements SocketServerDelegate { 15 | @Override 16 | public void onServerBeginListen(SocketServer socketServer, int port) { 17 | 18 | } 19 | 20 | @Override 21 | public void onServerStopListen(SocketServer socketServer, int port) { 22 | 23 | } 24 | 25 | @Override 26 | public void onClientConnected(SocketServer socketServer, SocketServerClient socketServerClient) { 27 | 28 | } 29 | 30 | @Override 31 | public void onClientDisconnected(SocketServer socketServer, SocketServerClient socketServerClient) { 32 | 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/util/BytesWrapper.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.util; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * BytesWrapper 7 | * AndroidSocketClient 8 | * Created by vilyever on 2016/4/27. 9 | * Feature: 10 | */ 11 | public class BytesWrapper { 12 | final BytesWrapper self = this; 13 | 14 | 15 | /* Constructors */ 16 | public BytesWrapper(byte[] bytes) { 17 | if (bytes == null) { 18 | throw new NullPointerException(); 19 | } 20 | this.bytes = bytes; 21 | } 22 | 23 | /* Public Methods */ 24 | public boolean equalsBytes(byte[] bytes) { 25 | return Arrays.equals(getBytes(), bytes); 26 | } 27 | 28 | /* Properties */ 29 | private final byte[] bytes; 30 | public byte[] getBytes() { 31 | return this.bytes; 32 | } 33 | 34 | /* Overrides */ 35 | @Override 36 | public boolean equals(Object other) 37 | { 38 | if (!(other instanceof BytesWrapper)) { 39 | return false; 40 | } 41 | return equalsBytes(((BytesWrapper)other).getBytes()); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Arrays.hashCode(getBytes()); 47 | } 48 | 49 | /* Delegates */ 50 | 51 | 52 | /* Private Methods */ 53 | 54 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/util/CharsetUtil.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | /** 6 | * CharsetUtil 7 | * AndroidSocketClient 8 | * Created by vilyever on 2016/4/11. 9 | * Feature: 10 | */ 11 | public class CharsetUtil { 12 | final CharsetUtil self = this; 13 | 14 | public static final String UTF_8 = "UTF-8"; 15 | 16 | /* Constructors */ 17 | 18 | 19 | /* Public Methods */ 20 | public static byte[] stringToData(String string, String charsetName) { 21 | if (string != null) { 22 | try { 23 | return string.getBytes(charsetName); 24 | } 25 | catch (UnsupportedEncodingException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | return null; 30 | } 31 | 32 | public static String dataToString(byte[] data, String charsetName) { 33 | if (data != null) { 34 | try { 35 | return new String(data, charsetName); 36 | } 37 | catch (UnsupportedEncodingException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | /* Properties */ 45 | 46 | 47 | /* Overrides */ 48 | 49 | 50 | /* Delegates */ 51 | 52 | 53 | /* Private Methods */ 54 | 55 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/util/IPUtil.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.util; 2 | 3 | import java.net.InetAddress; 4 | import java.net.NetworkInterface; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | /** 9 | * IPUtil 10 | * AndroidSocketClient 11 | * Created by vilyever on 2016/3/30. 12 | * Feature: 13 | */ 14 | public class IPUtil { 15 | final IPUtil self = this; 16 | 17 | 18 | /* Constructors */ 19 | 20 | 21 | /* Public Methods */ 22 | public static String getLocalIPAddress(boolean useIPv4) { 23 | try { 24 | List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); 25 | for (NetworkInterface networkInterface : interfaces) { 26 | List inetAddresses = Collections.list(networkInterface.getInetAddresses()); 27 | for (InetAddress address : inetAddresses) { 28 | if (!address.isLoopbackAddress()) { 29 | String sAddr = address.getHostAddress(); 30 | //boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr); 31 | boolean isIPv4 = sAddr.indexOf(':')<0; 32 | 33 | if (useIPv4) { 34 | if (isIPv4) 35 | return sAddr; 36 | } else { 37 | if (!isIPv4) { 38 | int delim = sAddr.indexOf('%'); // drop ip6 zone suffix 39 | return delim<0 ? sAddr.toUpperCase() : sAddr.substring(0, delim).toUpperCase(); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } catch (Exception ex) { } // for now eat exceptions 46 | return ""; 47 | } 48 | 49 | /* Properties */ 50 | 51 | 52 | /* Overrides */ 53 | 54 | 55 | /* Delegates */ 56 | 57 | 58 | /* Private Methods */ 59 | 60 | } -------------------------------------------------------------------------------- /socketclient/src/main/java/com/vilyever/socketclient/util/StringValidation.java: -------------------------------------------------------------------------------- 1 | package com.vilyever.socketclient.util; 2 | 3 | /** 4 | * StringValidation 5 | * ESB 6 | * Created by vilyever on 2016/2/23. 7 | * Feature: 8 | * 输入检查 9 | * 正则检查String是否符合某些格式 10 | */ 11 | public class StringValidation { 12 | final StringValidation self = this; 13 | 14 | public final static String RegexAllChinese = "^[\\u4e00-\\u9fa5]*$"; 15 | public final static String RegexChineseName = "^[\\u4e00-\\u9fa5]{2,15}$"; // According To Statistics, the longest name has 15 character 16 | public final static String RegexPhoneNumber = "^(((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8})|((\\d{3,4}-)?\\d{7,8}(-\\d{1,4})?)$"; 17 | public final static String RegexEmail = "w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*"; 18 | public final static String RegexIP = "^(25[0-5]|2[0-4][0-9]|1{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|1{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|1{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|1{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$"; 19 | public final static String RegexPort = "^6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}$"; 20 | 21 | 22 | /* Constructors */ 23 | 24 | 25 | /* Public Methods */ 26 | public static boolean validateRegularCharacter(String string, int minSize, int maxSize) { 27 | return validateRegex(string, "^\\w{" + minSize + "," + maxSize + "}$"); 28 | } 29 | 30 | public static boolean validateRegex(String string, String regex) { 31 | if (string == null) { 32 | return false; 33 | } 34 | 35 | return string.matches(regex); 36 | } 37 | 38 | 39 | /* Properties */ 40 | 41 | 42 | /* Overrides */ 43 | 44 | 45 | /* Delegates */ 46 | 47 | 48 | /* Private Methods */ 49 | 50 | } -------------------------------------------------------------------------------- /socketclient/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | VDSocketClient 3 | 4 | --------------------------------------------------------------------------------