├── .github
├── dependabot.yml
└── workflows
│ └── maven.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── pom.xml
├── protocol-zh.md
├── src
├── easechat-client-j
│ ├── build.gradle.kts
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── net
│ │ └── easecation
│ │ └── easechat
│ │ ├── Main.java
│ │ ├── api
│ │ ├── Logger.java
│ │ ├── Message.java
│ │ ├── MessageReceiver.java
│ │ ├── MessageSender.java
│ │ ├── SimpleLogger.java
│ │ └── message
│ │ │ ├── AutoSubChannelMessage.java
│ │ │ ├── ChannelMessage.java
│ │ │ ├── DisconnectMessage.java
│ │ │ ├── HelloMessage.java
│ │ │ ├── ReceiveMessage.java
│ │ │ └── TransmitMessage.java
│ │ └── network
│ │ ├── EaseChatClient.java
│ │ ├── MessageCodec.java
│ │ └── MessageHandler.java
├── easechat-exec-rs
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── easechat-nexus-rs
│ ├── Cargo.toml
│ ├── examples
│ │ └── console_parser.rs
│ └── src
│ │ ├── console_rule.pest
│ │ └── main.rs
├── easechat-nexus-v2-rs
│ ├── Cargo.toml
│ └── src
│ │ ├── adapt.rs
│ │ ├── lib.rs
│ │ └── service.rs
└── easechat-record-rs
│ ├── Cargo.toml
│ ├── examples
│ └── simple-mem-db.rs
│ └── src
│ └── main.rs
└── start.bat
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "maven"
4 | directory: "/"
5 | schedule:
6 | interval: "monthly"
7 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Java CI
2 |
3 | on:
4 | push:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build:
9 | name: Java ${{ matrix.java }}
10 | runs-on: ubuntu-latest
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | java:
15 | [
16 | 21,
17 | ]
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 | - name: Set up JDK ${{ matrix.java }}
22 | uses: actions/setup-java@v4
23 | with:
24 | java-version: ${{ matrix.java }}
25 | distribution: oracle
26 | cache: maven
27 | - name: Build with Maven
28 | run: mvn -B clean package
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/target
2 | **/*.rs.bk
3 | .idea/
4 | _server/
5 | **/*.iml
6 | **/*.class
7 | **/*.jar
8 | tstart.sh
9 | build
10 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "android_system_properties"
7 | version = "0.1.5"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
10 | dependencies = [
11 | "libc",
12 | ]
13 |
14 | [[package]]
15 | name = "ansi_term"
16 | version = "0.12.1"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
19 | dependencies = [
20 | "winapi 0.3.9",
21 | ]
22 |
23 | [[package]]
24 | name = "atty"
25 | version = "0.2.14"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
28 | dependencies = [
29 | "hermit-abi",
30 | "libc",
31 | "winapi 0.3.9",
32 | ]
33 |
34 | [[package]]
35 | name = "autocfg"
36 | version = "1.1.0"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
39 |
40 | [[package]]
41 | name = "bitflags"
42 | version = "1.3.2"
43 | source = "registry+https://github.com/rust-lang/crates.io-index"
44 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
45 |
46 | [[package]]
47 | name = "block-buffer"
48 | version = "0.10.3"
49 | source = "registry+https://github.com/rust-lang/crates.io-index"
50 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
51 | dependencies = [
52 | "generic-array",
53 | ]
54 |
55 | [[package]]
56 | name = "bumpalo"
57 | version = "3.11.1"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
60 |
61 | [[package]]
62 | name = "byteorder"
63 | version = "1.4.3"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
66 |
67 | [[package]]
68 | name = "bytes"
69 | version = "0.4.12"
70 | source = "registry+https://github.com/rust-lang/crates.io-index"
71 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
72 | dependencies = [
73 | "byteorder",
74 | "iovec",
75 | ]
76 |
77 | [[package]]
78 | name = "cc"
79 | version = "1.0.77"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
82 |
83 | [[package]]
84 | name = "cfg-if"
85 | version = "0.1.10"
86 | source = "registry+https://github.com/rust-lang/crates.io-index"
87 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
88 |
89 | [[package]]
90 | name = "cfg-if"
91 | version = "1.0.0"
92 | source = "registry+https://github.com/rust-lang/crates.io-index"
93 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
94 |
95 | [[package]]
96 | name = "chrono"
97 | version = "0.4.23"
98 | source = "registry+https://github.com/rust-lang/crates.io-index"
99 | checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
100 | dependencies = [
101 | "iana-time-zone",
102 | "js-sys",
103 | "num-integer",
104 | "num-traits",
105 | "time",
106 | "wasm-bindgen",
107 | "winapi 0.3.9",
108 | ]
109 |
110 | [[package]]
111 | name = "clap"
112 | version = "2.34.0"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
115 | dependencies = [
116 | "ansi_term",
117 | "atty",
118 | "bitflags",
119 | "strsim",
120 | "textwrap",
121 | "unicode-width",
122 | "vec_map",
123 | ]
124 |
125 | [[package]]
126 | name = "codespan-reporting"
127 | version = "0.11.1"
128 | source = "registry+https://github.com/rust-lang/crates.io-index"
129 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
130 | dependencies = [
131 | "termcolor",
132 | "unicode-width",
133 | ]
134 |
135 | [[package]]
136 | name = "core-foundation-sys"
137 | version = "0.8.3"
138 | source = "registry+https://github.com/rust-lang/crates.io-index"
139 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
140 |
141 | [[package]]
142 | name = "cpufeatures"
143 | version = "0.2.5"
144 | source = "registry+https://github.com/rust-lang/crates.io-index"
145 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
146 | dependencies = [
147 | "libc",
148 | ]
149 |
150 | [[package]]
151 | name = "crypto-common"
152 | version = "0.1.6"
153 | source = "registry+https://github.com/rust-lang/crates.io-index"
154 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
155 | dependencies = [
156 | "generic-array",
157 | "typenum",
158 | ]
159 |
160 | [[package]]
161 | name = "cxx"
162 | version = "1.0.83"
163 | source = "registry+https://github.com/rust-lang/crates.io-index"
164 | checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
165 | dependencies = [
166 | "cc",
167 | "cxxbridge-flags",
168 | "cxxbridge-macro",
169 | "link-cplusplus",
170 | ]
171 |
172 | [[package]]
173 | name = "cxx-build"
174 | version = "1.0.83"
175 | source = "registry+https://github.com/rust-lang/crates.io-index"
176 | checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
177 | dependencies = [
178 | "cc",
179 | "codespan-reporting",
180 | "once_cell",
181 | "proc-macro2",
182 | "quote",
183 | "scratch",
184 | "syn",
185 | ]
186 |
187 | [[package]]
188 | name = "cxxbridge-flags"
189 | version = "1.0.83"
190 | source = "registry+https://github.com/rust-lang/crates.io-index"
191 | checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
192 |
193 | [[package]]
194 | name = "cxxbridge-macro"
195 | version = "1.0.83"
196 | source = "registry+https://github.com/rust-lang/crates.io-index"
197 | checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
198 | dependencies = [
199 | "proc-macro2",
200 | "quote",
201 | "syn",
202 | ]
203 |
204 | [[package]]
205 | name = "digest"
206 | version = "0.10.6"
207 | source = "registry+https://github.com/rust-lang/crates.io-index"
208 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
209 | dependencies = [
210 | "block-buffer",
211 | "crypto-common",
212 | ]
213 |
214 | [[package]]
215 | name = "ease_chat_nexus"
216 | version = "0.0.2"
217 | dependencies = [
218 | "pest",
219 | "pest_derive",
220 | "ws",
221 | ]
222 |
223 | [[package]]
224 | name = "easechat-exec"
225 | version = "0.0.0"
226 | dependencies = [
227 | "easechat-nexus",
228 | ]
229 |
230 | [[package]]
231 | name = "easechat-nexus"
232 | version = "0.0.0"
233 | dependencies = [
234 | "ws",
235 | ]
236 |
237 | [[package]]
238 | name = "easechat-record"
239 | version = "0.0.0"
240 | dependencies = [
241 | "chrono",
242 | "clap",
243 | "rusqlite",
244 | "ws",
245 | ]
246 |
247 | [[package]]
248 | name = "fuchsia-cprng"
249 | version = "0.1.1"
250 | source = "registry+https://github.com/rust-lang/crates.io-index"
251 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
252 |
253 | [[package]]
254 | name = "fuchsia-zircon"
255 | version = "0.3.3"
256 | source = "registry+https://github.com/rust-lang/crates.io-index"
257 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
258 | dependencies = [
259 | "bitflags",
260 | "fuchsia-zircon-sys",
261 | ]
262 |
263 | [[package]]
264 | name = "fuchsia-zircon-sys"
265 | version = "0.3.3"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
268 |
269 | [[package]]
270 | name = "generic-array"
271 | version = "0.14.6"
272 | source = "registry+https://github.com/rust-lang/crates.io-index"
273 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
274 | dependencies = [
275 | "typenum",
276 | "version_check",
277 | ]
278 |
279 | [[package]]
280 | name = "hermit-abi"
281 | version = "0.1.19"
282 | source = "registry+https://github.com/rust-lang/crates.io-index"
283 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
284 | dependencies = [
285 | "libc",
286 | ]
287 |
288 | [[package]]
289 | name = "httparse"
290 | version = "1.8.0"
291 | source = "registry+https://github.com/rust-lang/crates.io-index"
292 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
293 |
294 | [[package]]
295 | name = "iana-time-zone"
296 | version = "0.1.53"
297 | source = "registry+https://github.com/rust-lang/crates.io-index"
298 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
299 | dependencies = [
300 | "android_system_properties",
301 | "core-foundation-sys",
302 | "iana-time-zone-haiku",
303 | "js-sys",
304 | "wasm-bindgen",
305 | "winapi 0.3.9",
306 | ]
307 |
308 | [[package]]
309 | name = "iana-time-zone-haiku"
310 | version = "0.1.1"
311 | source = "registry+https://github.com/rust-lang/crates.io-index"
312 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
313 | dependencies = [
314 | "cxx",
315 | "cxx-build",
316 | ]
317 |
318 | [[package]]
319 | name = "idna"
320 | version = "0.1.5"
321 | source = "registry+https://github.com/rust-lang/crates.io-index"
322 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
323 | dependencies = [
324 | "matches",
325 | "unicode-bidi",
326 | "unicode-normalization",
327 | ]
328 |
329 | [[package]]
330 | name = "iovec"
331 | version = "0.1.4"
332 | source = "registry+https://github.com/rust-lang/crates.io-index"
333 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
334 | dependencies = [
335 | "libc",
336 | ]
337 |
338 | [[package]]
339 | name = "js-sys"
340 | version = "0.3.60"
341 | source = "registry+https://github.com/rust-lang/crates.io-index"
342 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
343 | dependencies = [
344 | "wasm-bindgen",
345 | ]
346 |
347 | [[package]]
348 | name = "kernel32-sys"
349 | version = "0.2.2"
350 | source = "registry+https://github.com/rust-lang/crates.io-index"
351 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
352 | dependencies = [
353 | "winapi 0.2.8",
354 | "winapi-build",
355 | ]
356 |
357 | [[package]]
358 | name = "lazycell"
359 | version = "1.3.0"
360 | source = "registry+https://github.com/rust-lang/crates.io-index"
361 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
362 |
363 | [[package]]
364 | name = "libc"
365 | version = "0.2.138"
366 | source = "registry+https://github.com/rust-lang/crates.io-index"
367 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
368 |
369 | [[package]]
370 | name = "libsqlite3-sys"
371 | version = "0.11.1"
372 | source = "registry+https://github.com/rust-lang/crates.io-index"
373 | checksum = "3567bc1a0c84e2c0d71eeb4a1f08451babf7843babd733158777d9c686dad9f3"
374 | dependencies = [
375 | "pkg-config",
376 | "vcpkg",
377 | ]
378 |
379 | [[package]]
380 | name = "link-cplusplus"
381 | version = "1.0.7"
382 | source = "registry+https://github.com/rust-lang/crates.io-index"
383 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
384 | dependencies = [
385 | "cc",
386 | ]
387 |
388 | [[package]]
389 | name = "linked-hash-map"
390 | version = "0.5.6"
391 | source = "registry+https://github.com/rust-lang/crates.io-index"
392 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
393 |
394 | [[package]]
395 | name = "log"
396 | version = "0.4.17"
397 | source = "registry+https://github.com/rust-lang/crates.io-index"
398 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
399 | dependencies = [
400 | "cfg-if 1.0.0",
401 | ]
402 |
403 | [[package]]
404 | name = "lru-cache"
405 | version = "0.1.2"
406 | source = "registry+https://github.com/rust-lang/crates.io-index"
407 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
408 | dependencies = [
409 | "linked-hash-map",
410 | ]
411 |
412 | [[package]]
413 | name = "matches"
414 | version = "0.1.9"
415 | source = "registry+https://github.com/rust-lang/crates.io-index"
416 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
417 |
418 | [[package]]
419 | name = "mio"
420 | version = "0.6.23"
421 | source = "registry+https://github.com/rust-lang/crates.io-index"
422 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
423 | dependencies = [
424 | "cfg-if 0.1.10",
425 | "fuchsia-zircon",
426 | "fuchsia-zircon-sys",
427 | "iovec",
428 | "kernel32-sys",
429 | "libc",
430 | "log",
431 | "miow",
432 | "net2",
433 | "slab",
434 | "winapi 0.2.8",
435 | ]
436 |
437 | [[package]]
438 | name = "mio-extras"
439 | version = "2.0.6"
440 | source = "registry+https://github.com/rust-lang/crates.io-index"
441 | checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
442 | dependencies = [
443 | "lazycell",
444 | "log",
445 | "mio",
446 | "slab",
447 | ]
448 |
449 | [[package]]
450 | name = "miow"
451 | version = "0.2.2"
452 | source = "registry+https://github.com/rust-lang/crates.io-index"
453 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
454 | dependencies = [
455 | "kernel32-sys",
456 | "net2",
457 | "winapi 0.2.8",
458 | "ws2_32-sys",
459 | ]
460 |
461 | [[package]]
462 | name = "net2"
463 | version = "0.2.38"
464 | source = "registry+https://github.com/rust-lang/crates.io-index"
465 | checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
466 | dependencies = [
467 | "cfg-if 0.1.10",
468 | "libc",
469 | "winapi 0.3.9",
470 | ]
471 |
472 | [[package]]
473 | name = "num-integer"
474 | version = "0.1.45"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
477 | dependencies = [
478 | "autocfg",
479 | "num-traits",
480 | ]
481 |
482 | [[package]]
483 | name = "num-traits"
484 | version = "0.2.15"
485 | source = "registry+https://github.com/rust-lang/crates.io-index"
486 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
487 | dependencies = [
488 | "autocfg",
489 | ]
490 |
491 | [[package]]
492 | name = "once_cell"
493 | version = "1.16.0"
494 | source = "registry+https://github.com/rust-lang/crates.io-index"
495 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
496 |
497 | [[package]]
498 | name = "percent-encoding"
499 | version = "1.0.1"
500 | source = "registry+https://github.com/rust-lang/crates.io-index"
501 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
502 |
503 | [[package]]
504 | name = "pest"
505 | version = "2.5.1"
506 | source = "registry+https://github.com/rust-lang/crates.io-index"
507 | checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
508 | dependencies = [
509 | "thiserror",
510 | "ucd-trie",
511 | ]
512 |
513 | [[package]]
514 | name = "pest_derive"
515 | version = "2.5.1"
516 | source = "registry+https://github.com/rust-lang/crates.io-index"
517 | checksum = "cdc078600d06ff90d4ed238f0119d84ab5d43dbaad278b0e33a8820293b32344"
518 | dependencies = [
519 | "pest",
520 | "pest_generator",
521 | ]
522 |
523 | [[package]]
524 | name = "pest_generator"
525 | version = "2.5.1"
526 | source = "registry+https://github.com/rust-lang/crates.io-index"
527 | checksum = "28a1af60b1c4148bb269006a750cff8e2ea36aff34d2d96cf7be0b14d1bed23c"
528 | dependencies = [
529 | "pest",
530 | "pest_meta",
531 | "proc-macro2",
532 | "quote",
533 | "syn",
534 | ]
535 |
536 | [[package]]
537 | name = "pest_meta"
538 | version = "2.5.1"
539 | source = "registry+https://github.com/rust-lang/crates.io-index"
540 | checksum = "fec8605d59fc2ae0c6c1aefc0c7c7a9769732017c0ce07f7a9cfffa7b4404f20"
541 | dependencies = [
542 | "once_cell",
543 | "pest",
544 | "sha1 0.10.5",
545 | ]
546 |
547 | [[package]]
548 | name = "pkg-config"
549 | version = "0.3.26"
550 | source = "registry+https://github.com/rust-lang/crates.io-index"
551 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
552 |
553 | [[package]]
554 | name = "proc-macro2"
555 | version = "1.0.47"
556 | source = "registry+https://github.com/rust-lang/crates.io-index"
557 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
558 | dependencies = [
559 | "unicode-ident",
560 | ]
561 |
562 | [[package]]
563 | name = "quote"
564 | version = "1.0.21"
565 | source = "registry+https://github.com/rust-lang/crates.io-index"
566 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
567 | dependencies = [
568 | "proc-macro2",
569 | ]
570 |
571 | [[package]]
572 | name = "rand"
573 | version = "0.4.6"
574 | source = "registry+https://github.com/rust-lang/crates.io-index"
575 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
576 | dependencies = [
577 | "fuchsia-cprng",
578 | "libc",
579 | "rand_core 0.3.1",
580 | "rdrand",
581 | "winapi 0.3.9",
582 | ]
583 |
584 | [[package]]
585 | name = "rand_core"
586 | version = "0.3.1"
587 | source = "registry+https://github.com/rust-lang/crates.io-index"
588 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
589 | dependencies = [
590 | "rand_core 0.4.2",
591 | ]
592 |
593 | [[package]]
594 | name = "rand_core"
595 | version = "0.4.2"
596 | source = "registry+https://github.com/rust-lang/crates.io-index"
597 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
598 |
599 | [[package]]
600 | name = "rdrand"
601 | version = "0.4.0"
602 | source = "registry+https://github.com/rust-lang/crates.io-index"
603 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
604 | dependencies = [
605 | "rand_core 0.3.1",
606 | ]
607 |
608 | [[package]]
609 | name = "rusqlite"
610 | version = "0.16.0"
611 | source = "registry+https://github.com/rust-lang/crates.io-index"
612 | checksum = "6381ddfe91dbb659b4b132168da15985bc84162378cf4fcdc4eb99c857d063e2"
613 | dependencies = [
614 | "bitflags",
615 | "chrono",
616 | "libsqlite3-sys",
617 | "lru-cache",
618 | "time",
619 | ]
620 |
621 | [[package]]
622 | name = "scratch"
623 | version = "1.0.2"
624 | source = "registry+https://github.com/rust-lang/crates.io-index"
625 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
626 |
627 | [[package]]
628 | name = "sha1"
629 | version = "0.6.1"
630 | source = "registry+https://github.com/rust-lang/crates.io-index"
631 | checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
632 | dependencies = [
633 | "sha1_smol",
634 | ]
635 |
636 | [[package]]
637 | name = "sha1"
638 | version = "0.10.5"
639 | source = "registry+https://github.com/rust-lang/crates.io-index"
640 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
641 | dependencies = [
642 | "cfg-if 1.0.0",
643 | "cpufeatures",
644 | "digest",
645 | ]
646 |
647 | [[package]]
648 | name = "sha1_smol"
649 | version = "1.0.0"
650 | source = "registry+https://github.com/rust-lang/crates.io-index"
651 | checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
652 |
653 | [[package]]
654 | name = "slab"
655 | version = "0.4.7"
656 | source = "registry+https://github.com/rust-lang/crates.io-index"
657 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
658 | dependencies = [
659 | "autocfg",
660 | ]
661 |
662 | [[package]]
663 | name = "strsim"
664 | version = "0.8.0"
665 | source = "registry+https://github.com/rust-lang/crates.io-index"
666 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
667 |
668 | [[package]]
669 | name = "syn"
670 | version = "1.0.105"
671 | source = "registry+https://github.com/rust-lang/crates.io-index"
672 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
673 | dependencies = [
674 | "proc-macro2",
675 | "quote",
676 | "unicode-ident",
677 | ]
678 |
679 | [[package]]
680 | name = "termcolor"
681 | version = "1.1.3"
682 | source = "registry+https://github.com/rust-lang/crates.io-index"
683 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
684 | dependencies = [
685 | "winapi-util",
686 | ]
687 |
688 | [[package]]
689 | name = "textwrap"
690 | version = "0.11.0"
691 | source = "registry+https://github.com/rust-lang/crates.io-index"
692 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
693 | dependencies = [
694 | "unicode-width",
695 | ]
696 |
697 | [[package]]
698 | name = "thiserror"
699 | version = "1.0.37"
700 | source = "registry+https://github.com/rust-lang/crates.io-index"
701 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
702 | dependencies = [
703 | "thiserror-impl",
704 | ]
705 |
706 | [[package]]
707 | name = "thiserror-impl"
708 | version = "1.0.37"
709 | source = "registry+https://github.com/rust-lang/crates.io-index"
710 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
711 | dependencies = [
712 | "proc-macro2",
713 | "quote",
714 | "syn",
715 | ]
716 |
717 | [[package]]
718 | name = "time"
719 | version = "0.1.45"
720 | source = "registry+https://github.com/rust-lang/crates.io-index"
721 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
722 | dependencies = [
723 | "libc",
724 | "wasi",
725 | "winapi 0.3.9",
726 | ]
727 |
728 | [[package]]
729 | name = "tinyvec"
730 | version = "1.6.0"
731 | source = "registry+https://github.com/rust-lang/crates.io-index"
732 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
733 | dependencies = [
734 | "tinyvec_macros",
735 | ]
736 |
737 | [[package]]
738 | name = "tinyvec_macros"
739 | version = "0.1.0"
740 | source = "registry+https://github.com/rust-lang/crates.io-index"
741 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
742 |
743 | [[package]]
744 | name = "typenum"
745 | version = "1.16.0"
746 | source = "registry+https://github.com/rust-lang/crates.io-index"
747 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
748 |
749 | [[package]]
750 | name = "ucd-trie"
751 | version = "0.1.5"
752 | source = "registry+https://github.com/rust-lang/crates.io-index"
753 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
754 |
755 | [[package]]
756 | name = "unicode-bidi"
757 | version = "0.3.8"
758 | source = "registry+https://github.com/rust-lang/crates.io-index"
759 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
760 |
761 | [[package]]
762 | name = "unicode-ident"
763 | version = "1.0.5"
764 | source = "registry+https://github.com/rust-lang/crates.io-index"
765 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
766 |
767 | [[package]]
768 | name = "unicode-normalization"
769 | version = "0.1.22"
770 | source = "registry+https://github.com/rust-lang/crates.io-index"
771 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
772 | dependencies = [
773 | "tinyvec",
774 | ]
775 |
776 | [[package]]
777 | name = "unicode-width"
778 | version = "0.1.10"
779 | source = "registry+https://github.com/rust-lang/crates.io-index"
780 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
781 |
782 | [[package]]
783 | name = "url"
784 | version = "1.7.2"
785 | source = "registry+https://github.com/rust-lang/crates.io-index"
786 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
787 | dependencies = [
788 | "idna",
789 | "matches",
790 | "percent-encoding",
791 | ]
792 |
793 | [[package]]
794 | name = "vcpkg"
795 | version = "0.2.15"
796 | source = "registry+https://github.com/rust-lang/crates.io-index"
797 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
798 |
799 | [[package]]
800 | name = "vec_map"
801 | version = "0.8.2"
802 | source = "registry+https://github.com/rust-lang/crates.io-index"
803 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
804 |
805 | [[package]]
806 | name = "version_check"
807 | version = "0.9.4"
808 | source = "registry+https://github.com/rust-lang/crates.io-index"
809 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
810 |
811 | [[package]]
812 | name = "wasi"
813 | version = "0.10.0+wasi-snapshot-preview1"
814 | source = "registry+https://github.com/rust-lang/crates.io-index"
815 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
816 |
817 | [[package]]
818 | name = "wasm-bindgen"
819 | version = "0.2.83"
820 | source = "registry+https://github.com/rust-lang/crates.io-index"
821 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
822 | dependencies = [
823 | "cfg-if 1.0.0",
824 | "wasm-bindgen-macro",
825 | ]
826 |
827 | [[package]]
828 | name = "wasm-bindgen-backend"
829 | version = "0.2.83"
830 | source = "registry+https://github.com/rust-lang/crates.io-index"
831 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
832 | dependencies = [
833 | "bumpalo",
834 | "log",
835 | "once_cell",
836 | "proc-macro2",
837 | "quote",
838 | "syn",
839 | "wasm-bindgen-shared",
840 | ]
841 |
842 | [[package]]
843 | name = "wasm-bindgen-macro"
844 | version = "0.2.83"
845 | source = "registry+https://github.com/rust-lang/crates.io-index"
846 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
847 | dependencies = [
848 | "quote",
849 | "wasm-bindgen-macro-support",
850 | ]
851 |
852 | [[package]]
853 | name = "wasm-bindgen-macro-support"
854 | version = "0.2.83"
855 | source = "registry+https://github.com/rust-lang/crates.io-index"
856 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
857 | dependencies = [
858 | "proc-macro2",
859 | "quote",
860 | "syn",
861 | "wasm-bindgen-backend",
862 | "wasm-bindgen-shared",
863 | ]
864 |
865 | [[package]]
866 | name = "wasm-bindgen-shared"
867 | version = "0.2.83"
868 | source = "registry+https://github.com/rust-lang/crates.io-index"
869 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
870 |
871 | [[package]]
872 | name = "winapi"
873 | version = "0.2.8"
874 | source = "registry+https://github.com/rust-lang/crates.io-index"
875 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
876 |
877 | [[package]]
878 | name = "winapi"
879 | version = "0.3.9"
880 | source = "registry+https://github.com/rust-lang/crates.io-index"
881 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
882 | dependencies = [
883 | "winapi-i686-pc-windows-gnu",
884 | "winapi-x86_64-pc-windows-gnu",
885 | ]
886 |
887 | [[package]]
888 | name = "winapi-build"
889 | version = "0.1.1"
890 | source = "registry+https://github.com/rust-lang/crates.io-index"
891 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
892 |
893 | [[package]]
894 | name = "winapi-i686-pc-windows-gnu"
895 | version = "0.4.0"
896 | source = "registry+https://github.com/rust-lang/crates.io-index"
897 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
898 |
899 | [[package]]
900 | name = "winapi-util"
901 | version = "0.1.5"
902 | source = "registry+https://github.com/rust-lang/crates.io-index"
903 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
904 | dependencies = [
905 | "winapi 0.3.9",
906 | ]
907 |
908 | [[package]]
909 | name = "winapi-x86_64-pc-windows-gnu"
910 | version = "0.4.0"
911 | source = "registry+https://github.com/rust-lang/crates.io-index"
912 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
913 |
914 | [[package]]
915 | name = "ws"
916 | version = "0.7.9"
917 | source = "registry+https://github.com/rust-lang/crates.io-index"
918 | checksum = "329d3e6dd450a9c5c73024e1047f0be7e24121a68484eb0b5368977bee3cf8c3"
919 | dependencies = [
920 | "byteorder",
921 | "bytes",
922 | "httparse",
923 | "log",
924 | "mio",
925 | "mio-extras",
926 | "rand",
927 | "sha1 0.6.1",
928 | "slab",
929 | "url",
930 | ]
931 |
932 | [[package]]
933 | name = "ws2_32-sys"
934 | version = "0.2.1"
935 | source = "registry+https://github.com/rust-lang/crates.io-index"
936 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
937 | dependencies = [
938 | "winapi 0.2.8",
939 | "winapi-build",
940 | ]
941 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "src/easechat-nexus-rs",
4 | "src/easechat-nexus-v2-rs",
5 | "src/easechat-exec-rs",
6 | "src/easechat-record-rs"
7 | ]
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Fang Yizhou, HDJ, Luo Jia, Woder
2 | EaseChat is licensed under the Mulan PSL v1.
3 | You can use this software according to the terms and conditions of the Mulan PSL v1.
4 | You may obtain a copy of Mulan PSL v1 at:
5 | http://license.coscl.org.cn/MulanPSL
6 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
7 | IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
8 | PURPOSE.
9 | See the Mulan PSL v1 for more details.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EaseChat
2 |
3 | Simple message exchange network with channel-subscription based structure.
4 | EaseChat was originally designed as a chat message exchanging service for games, but its usage could be widen to debug messages
5 | and commands for maintaining.
6 |
7 | Although the first version of EaseChat Nexus may be less flexible, we deployed it on production use months ago.
8 | Its record on non-pausing and reboot-free continous execution is 3,432 hours on our mini-game server network region, since 30 Jan to 22 Jun 2019, killed mistaken by a ctrl-c quit. The network consists of ~350 endpoints and produces ~1k messages on average per second, their CPU and RAM usage being low at the same time.
9 | Players mostly suggest it 'Great' in our recent survey on this part of the game's experience.
10 |
11 | ## Project structure
12 |
13 | | Name | Description |
14 | |:----|:-----------|
15 | | easechat-client-j | Netty-based Java implementation of EaseChat client oriented to production |
16 | | easechat-exec-rs | (In progress) Complete cli application of EaseChat in Rust |
17 | | easechat-nexus-rs | First version of Rust EaseChat nexus impl using mio and ws |
18 | | easechat-nexus-v2-rs | (In progress) Redesigned Rust EaseChat protocol library intended for general use |
19 | | easechat-record-rs | (In progress) Simple Rust cli EaseChat client app, connect to nexus and save message via SQLite |
20 |
21 | ## Run EaseChat Nexus
22 |
23 | 1. Install [rust](https://rust-lang.org/), or run `rustup update` if already installed.
24 | 2. Execute `cargo run -p ease_chat_nexus` in project root.
25 |
26 | Step 1 is necessary because this EaseChat Nexus implementation requires rust edition 2018 installed.
27 | This implementation is written in rust version 1.32.0-stable.
28 |
29 | ## Protocol specification
30 |
31 | [中文协议文档](https://github.com/EaseCation/ease_chat/blob/master/protocol-zh.md)
32 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | net.easecation
8 | easechat
9 | 1.0-SNAPSHOT
10 | pom
11 |
12 |
13 | src/easechat-client-j
14 |
15 |
16 |
--------------------------------------------------------------------------------
/protocol-zh.md:
--------------------------------------------------------------------------------
1 | # Protocol 协议说明
2 |
3 | ### 基本:
4 |
5 | - 本协议暂时基于WebSocket,属于内网协议
6 | - 为方便调试,直接通过传递字符串进行信息交换,在字符串中使用`|`分割信息体
7 | - 未来将使用可靠性可选的、基于UDP的虚拟连接协议,以字节方式传输;加强安全性,以便开放到公网
8 |
9 | ### 协议种类:
10 |
11 | pid位于数据的第一部分。一般由一个数字与一个简写字母组成。数字代表协议版本号,后方字母代表协议分类。
12 |
13 | Client发送给Nexus的有以下类型:
14 |
15 | - `1h` Hello 握手
16 | - `1c` Channel 订阅频道
17 | - `1t` Transmit 发送消息
18 | - `1d` Disconnect 断开连接
19 |
20 | Nexus主动发送给Client的有以下类型:
21 |
22 | - `1r` Receive 接收消息
23 |
24 | ## 协议内容:
25 |
26 | `Tip:为了防止注入,涉及到第三方可编辑的字符串部分,都特别加入了字符串长度用于检验。`
27 |
28 | ### `1h` Hello 握手:
29 |
30 | 组合方式:1h|名称`长度(字节)`|名称
31 |
32 | _握手是为了上报服务端,该客户端的`名称`,用于发送消息时告知其他客户端消息的来源。_
33 |
34 | 例如:`1h|12|abcdef123456`
35 |
36 | ### `1c` Channel 订阅频道:
37 |
38 | 组合方式:1c|频道名字`长度(字节)`|频道名字|需要订阅的时间(秒部分)|需要订阅的时间(纳秒部分)
39 |
40 | _订阅就像续命,客户端需要定时订阅频道,来告知服务端需要向我发送哪些频道的消息。_
41 |
42 | 例如:
43 | 订阅c/lobby频道五分钟 `1c|7|c/lobby|300|0`
44 |
45 | ### `1t` Transmit 发送消息:
46 |
47 | 组合方式:1t|频道`长度(字节)`|频道名字|消息`长度(字节)`|消息
48 |
49 | _客户端发送给服务端_
50 |
51 | 例如:
52 | 发送helloworld到c/lobby频道 `1t|7|c/lobby|10|helloworld`
53 |
54 | ### `1d` Disconnect 断开连接:
55 |
56 | 组合方式:1d|理由`长度(字节)`|理由
57 |
58 | _发送后,应由服务端断开连接_
59 |
60 | 例如:`1d|11|just logout`
61 |
62 | ### `1r` Receive 接收消息:
63 |
64 | 组合方式:1r|消息来源字符串`长度(字节)`|消息来源|频道字符串`长度(字节)`|频道|消息字符串`长度(字节)`|消息
65 |
66 | _客户端订阅后,由中转枢纽主动发送给客户端。中转枢纽将忽略所有客户端发来的1r类型消息。_
67 |
--------------------------------------------------------------------------------
/src/easechat-client-j/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | application
3 | id("ecbuild.java-conventions")
4 | }
5 |
6 | application {
7 | mainClass = "net.easecation.easechat.Main"
8 | }
9 |
10 | dependencies {
11 | api(libs.netty.all)
12 | }
13 |
14 | description = "easechat-client-j"
15 |
--------------------------------------------------------------------------------
/src/easechat-client-j/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | net.easecation
8 | easechat-client-j
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 21
13 | 21
14 | UTF-8
15 |
16 |
17 |
18 |
19 | io.netty
20 | netty-all
21 | 4.1.107.Final
22 |
23 |
24 |
25 |
26 | ${project.artifactId}
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-shade-plugin
31 | 3.6.0
32 |
33 |
34 | package
35 |
36 | shade
37 |
38 |
39 |
40 |
41 | net.easecation.easechat.Main
42 |
43 |
44 | ${project.build.directory}/dependency-reduced-pom.xml
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/Main.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat;
2 |
3 | import net.easecation.easechat.api.message.AutoSubChannelMessage;
4 | import net.easecation.easechat.api.message.TransmitMessage;
5 | import net.easecation.easechat.network.EaseChatClient;
6 |
7 | import java.net.URI;
8 | import java.util.Arrays;
9 | import java.util.Objects;
10 |
11 | public class Main {
12 |
13 | public static void main(String[] args) {
14 |
15 | EaseChatClient c = startClient();
16 |
17 | try {
18 | Thread.sleep(5000);
19 | } catch (InterruptedException e) {
20 | e.printStackTrace();
21 | }
22 |
23 | c.shutdown();
24 |
25 | System.exit(0);
26 |
27 | EaseChatClient[] clients = new EaseChatClient[1000];
28 |
29 | //压力测试 统计并显示已成功握手的连接数量
30 | new Thread(() -> {
31 | while(true) {
32 | try {
33 | System.out.println("连接成功数:" + Arrays.stream(clients).filter(Objects::nonNull).filter(EaseChatClient::isHandshake).count());
34 | } catch (Exception e) {
35 | e.printStackTrace();
36 | }
37 | try {
38 | Thread.sleep(1000);
39 | } catch (InterruptedException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 | }).start();
44 |
45 | //压力测试 定时发送消息
46 | new Thread(() -> {
47 | while(true) {
48 | Arrays.stream(clients).filter(Objects::nonNull).filter(EaseChatClient::isHandshake).forEach(client -> {
49 | client.getSender().sendSyncTransmitMessage(new TransmitMessage("buglet", "TEST!$$!" + System.currentTimeMillis()));
50 | });
51 | try {
52 | Thread.sleep(1000);
53 | } catch (InterruptedException e) {
54 | e.printStackTrace();
55 | }
56 | }
57 | }).start();
58 |
59 | //压力测试 创建大批量连接
60 | for (int i = 0; i < clients.length; i++) {
61 | try {
62 | clients[i] = startClient();
63 | } catch (Exception e) {
64 | e.printStackTrace();
65 | }
66 | }
67 |
68 | }
69 |
70 | private static EaseChatClient startClient() {
71 | EaseChatClient client = new EaseChatClient("ChinaHDJ", URI.create("wx://192.168.31.122:6500"), System.out::println);
72 |
73 | try {
74 | client.start();
75 | } catch (Exception e) {
76 | e.printStackTrace();
77 | System.exit(0);
78 | }
79 | client.getSender().sendSyncChannelMessage(new AutoSubChannelMessage("buglet", 5), f -> client.getLogger().info("已订阅频道!"));
80 | client.getSender().sendSyncChannelMessage(new AutoSubChannelMessage("lobby/main", 5), f -> client.getLogger().info("已订阅频道!"));
81 |
82 | return client;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/Logger.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api;
2 |
3 | /**
4 | * author: MagicDroidX
5 | * Nukkit Project
6 | */
7 | public interface Logger {
8 |
9 | void emergency(String message);
10 |
11 | void alert(String message);
12 |
13 | void critical(String message);
14 |
15 | void error(String message);
16 |
17 | void warning(String message);
18 |
19 | void notice(String message);
20 |
21 | void info(String message);
22 |
23 | void debug(String message);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/Message.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api;
2 |
3 | /*
4 | * 封装一条 消息数据
5 | * */
6 | public interface Message {
7 | String MESSAGE_HELLO = "1h"; //客户端向服务端 握手
8 | String MESSAGE_CHANNEL = "1c"; // 客户端向服务端 订阅频道
9 | String MESSAGE_TRANSMIT = "1t"; // 客户端向服务端发送消息
10 | String MESSAGE_RECEIVE = "1r"; // 服务端 向 客户端发送的数据
11 | String MESSAGE_DISCONNECT = "1d"; // 客户端主动断开与服务端的连接
12 |
13 | int getMessageLength();
14 |
15 | String getMessageType();
16 | }
17 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/MessageReceiver.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api;
2 |
3 | import net.easecation.easechat.api.message.ReceiveMessage;
4 | import net.easecation.easechat.network.EaseChatClient;
5 |
6 | /*
7 | * 消息接收器
8 | * */
9 | public class MessageReceiver {
10 | private Listener listener;
11 | private final EaseChatClient client;
12 |
13 | public MessageReceiver(EaseChatClient client, Listener listener){
14 | this.client = client;
15 | this.listener = listener;
16 | }
17 |
18 | public void setListener(Listener listener) {
19 | if (this.listener == null){
20 | this.listener = listener;
21 | }
22 | }
23 |
24 | public void receive(ReceiveMessage message){
25 | if (listener != null) listener.listen(message);
26 | }
27 |
28 | /*
29 | * 实现 Listener 接口处理接数据接收
30 | * */
31 | @FunctionalInterface
32 | public interface Listener{
33 | void listen(ReceiveMessage logger);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/MessageSender.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api;
2 |
3 | import net.easecation.easechat.api.message.AutoSubChannelMessage;
4 | import net.easecation.easechat.api.message.ChannelMessage;
5 | import net.easecation.easechat.api.message.HelloMessage;
6 | import net.easecation.easechat.api.message.TransmitMessage;
7 | import io.netty.channel.Channel;
8 |
9 | import io.netty.channel.ChannelFuture;
10 | import io.netty.util.concurrent.Future;
11 | import net.easecation.easechat.network.EaseChatClient;
12 |
13 | import java.util.Timer;
14 | import java.util.TimerTask;
15 |
16 | public class MessageSender {
17 |
18 | private final EaseChatClient client;
19 | private final Channel channel;
20 | private final Timer timer;
21 |
22 | public Channel getChannel() {
23 | return channel;
24 | }
25 |
26 | public MessageSender(EaseChatClient client, Channel channel) {
27 | this.client = client;
28 | this.channel = channel;
29 | this.timer = new Timer();
30 | }
31 |
32 | public void stopTimer() {
33 | timer.cancel();
34 | }
35 |
36 | private void catchHandleAutoSubChannelMessage(Message message) {
37 | if (message instanceof AutoSubChannelMessage) {
38 | AutoSubChannelMessage autoSubChannelMessage = (AutoSubChannelMessage) message;
39 | timer.schedule(new AutoSubTimerTask(autoSubChannelMessage), autoSubChannelMessage.getSubscriptionTime() * 900);
40 | }
41 | }
42 |
43 | private boolean checkHandshake(Message message) {
44 | if (message instanceof HelloMessage) return true;
45 | if (!client.isHandshake()) {
46 | client.getInitChannelMessages().add(message);
47 | return false;
48 | }
49 | return true;
50 | }
51 |
52 | /**
53 | * 发送消息 同步方式 不使用Result处理返回值
54 | */
55 | public boolean sendSyncMessage(Message message) {
56 | if (!checkHandshake(message)) return true;
57 | catchHandleAutoSubChannelMessage(message);
58 | try {
59 | return getChannel().writeAndFlush(message).sync().isSuccess();
60 | } catch (InterruptedException e) {
61 | e.printStackTrace();
62 | return false;
63 | }
64 | }
65 |
66 | /**
67 | * 发送消息 同步方式 使用Result处理返回值
68 | */
69 | public void sendSyncMessage(Message message, Result result) {
70 | if (!checkHandshake(message)) return;
71 | catchHandleAutoSubChannelMessage(message);
72 |
73 | try {
74 | ChannelFuture future = getChannel().writeAndFlush(message).sync();
75 |
76 | if (result != null) result.handle(future);
77 | } catch (InterruptedException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 |
82 | /**
83 | * 异步发送
84 | */
85 | public void sendAsyncMessage(Message message, Result result) {
86 | if (!checkHandshake(message)) return;
87 | catchHandleAutoSubChannelMessage(message);
88 |
89 | this.channel.writeAndFlush(message).addListener(result::handle);
90 | }
91 |
92 | public boolean sendSyncChannelMessage(ChannelMessage message) {
93 | return sendSyncMessage(message);
94 | }
95 |
96 | public void sendSyncChannelMessage(ChannelMessage message, Result result) {
97 | sendSyncMessage(message, result);
98 | }
99 |
100 | public boolean sendSyncHelloMessage(HelloMessage message) {
101 | return sendSyncMessage(message);
102 | }
103 |
104 | public void sendSyncHelloMessage(HelloMessage message, Result result) {
105 | sendSyncMessage(message, result);
106 | }
107 |
108 | public boolean sendSyncTransmitMessage(TransmitMessage message) {
109 | return sendSyncMessage(message);
110 | }
111 |
112 | public void sendSyncTransmitMessage(TransmitMessage message, Result result) {
113 | sendSyncMessage(message, result);
114 | }
115 |
116 | public void sendAsyncHelloMessage(HelloMessage message, Result result) {
117 | sendAsyncMessage(message, result);
118 | }
119 |
120 | public void sendAsyncChannelMessage(ChannelMessage message, Result result) {
121 | sendAsyncMessage(message, result);
122 | }
123 |
124 | public void sendAsyncTransmitMessage(TransmitMessage message, Result result) {
125 | sendAsyncMessage(message, result);
126 | }
127 |
128 | public class AutoSubTimerTask extends TimerTask {
129 |
130 | private final AutoSubChannelMessage message;
131 |
132 | public AutoSubTimerTask(AutoSubChannelMessage message) {
133 | this.message = message;
134 | message.setTimerTask(this);
135 | }
136 |
137 | @Override
138 | public void run() {
139 | if (message.isCloseAutoSub()) this.cancel();
140 |
141 | MessageSender.this.sendSyncChannelMessage(message);
142 | }
143 | }
144 |
145 | @FunctionalInterface
146 | public interface Result {
147 | void handle(Future future) throws InterruptedException;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/SimpleLogger.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | public class SimpleLogger implements Logger {
7 |
8 | @Override
9 | public void emergency(String message) {
10 | System.out.println(getTimeFormat() + "[EMERGENCY] " + message);
11 | }
12 |
13 | @Override
14 | public void alert(String message) {
15 | System.out.println(getTimeFormat() + "[ALERT] " + message);
16 | }
17 |
18 | @Override
19 | public void critical(String message) {
20 | System.out.println(getTimeFormat() + "[CRITICAL] " + message);
21 | }
22 |
23 | @Override
24 | public void error(String message) {
25 | System.out.println(getTimeFormat() + "[ERROR] " + message);
26 | }
27 |
28 | @Override
29 | public void warning(String message) {
30 | System.out.println(getTimeFormat() + "[WARNING] " + message);
31 | }
32 |
33 | @Override
34 | public void notice(String message) {
35 | System.out.println(getTimeFormat() + "[NOTICE] " + message);
36 | }
37 |
38 | @Override
39 | public void info(String message) {
40 | System.out.println(getTimeFormat() + "[INFO] " + message);
41 | }
42 |
43 | @Override
44 | public void debug(String message) {
45 | System.out.println(getTimeFormat() + "[DEBUG] " + message);
46 | }
47 |
48 | private String getTimeFormat() {
49 | Date now = new Date();
50 | return new SimpleDateFormat("HH:mm:ss ").format(now);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/AutoSubChannelMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.MessageSender;
4 |
5 | public class AutoSubChannelMessage extends ChannelMessage {
6 | private boolean closeAutoSub;
7 | private MessageSender.AutoSubTimerTask timerTask;
8 |
9 | public AutoSubChannelMessage(String channelName) {
10 | super(channelName);
11 | }
12 |
13 | public AutoSubChannelMessage(String channelName, int subscriptionTime) {
14 | super(channelName, subscriptionTime);
15 | }
16 |
17 | public AutoSubChannelMessage(String channelName, int subscriptionTime, int subscriptionTimeNS) {
18 | super(channelName, subscriptionTime, subscriptionTimeNS);
19 | }
20 |
21 | public void setCloseAutoSub(boolean closeAutoSub) {
22 | this.closeAutoSub = closeAutoSub;
23 | }
24 |
25 | public boolean isCloseAutoSub() {
26 | return closeAutoSub;
27 | }
28 |
29 | public MessageSender.AutoSubTimerTask getTimerTask() {
30 | return timerTask;
31 | }
32 |
33 | public void setTimerTask(MessageSender.AutoSubTimerTask timerTask) {
34 | if(this.timerTask == null) this.timerTask = timerTask;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/ChannelMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.Message;
4 |
5 | /**
6 | * 订阅消息封装
7 | */
8 | public class ChannelMessage implements Message {
9 |
10 | private final String channelName;
11 | private final int subscriptionTime;
12 | private final int subscriptionTimeNS;
13 |
14 | public final static int DEFAULT_SUBSCRIPTION_TIME = 600; //默认订阅时间 十分钟
15 |
16 | public final static int DEFAULT_SUBSCRIPTION_TIME_NS = 0; //默认订阅时间(纳秒)0
17 |
18 | public ChannelMessage(String channelName){
19 | this(channelName, DEFAULT_SUBSCRIPTION_TIME);
20 | }
21 |
22 | public ChannelMessage(String channelName, int subscriptionTime){
23 | this(channelName, subscriptionTime, DEFAULT_SUBSCRIPTION_TIME_NS);
24 | }
25 |
26 | public ChannelMessage(String channelName, int subscriptionTime, int subscriptionTimeNS){
27 | this.channelName = channelName;
28 | this.subscriptionTime = subscriptionTime;
29 | this.subscriptionTimeNS = subscriptionTimeNS;
30 | }
31 |
32 | public int getSubscriptionTime() {
33 | return subscriptionTime;
34 | }
35 |
36 | @Override
37 | public int getMessageLength() {
38 | return channelName.getBytes().length;
39 | }
40 |
41 | @Override
42 | public String getMessageType() {
43 | return Message.MESSAGE_CHANNEL;
44 | }
45 |
46 | @Override
47 | public String toString() {
48 | return String.join(
49 | "|",
50 | getMessageType(),
51 | String.valueOf(getMessageLength()),
52 | this.channelName,
53 | String.valueOf(this.subscriptionTime),
54 | String.valueOf(this.subscriptionTimeNS)
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/DisconnectMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.Message;
4 |
5 | /*
6 | * 断开连接 封装
7 | * */
8 | public class DisconnectMessage implements Message {
9 | private final String text;
10 |
11 | public DisconnectMessage(String text){
12 | this.text = text;
13 | }
14 |
15 | @Override
16 | public String getMessageType() {
17 | return Message.MESSAGE_DISCONNECT;
18 | }
19 |
20 | @Override
21 | public int getMessageLength() {
22 | return text.getBytes().length;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return String.join("|", getMessageType(), String.valueOf(getMessageLength()), text);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/HelloMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.Message;
4 |
5 | /*
6 | * 握手消息 封装
7 | * */
8 | public class HelloMessage implements Message {
9 | private final String text;
10 |
11 | public HelloMessage(String text){
12 | this.text = text;
13 | }
14 |
15 | @Override
16 | public String getMessageType() {
17 | return Message.MESSAGE_HELLO;
18 | }
19 |
20 | @Override
21 | public int getMessageLength() {
22 | return text.getBytes().length;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return String.join("|", getMessageType(), String.valueOf(getMessageLength()), text);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/ReceiveMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.Message;
4 |
5 | /*
6 | * 消息接收数据 封装
7 | * */
8 | public class ReceiveMessage implements Message {
9 | private final String text;
10 | private final String channelName;
11 | private final String form;
12 |
13 | public static ReceiveMessage valueOf(String source){
14 | String[] data = source.split("\\|",7);
15 |
16 | if (!data[0].equals(MESSAGE_RECEIVE)){
17 | throw new IllegalArgumentException("协议头有误");
18 | }
19 | if (data[2].getBytes().length != Integer.parseInt(data[1])){
20 | throw new IllegalArgumentException("协议头有误");
21 | }
22 |
23 | if (data[4].getBytes().length != Integer.parseInt(data[3])){
24 | throw new IllegalArgumentException("协议头有误");
25 | }
26 |
27 | if (data[6].getBytes().length != Integer.parseInt(data[5])){
28 | throw new IllegalArgumentException("协议头有误");
29 | }
30 |
31 | return new ReceiveMessage(data[2], data[4], data[6]);
32 | }
33 |
34 | private ReceiveMessage(String form, String channelName, String text){
35 | this.form = form;
36 | this.channelName = channelName;
37 | this.text = text;
38 | }
39 |
40 | public String getForm() {
41 | return form;
42 | }
43 |
44 | public String getChannelName() {
45 | return channelName;
46 | }
47 |
48 | public String getText() {
49 | return text;
50 | }
51 |
52 | @Override
53 | public int getMessageLength() {
54 | return text.getBytes().length;
55 | }
56 |
57 | @Override
58 | public String getMessageType() {
59 | return Message.MESSAGE_RECEIVE;
60 | }
61 |
62 | @Override
63 | public String toString() {
64 | return String.join(
65 | "|",
66 | getMessageType(),
67 | String.valueOf(form.getBytes().length),
68 | getForm(),
69 | String.valueOf(channelName.getBytes().length),
70 | getChannelName(),
71 | String.valueOf(text.getBytes().length),
72 | getText()
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/api/message/TransmitMessage.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.api.message;
2 |
3 | import net.easecation.easechat.api.Message;
4 |
5 | /*
6 | * 发送消息 封装
7 | * */
8 | public class TransmitMessage implements Message {
9 |
10 | private final String channelName;
11 | private final String text;
12 |
13 | public TransmitMessage(String channelName, String text){
14 | if (channelName == null || channelName.isEmpty()){
15 | throw new IllegalArgumentException("channel 不能为空");
16 | }
17 |
18 | if (text == null || text.isEmpty()){
19 | throw new IllegalArgumentException("text 不能为空");
20 | }
21 |
22 | this.channelName = channelName;
23 | this.text = text;
24 | }
25 |
26 | @Override
27 | public int getMessageLength() {
28 | return text.getBytes().length;
29 | }
30 |
31 | @Override
32 | public String getMessageType() {
33 | return Message.MESSAGE_TRANSMIT;
34 | }
35 |
36 | @Override
37 | public String toString() {
38 | return String.join(
39 | "|",
40 | getMessageType(),
41 | String.valueOf(this.channelName.getBytes().length),
42 | this.channelName,
43 | String.valueOf(getMessageLength()),
44 | text
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/network/EaseChatClient.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.network;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.*;
5 | import io.netty.channel.nio.NioEventLoopGroup;
6 | import io.netty.channel.socket.SocketChannel;
7 | import io.netty.channel.socket.nio.NioSocketChannel;
8 | import io.netty.handler.codec.http.*;
9 | import io.netty.handler.codec.http.websocketx.*;
10 |
11 | import net.easecation.easechat.api.*;
12 | import net.easecation.easechat.api.message.ChannelMessage;
13 | import net.easecation.easechat.api.message.DisconnectMessage;
14 |
15 | import java.net.URI;
16 | import java.util.ArrayList;
17 | import java.util.Arrays;
18 | import java.util.List;
19 | import java.util.function.Consumer;
20 |
21 | public class EaseChatClient {
22 |
23 | private EventLoopGroup loopGroup;
24 | private Channel channel = null;
25 | private boolean isHandshake = false;
26 | private MessageSender sender;
27 | private final MessageReceiver receiver;
28 | private Logger logger = new SimpleLogger();
29 | private final URI websocketURI;
30 | private final List initChannelMessages = new ArrayList<>();
31 |
32 | /*
33 | * name 用与 向服务端发起1h握手协议时必须带的参数
34 | * */
35 | public EaseChatClient(String name, URI websocketURI, MessageReceiver.Listener listener) {
36 | if (name == null || name.isEmpty()) throw new IllegalArgumentException("带个 name 参数啊");
37 | this.name = name;
38 | if (websocketURI == null) {
39 | throw new IllegalArgumentException("url不能为空");
40 | }
41 |
42 | this.websocketURI = websocketURI;
43 | this.receiver = new MessageReceiver(this, listener);
44 | }
45 |
46 | public EaseChatClient(String name, URI websocketURI, ChannelMessage[] messages, MessageReceiver.Listener listener) {
47 | this(name, websocketURI, listener);
48 | this.initChannelMessages.addAll(Arrays.asList(messages));
49 | }
50 |
51 | public Logger getLogger() {
52 | return logger;
53 | }
54 |
55 | public void setLogger(Logger logger) {
56 | this.logger = logger;
57 | }
58 |
59 | private final String name;
60 |
61 | String getName() {
62 | return name;
63 | }
64 |
65 | public MessageSender getSender() {
66 | return sender;
67 | }
68 |
69 | MessageReceiver getReceiver() {
70 | return receiver;
71 | }
72 |
73 | public boolean isHandshake() {
74 | return isHandshake;
75 | }
76 |
77 | public void setHandshake(boolean handshake) {
78 | isHandshake = handshake;
79 | }
80 |
81 | public List getInitChannelMessages() {
82 | return initChannelMessages;
83 | }
84 |
85 | public void start() throws Exception {
86 | start(null);
87 | }
88 |
89 | public void start(Consumer setup) throws Exception {
90 | Bootstrap bootstrap = new Bootstrap();
91 | if (setup != null) {
92 | setup.accept(bootstrap);
93 | }
94 | if (bootstrap.config().group() == null) {
95 | loopGroup = new NioEventLoopGroup();
96 | bootstrap.group(loopGroup)
97 | .channel(NioSocketChannel.class);
98 | }
99 | bootstrap
100 | .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(1024 * 1024))
101 | .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
102 | .handler(new ChannelInitializer() {
103 | @Override
104 | protected void initChannel(SocketChannel channel) {
105 | channel.pipeline()
106 | .addLast(new HttpClientCodec())
107 | .addLast(new HttpObjectAggregator(65535))
108 | .addLast(new WebSocketFrameAggregator(65535))
109 | .addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(websocketURI, WebSocketVersion.V13, null, true, new DefaultHttpHeaders())))
110 | .addLast(new MessageCodec())
111 | .addLast(new MessageHandler(EaseChatClient.this));
112 | }
113 | });
114 | try {
115 | this.channel = bootstrap.connect(websocketURI.getHost(), websocketURI.getPort()).sync().channel();
116 | this.sender = new MessageSender(this, channel);
117 | getLogger().info("Nexus WebSocket 连接成功");
118 | } catch (Exception e) {
119 | getLogger().warning("Nexus WebSocket 连接失败");
120 | getLogger().warning(e.getMessage());
121 | throw e;
122 | }
123 | }
124 |
125 | /*
126 | * 关闭EaseChatClient 同步
127 | * */
128 | public boolean shutdown() {
129 | if (this.getSender() != null) {
130 | this.getSender().sendSyncMessage(new DisconnectMessage("shutdown"));
131 | try {
132 | channel.closeFuture().sync().isSuccess();
133 | } catch (InterruptedException e) {
134 | e.printStackTrace();
135 | return false;
136 | }
137 | }
138 |
139 | if (loopGroup == null) {
140 | return true;
141 | }
142 | return loopGroup.shutdownGracefully().isSuccess();
143 | }
144 |
145 | public boolean isActive() {
146 | return channel != null && channel.isActive();
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/easechat-client-j/src/main/java/net/easecation/easechat/network/MessageCodec.java:
--------------------------------------------------------------------------------
1 | package net.easecation.easechat.network;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.handler.codec.MessageToMessageCodec;
5 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
6 | import net.easecation.easechat.api.Message;
7 | import net.easecation.easechat.api.message.ReceiveMessage;
8 |
9 | import java.util.List;
10 |
11 | public class MessageCodec extends MessageToMessageCodec{
12 | @Override
13 | protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame frame, List