├── .gitignore ├── LICENSE ├── LightComm4J ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── github │ │ └── luohaha │ │ ├── client │ │ └── LightCommClient.java │ │ ├── connection │ │ ├── Conn.java │ │ ├── Connection.java │ │ └── DataBag.java │ │ ├── context │ │ ├── Context.java │ │ └── ContextBean.java │ │ ├── exception │ │ └── ConnectionCloseException.java │ │ ├── handler │ │ └── IoHandler.java │ │ ├── inter │ │ ├── OnAccept.java │ │ ├── OnAcceptError.java │ │ ├── OnClose.java │ │ ├── OnConnect.java │ │ ├── OnConnectError.java │ │ ├── OnRead.java │ │ ├── OnReadError.java │ │ ├── OnWrite.java │ │ └── OnWriteError.java │ │ ├── param │ │ ├── ClientParam.java │ │ ├── Param.java │ │ └── ServerParam.java │ │ ├── rpc │ │ ├── FunctionAndParam.java │ │ ├── IRpcFunction.java │ │ ├── RpcClient.java │ │ └── RpcServer.java │ │ ├── server │ │ └── LightCommServer.java │ │ ├── tools │ │ └── ObjectAndByteArray.java │ │ └── worker │ │ ├── Accepter.java │ │ ├── Connector.java │ │ ├── IoWorker.java │ │ ├── JobBean.java │ │ └── Worker.java │ └── test │ └── java │ └── com │ └── github │ └── luohaha │ ├── chatroom │ ├── ChatRoomClient.java │ └── ChatRoomServer.java │ ├── rpc │ ├── AddRpc.java │ ├── RpcClientTest.java │ └── RpcServerTest.java │ └── server │ └── test │ ├── TestClient.java │ └── TestServer.java └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | # Directories # 14 | /build/ 15 | /bin/ 16 | target/ 17 | LightComm4J/target/ 18 | 19 | # OS Files # 20 | .DS_Store 21 | 22 | *.class 23 | 24 | # Package Files # 25 | *.jar 26 | *.war 27 | *.ear 28 | *.db 29 | 30 | ###################### 31 | # Windows 32 | ###################### 33 | 34 | # Windows image file caches 35 | Thumbs.db 36 | 37 | # Folder config file 38 | Desktop.ini 39 | 40 | ###################### 41 | # OSX 42 | ###################### 43 | 44 | .DS_Store 45 | .svn 46 | 47 | # Thumbnails 48 | ._* 49 | 50 | # Files that might appear on external disk 51 | .Spotlight-V100 52 | .Trashes 53 | 54 | 55 | ###################### 56 | # Eclipse 57 | ###################### 58 | 59 | *.pydevproject 60 | .project 61 | .metadata 62 | bin/** 63 | tmp/** 64 | tmp/**/* 65 | *.tmp 66 | *.bak 67 | *.swp 68 | *~.nib 69 | local.properties 70 | .classpath 71 | .settings/ 72 | .loadpath 73 | /src/main/resources/rebel.xml 74 | # External tool builders 75 | .externalToolBuilders/ 76 | 77 | # Locally stored "Eclipse launch configurations" 78 | *.launch 79 | 80 | # CDT-specific 81 | .cproject 82 | 83 | # PDT-specific 84 | .buildpath -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /LightComm4J/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.luohaha 5 | LightComm4J 6 | 1.0.0 7 | jar 8 | LightComm4J 9 | 10 | Yet another lightweight asynchronous network library for java 11 | http://maven.apache.org 12 | 13 | 14 | 15 | GNU AFFERO GENERAL PUBLIC LICENSE, Version 3 16 | https://www.gnu.org/licenses/agpl-3.0.html 17 | 18 | 19 | 20 | 21 | 22 | luoyixin 23 | 18810541851@163.com 24 | 25 | 26 | 27 | 28 | scm:git@github.com:luohaha/LightComm4J.git 29 | scm:git@github.com:luohaha/LightComm4J.git 30 | git@github.com:luohaha/LightComm4J.git 31 | 32 | 33 | 34 | UTF-8 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 3.5.1 43 | 44 | 1.8 45 | 1.8 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | release 54 | 55 | 56 | oss 57 | https://oss.sonatype.org/content/repositories/snapshots/ 58 | 59 | 60 | oss 61 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-source-plugin 71 | 3.0.1 72 | 73 | 74 | package 75 | 76 | jar-no-fork 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-javadoc-plugin 85 | 2.10.4 86 | 87 | 88 | package 89 | 90 | jar 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-gpg-plugin 99 | 1.6 100 | 101 | 102 | sign-artifacts 103 | verify 104 | 105 | sign 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/client/LightCommClient.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.client; 2 | 3 | import java.util.logging.Logger; 4 | import com.github.luohaha.param.ClientParam; 5 | import com.github.luohaha.worker.Connector; 6 | import com.github.luohaha.worker.IoWorker; 7 | 8 | public class LightCommClient { 9 | private Connector connector; 10 | private int ioThreadPoolSize = 1; 11 | private Logger logger = Logger.getLogger("LightComm4J"); 12 | 13 | public LightCommClient(int ioThreadPoolSize) { 14 | 15 | this.ioThreadPoolSize = ioThreadPoolSize; 16 | this.connector = new Connector(); 17 | for (int i = 0; i < this.ioThreadPoolSize; i++) { 18 | IoWorker ioWorker = new IoWorker(i); 19 | connector.addWorker(ioWorker); 20 | new Thread(ioWorker).start(); 21 | this.logger.info("[IoWorker-" + i + "]" + " start..."); 22 | } 23 | new Thread(connector).start(); 24 | this.logger.info("[Connector]" + " start..."); 25 | } 26 | 27 | public void connect(String host, int port, ClientParam param) { 28 | this.connector.connect(host, port, param); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/connection/Conn.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.connection; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketAddress; 5 | import java.nio.channels.ClosedChannelException; 6 | 7 | import com.github.luohaha.exception.ConnectionCloseException; 8 | 9 | public interface Conn { 10 | public void write(byte[] data) throws ConnectionCloseException, ClosedChannelException; 11 | public void close() throws IOException; 12 | public void doClose() throws IOException; 13 | public SocketAddress getLocalAddress() throws IOException; 14 | public SocketAddress getRemoteAddress() throws IOException; 15 | public void setSendBuffer(int size) throws IOException; 16 | public void setRecvBuffer(int size) throws IOException; 17 | public void setKeepAlive(boolean flag) throws IOException; 18 | public void setReUseAddr(boolean flag) throws IOException; 19 | public void setNoDelay(boolean flag) throws IOException; 20 | public int getSendBuffer() throws IOException; 21 | public int getRecvBuffer() throws IOException; 22 | public boolean getKeepAlive() throws IOException; 23 | public boolean getReUseAddr() throws IOException; 24 | public boolean getNoDelay() throws IOException; 25 | } 26 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/connection/Connection.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.connection; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketAddress; 5 | import java.net.SocketOption; 6 | import java.net.StandardSocketOptions; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.ClosedChannelException; 9 | import java.nio.channels.SelectionKey; 10 | import java.nio.channels.Selector; 11 | import java.nio.channels.SocketChannel; 12 | import java.util.concurrent.BlockingQueue; 13 | import java.util.concurrent.LinkedBlockingQueue; 14 | import java.util.logging.Logger; 15 | 16 | import com.github.luohaha.context.Context; 17 | import com.github.luohaha.context.ContextBean; 18 | import com.github.luohaha.exception.ConnectionCloseException; 19 | 20 | public class Connection implements Conn { 21 | private Context context; 22 | private SocketChannel channel; 23 | private Selector selector; 24 | private BlockingQueue readyToWrite = new LinkedBlockingQueue<>(); 25 | // write function could be call just once 26 | private boolean onWriteCalled = false; 27 | private boolean readyToClose = false; 28 | private Logger logger = Logger.getLogger("LightComm4J"); 29 | 30 | public Connection(Context context, SocketChannel channel, Selector selector) { 31 | super(); 32 | this.context = context; 33 | this.channel = channel; 34 | this.selector = selector; 35 | } 36 | 37 | public synchronized void write(byte[] data) throws ConnectionCloseException, ClosedChannelException { 38 | if (readyToClose) 39 | throw new ConnectionCloseException(); 40 | ContextBean bean = context.getChanToContextBean().get(channel); 41 | ByteBuffer buffer = ByteBuffer.allocate(data.length + 4); 42 | buffer.putInt(data.length); 43 | buffer.put(data); 44 | buffer.flip(); 45 | readyToWrite.add(buffer); 46 | int ops = bean.getOps(); 47 | ops |= SelectionKey.OP_WRITE; 48 | bean.setOps(ops); 49 | this.channel.register(this.selector, ops); 50 | this.selector.wakeup(); 51 | } 52 | 53 | /** 54 | * set close flag 55 | * 56 | */ 57 | public void close() { 58 | this.readyToClose = true; 59 | if (this.readyToWrite.isEmpty()) { 60 | doClose(); 61 | } 62 | } 63 | 64 | /** 65 | * close channel 66 | */ 67 | public void doClose() { 68 | this.context.removeContextByChan(channel); 69 | try { 70 | this.channel.close(); 71 | } catch (IOException e) { 72 | this.logger.warning("[Close Event] : " + e.toString()); 73 | } 74 | } 75 | 76 | public BlockingQueue getReadyToWrite() { 77 | return readyToWrite; 78 | } 79 | 80 | public boolean isOnWriteCalled() { 81 | return onWriteCalled; 82 | } 83 | 84 | public void setOnWriteCalled(boolean onWriteCalled) { 85 | this.onWriteCalled = onWriteCalled; 86 | } 87 | 88 | public boolean isReadyToClose() { 89 | return readyToClose; 90 | } 91 | 92 | public void setReadyToClose(boolean readyToClose) { 93 | this.readyToClose = readyToClose; 94 | } 95 | 96 | @Override 97 | public SocketAddress getLocalAddress() throws IOException { 98 | return this.channel.getLocalAddress(); 99 | } 100 | 101 | @Override 102 | public SocketAddress getRemoteAddress() throws IOException { 103 | return this.channel.getRemoteAddress(); 104 | } 105 | 106 | /** 107 | * set send buffer's size 108 | */ 109 | public void setSendBuffer(int size) throws IOException { 110 | this.channel.setOption(StandardSocketOptions.SO_SNDBUF, size); 111 | } 112 | 113 | /** 114 | * get send buffer' size 115 | * 116 | * @return 117 | * SO_SNDBUF 118 | * @throws IOException 119 | * IOException 120 | */ 121 | public int getSendBuffer() throws IOException { 122 | return this.channel.getOption(StandardSocketOptions.SO_SNDBUF); 123 | } 124 | 125 | /** 126 | * set recv buffer's size 127 | */ 128 | public void setRecvBuffer(int size) throws IOException { 129 | this.channel.setOption(StandardSocketOptions.SO_RCVBUF, size); 130 | } 131 | 132 | /** 133 | * get recv buffer's size 134 | * 135 | * @return 136 | * SO_RCVBUF 137 | * @throws IOException 138 | * IOException 139 | */ 140 | public int getRecvBuffer() throws IOException { 141 | return this.channel.getOption(StandardSocketOptions.SO_RCVBUF); 142 | } 143 | 144 | /** 145 | * set keep alive 146 | */ 147 | public void setKeepAlive(boolean flag) throws IOException { 148 | this.channel.setOption(StandardSocketOptions.SO_KEEPALIVE, flag); 149 | } 150 | 151 | /** 152 | * get keep alive 153 | * 154 | * @return 155 | * SO_KEEPALIVE 156 | * @throws IOException 157 | * IOException 158 | */ 159 | public boolean getKeepAlive() throws IOException { 160 | return this.channel.getOption(StandardSocketOptions.SO_KEEPALIVE); 161 | } 162 | 163 | /** 164 | * set reuse address 165 | */ 166 | public void setReUseAddr(boolean flag) throws IOException { 167 | this.channel.setOption(StandardSocketOptions.SO_REUSEADDR, flag); 168 | } 169 | 170 | /** 171 | * get reuse address 172 | * 173 | * @return 174 | * SO_REUSEADDR 175 | * @throws IOException 176 | * IOException 177 | */ 178 | public boolean getReUseAddr() throws IOException { 179 | return this.channel.getOption(StandardSocketOptions.SO_REUSEADDR); 180 | } 181 | 182 | /** 183 | * set no delay 184 | */ 185 | public void setNoDelay(boolean flag) throws IOException { 186 | this.channel.setOption(StandardSocketOptions.TCP_NODELAY, flag); 187 | } 188 | 189 | /** 190 | * get no delay 191 | * 192 | * @return 193 | * TCP_NODELAY 194 | * @throws IOException 195 | * IOException 196 | */ 197 | public boolean getNoDelay() throws IOException { 198 | return this.channel.getOption(StandardSocketOptions.TCP_NODELAY); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/connection/DataBag.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.connection; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.ArrayDeque; 5 | import java.util.Queue; 6 | 7 | public class DataBag { 8 | 9 | private byte[] tmpBuffer; 10 | private int size; 11 | private int pos; 12 | private int remainToRead = -1; 13 | private boolean isFinish = false; 14 | private static final int CHUNK_SIZE = 1024; 15 | 16 | public DataBag() { 17 | this.size = CHUNK_SIZE; 18 | this.pos = 0; 19 | tmpBuffer = new byte[this.size]; 20 | } 21 | 22 | /** 23 | * read data from byte buffer 24 | * @param buffer 25 | * read from this buffer 26 | * @return 27 | * buffer's current position 28 | */ 29 | public int readFrom(ByteBuffer buffer) { 30 | int start = buffer.position(); 31 | byte[] data = new byte[buffer.remaining()]; 32 | buffer.get(data); 33 | int pos = readFromBytes(data); 34 | buffer.position(start + pos); 35 | return pos; 36 | } 37 | 38 | private int readFromBytes(byte[] data) { 39 | // if buffer's size isn't enough 40 | while (this.pos + data.length > this.size) { 41 | extendMemory(); 42 | } 43 | if (remainToRead == -1) { 44 | // begin to get data 45 | if (this.pos + data.length >= 4) { 46 | // data's position 47 | int i = 0; 48 | for (; this.pos < 4; i++) { 49 | tmpBuffer[this.pos++] = data[i]; 50 | } 51 | remainToRead = byteToInteger(tmpBuffer); 52 | for (; i < data.length && remainToRead > 0; i++) { 53 | tmpBuffer[this.pos++] = data[i]; 54 | remainToRead--; 55 | } 56 | if (remainToRead == 0) { 57 | this.isFinish = true; 58 | } 59 | return i; 60 | } else { 61 | for (int i = 0; i < data.length; i++) { 62 | tmpBuffer[this.pos++] = data[i]; 63 | } 64 | return data.length; 65 | } 66 | } else { 67 | // continue to get data 68 | if (isFinish) 69 | return 0; 70 | // data's position 71 | int i = 0; 72 | for (; i < data.length && remainToRead > 0; i++) { 73 | tmpBuffer[this.pos++] = data[i]; 74 | remainToRead--; 75 | } 76 | if (remainToRead == 0) { 77 | this.isFinish = true; 78 | } 79 | return i; 80 | } 81 | } 82 | 83 | public boolean isFinish() { 84 | return isFinish; 85 | } 86 | 87 | /** 88 | * get bytebuffer from tmpBuffer, when it finish 89 | * @return 90 | * buffer 91 | */ 92 | public ByteBuffer getByteBuffer() { 93 | if (isFinish()) { 94 | int length = byteToInteger(tmpBuffer); 95 | ByteBuffer buffer = ByteBuffer.allocate(length); 96 | buffer.put(tmpBuffer, 4, length); 97 | return buffer; 98 | } else { 99 | return null; 100 | } 101 | } 102 | 103 | /** 104 | * get bytes from tmpBuffer, when it finish 105 | * @return 106 | * return byte array from tmpBuffer 107 | */ 108 | public byte[] getBytes() { 109 | if (!isFinish()) 110 | return null; 111 | byte[] res = new byte[this.pos - 4]; 112 | for (int i = 0; i < res.length; i++) { 113 | res[i] = this.tmpBuffer[i + 4]; 114 | } 115 | return res; 116 | } 117 | 118 | /** 119 | * byte转int,采用小端字节序 120 | * @param data 121 | * byte 122 | * @return 123 | * int 124 | */ 125 | private int byteToInteger(byte[] data) { 126 | return byteToIntegerFromPos(data, 0); 127 | } 128 | 129 | /** 130 | * byte转int,采用小端字节序,从start位置开始 131 | * @param data 132 | * byte 133 | * @param start 134 | * start position 135 | * @return 136 | * int 137 | */ 138 | private int byteToIntegerFromPos(byte[] data, int start) { 139 | return data[start + 3] & 0xff | 140 | (data[start + 2] & 0xff) << 8 | 141 | (data[start + 1] & 0xff) << 16 | 142 | (data[start + 0] & 0xff) << 24; 143 | } 144 | 145 | /** 146 | * extend tmpBuffer's size 147 | */ 148 | private void extendMemory() { 149 | this.size += CHUNK_SIZE; 150 | byte[] newBuffer = new byte[this.size]; 151 | for (int i = 0; i < this.pos; i++) { 152 | newBuffer[i] = tmpBuffer[i]; 153 | } 154 | this.tmpBuffer = newBuffer; 155 | } 156 | 157 | /* 158 | public static void main(String[] args) { 159 | ByteBuffer buffer = ByteBuffer.allocate(16); 160 | buffer.putInt(45); 161 | buffer.putInt(34); 162 | buffer.putInt(4); 163 | buffer.flip(); 164 | DataBag bag = new DataBag(); 165 | byte[] res = new byte[12]; 166 | buffer.get(res); 167 | 168 | }*/ 169 | } 170 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/context/Context.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.context; 2 | 3 | import java.nio.channels.SocketChannel; 4 | import java.util.ArrayDeque; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import com.github.luohaha.connection.Connection; 9 | import com.github.luohaha.connection.DataBag; 10 | import com.github.luohaha.param.Param; 11 | 12 | public class Context { 13 | 14 | private Map chanToContextBean = new HashMap<>(); 15 | 16 | public Context() { 17 | 18 | } 19 | 20 | public Map getChanToContextBean() { 21 | return chanToContextBean; 22 | } 23 | 24 | public void setChanToContextBean(Map chanToContextBean) { 25 | this.chanToContextBean = chanToContextBean; 26 | } 27 | 28 | /** 29 | * init this channel's context 30 | * @param channel 31 | * channel 32 | * @param connection 33 | * connection 34 | * @param ops 35 | * operations 36 | * @param param 37 | * param 38 | */ 39 | public void initContext(SocketChannel channel, Connection connection, int ops, Param param) { 40 | ContextBean bean = new ContextBean(connection, new ArrayDeque<>(), new DataBag(), ops, param); 41 | this.chanToContextBean.put(channel, bean); 42 | } 43 | 44 | /** 45 | * remove this channel's context 46 | * @param channel 47 | * channel 48 | */ 49 | public void removeContextByChan(SocketChannel channel) { 50 | this.chanToContextBean.remove(channel); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/context/ContextBean.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.context; 2 | 3 | import java.util.Queue; 4 | 5 | import com.github.luohaha.connection.Connection; 6 | import com.github.luohaha.connection.DataBag; 7 | import com.github.luohaha.param.Param; 8 | 9 | public class ContextBean { 10 | // a connection 11 | private Connection connection; 12 | // already read 13 | private Queue alreadyReadData; 14 | // ready to read 15 | private DataBag readyToRead; 16 | // operations 17 | private int ops; 18 | // params 19 | private Param param; 20 | public ContextBean(Connection connection, Queue alreadyReadData, DataBag readyToRead, int ops, 21 | Param param) { 22 | super(); 23 | this.connection = connection; 24 | this.alreadyReadData = alreadyReadData; 25 | this.readyToRead = readyToRead; 26 | this.ops = ops; 27 | this.param = param; 28 | } 29 | public Connection getConnection() { 30 | return connection; 31 | } 32 | public void setConnection(Connection connection) { 33 | this.connection = connection; 34 | } 35 | public Queue getAlreadyReadData() { 36 | return alreadyReadData; 37 | } 38 | public void setAlreadyReadData(Queue alreadyReadData) { 39 | this.alreadyReadData = alreadyReadData; 40 | } 41 | public DataBag getReadyToRead() { 42 | return readyToRead; 43 | } 44 | public void setReadyToRead(DataBag readyToRead) { 45 | this.readyToRead = readyToRead; 46 | } 47 | public int getOps() { 48 | return ops; 49 | } 50 | public void setOps(int ops) { 51 | this.ops = ops; 52 | } 53 | public Param getParam() { 54 | return param; 55 | } 56 | public void setParam(Param param) { 57 | this.param = param; 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/exception/ConnectionCloseException.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.exception; 2 | 3 | public class ConnectionCloseException extends Exception { 4 | public ConnectionCloseException() { 5 | super("connection already close!"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/handler/IoHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.handler; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.ClosedChannelException; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.SocketChannel; 9 | import java.util.Queue; 10 | import java.util.logging.Logger; 11 | 12 | import com.github.luohaha.connection.Connection; 13 | import com.github.luohaha.connection.DataBag; 14 | import com.github.luohaha.context.Context; 15 | import com.github.luohaha.context.ContextBean; 16 | import com.github.luohaha.inter.OnClose; 17 | import com.github.luohaha.inter.OnRead; 18 | import com.github.luohaha.inter.OnWrite; 19 | 20 | public class IoHandler { 21 | private Logger logger = Logger.getLogger("LightComm4J"); 22 | private Context context; 23 | private Selector selector; 24 | private static final int BUFFER_SIZE = 1024; 25 | 26 | public IoHandler(Selector selector, Context context) { 27 | super(); 28 | this.context = context; 29 | this.selector = selector; 30 | } 31 | 32 | /** 33 | * read data from remote site by channel 34 | * 35 | * @param channel 36 | * channel 37 | * @param onRead 38 | * onRead 39 | * @param onClose 40 | * onClose 41 | * @throws IOException 42 | * ioexception 43 | */ 44 | public void readDataFromRemoteSite(SocketChannel channel, OnRead onRead, OnClose onClose) throws IOException { 45 | // store current data 46 | ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); 47 | ContextBean bean = this.context.getChanToContextBean().get(channel); 48 | // read from remote side 49 | int count = channel.read(buffer); 50 | if (count >= 0) { 51 | // set buffer's position to 0 52 | buffer.flip(); 53 | while (buffer.hasRemaining()) { 54 | DataBag bag = bean.getReadyToRead(); 55 | bag.readFrom(buffer); 56 | if (bag.isFinish()) { 57 | // finish read one data bag 58 | bean.getAlreadyReadData().add(bag.getBytes()); 59 | bean.setReadyToRead(new DataBag()); 60 | } 61 | } 62 | // call user's custom function 63 | Queue dataQueue = bean.getAlreadyReadData(); 64 | while (!dataQueue.isEmpty()) { 65 | onRead.onRead(bean.getConnection(), dataQueue.poll()); 66 | } 67 | } else { 68 | // read end 69 | try { 70 | closeRead(channel); 71 | } catch (ClosedChannelException e) { 72 | this.logger.warning("[Read Event] : " + e.toString()); 73 | throw new IOException(); 74 | } 75 | if (onClose != null) 76 | onClose.onClose(bean.getConnection()); 77 | } 78 | } 79 | 80 | /** 81 | * write data to remote site 82 | * 83 | * @param channel 84 | * channnel 85 | * @param onWrite 86 | * onWrite 87 | * @throws IOException 88 | * IOException 89 | */ 90 | public void writeDataToRemoteSite(SocketChannel channel, OnWrite onWrite) throws IOException { 91 | ContextBean bean = this.context.getChanToContextBean().get(channel); 92 | Connection connection = bean.getConnection(); 93 | // call write function when user define such function and haven't call 94 | // it yet! 95 | if (onWrite != null && !connection.isOnWriteCalled()) { 96 | connection.setOnWriteCalled(true); 97 | onWrite.onWrite(connection); 98 | } 99 | 100 | ByteBuffer buffer = connection.getReadyToWrite().peek(); 101 | if (buffer != null) { 102 | if (buffer.hasRemaining()) { 103 | channel.write(buffer); 104 | } 105 | // if this buffer finish write to buffer, delete it from queue 106 | if (!buffer.hasRemaining()) { 107 | connection.getReadyToWrite().poll(); 108 | } 109 | } 110 | 111 | // nothing to write 112 | if (connection.getReadyToWrite().isEmpty()) { 113 | try { 114 | closeWrite(channel); 115 | } catch (ClosedChannelException e) { 116 | this.logger.warning("[Write Event] : " + e.toString()); 117 | throw new IOException(); 118 | } 119 | if (connection.isReadyToClose()) { 120 | connection.doClose(); 121 | } 122 | return; 123 | } 124 | } 125 | 126 | /** 127 | * close write event 128 | * 129 | * @param channel 130 | * channel 131 | * @throws ClosedChannelException 132 | * ClosedChannelException 133 | */ 134 | private void closeWrite(SocketChannel channel) throws ClosedChannelException { 135 | closeOps(channel, SelectionKey.OP_WRITE); 136 | } 137 | 138 | /** 139 | * close read event 140 | * 141 | * @param channel 142 | * channel 143 | * @throws ClosedChannelException 144 | * ClosedChannelException 145 | */ 146 | private void closeRead(SocketChannel channel) throws ClosedChannelException { 147 | closeOps(channel, SelectionKey.OP_READ); 148 | } 149 | 150 | /** 151 | * close some operations 152 | * 153 | * @param channel 154 | * channel 155 | * @param opsToClose 156 | * opsToClose 157 | * @throws ClosedChannelException 158 | * ClosedChannelException 159 | */ 160 | private void closeOps(SocketChannel channel, int opsToClose) throws ClosedChannelException { 161 | ContextBean bean = this.context.getChanToContextBean().get(channel); 162 | int ops = bean.getOps(); 163 | ops = (~opsToClose) & ops; 164 | bean.setOps(ops); 165 | channel.register(this.selector, ops); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnAccept.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnAccept { 6 | public void onAccept(Conn connection); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnAcceptError.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | public interface OnAcceptError { 4 | public void onAcceptError(Exception e); 5 | } 6 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnClose.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnClose { 6 | public void onClose(Conn conn); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnConnect.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnConnect { 6 | public void onConnect(Conn connection); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnConnectError.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | public interface OnConnectError { 4 | public void onConnectError(Exception e); 5 | } 6 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnRead.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnRead { 6 | public void onRead(Conn connection, byte[] data); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnReadError.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnReadError { 6 | public void onReadError(Conn conn, Exception e); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnWrite.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnWrite { 6 | public void onWrite(Conn connection); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/inter/OnWriteError.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.inter; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | 5 | public interface OnWriteError { 6 | public void onWriteError(Conn conn, Exception e); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/param/ClientParam.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.param; 2 | 3 | import com.github.luohaha.inter.OnAccept; 4 | import com.github.luohaha.inter.OnClose; 5 | import com.github.luohaha.inter.OnConnectError; 6 | import com.github.luohaha.inter.OnConnect; 7 | import com.github.luohaha.inter.OnRead; 8 | import com.github.luohaha.inter.OnWrite; 9 | 10 | public class ClientParam extends Param { 11 | private OnConnect onConnect; 12 | private OnConnectError onConnectError; 13 | 14 | public ClientParam() { 15 | // 16 | } 17 | 18 | public OnConnect getOnConnect() { 19 | return onConnect; 20 | } 21 | 22 | public void setOnConnect(OnConnect onConnect) { 23 | this.onConnect = onConnect; 24 | } 25 | 26 | public OnConnectError getOnConnectError() { 27 | return onConnectError; 28 | } 29 | 30 | public void setOnConnectError(OnConnectError onConnectError) { 31 | this.onConnectError = onConnectError; 32 | } 33 | 34 | 35 | 36 | @Override 37 | public OnAccept getOnAccept() { 38 | return null; 39 | } 40 | 41 | @Override 42 | public OnConnect getOnConnection() { 43 | return this.onConnect; 44 | } 45 | 46 | 47 | @Override 48 | public boolean isServerParam() { 49 | return false; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/param/Param.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.param; 2 | 3 | import java.util.logging.ConsoleHandler; 4 | import java.util.logging.Handler; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | import com.github.luohaha.inter.OnAccept; 9 | import com.github.luohaha.inter.OnClose; 10 | import com.github.luohaha.inter.OnConnectError; 11 | import com.github.luohaha.inter.OnConnect; 12 | import com.github.luohaha.inter.OnRead; 13 | import com.github.luohaha.inter.OnReadError; 14 | import com.github.luohaha.inter.OnWrite; 15 | import com.github.luohaha.inter.OnWriteError; 16 | 17 | public abstract class Param { 18 | 19 | // logger 20 | private Logger logger = Logger.getLogger("LightComm4J"); 21 | 22 | private OnRead onRead; 23 | private OnReadError onReadError; 24 | private OnWrite onWrite; 25 | private OnWriteError onWriteError; 26 | private OnClose onClose; 27 | 28 | public OnReadError getOnReadError() { 29 | return onReadError; 30 | } 31 | public void setOnReadError(OnReadError onReadError) { 32 | this.onReadError = onReadError; 33 | } 34 | public OnWriteError getOnWriteError() { 35 | return onWriteError; 36 | } 37 | public void setOnWriteError(OnWriteError onWriteError) { 38 | this.onWriteError = onWriteError; 39 | } 40 | public OnRead getOnRead() { 41 | return onRead; 42 | } 43 | public void setOnRead(OnRead onRead) { 44 | this.onRead = onRead; 45 | } 46 | public OnWrite getOnWrite() { 47 | return onWrite; 48 | } 49 | public void setOnWrite(OnWrite onWrite) { 50 | this.onWrite = onWrite; 51 | } 52 | public OnClose getOnClose() { 53 | return onClose; 54 | } 55 | public void setOnClose(OnClose onClose) { 56 | this.onClose = onClose; 57 | } 58 | 59 | public void addLogHandler(Handler handler) { 60 | this.logger.addHandler(handler); 61 | } 62 | 63 | public void setLogLevel(Level level) { 64 | this.logger.setLevel(level); 65 | } 66 | 67 | public Logger getLogger() { 68 | return logger; 69 | } 70 | public abstract OnAccept getOnAccept(); 71 | public abstract OnConnect getOnConnection(); 72 | public abstract boolean isServerParam(); 73 | } 74 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/param/ServerParam.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.param; 2 | 3 | import com.github.luohaha.inter.OnAccept; 4 | import com.github.luohaha.inter.OnAcceptError; 5 | import com.github.luohaha.inter.OnClose; 6 | import com.github.luohaha.inter.OnConnect; 7 | import com.github.luohaha.inter.OnRead; 8 | import com.github.luohaha.inter.OnWrite; 9 | 10 | public class ServerParam extends Param { 11 | private String host; 12 | private int port; 13 | private int backlog = 32; 14 | private OnAccept onAccept; 15 | private OnAcceptError onAcceptError; 16 | 17 | public ServerParam(String host, int port) { 18 | this.host = host; 19 | this.port = port; 20 | } 21 | 22 | public int getBacklog() { 23 | return backlog; 24 | } 25 | 26 | public void setBacklog(int backlog) { 27 | this.backlog = backlog; 28 | } 29 | 30 | 31 | public void setOnAccept(OnAccept onAccept) { 32 | this.onAccept = onAccept; 33 | } 34 | 35 | public String getHost() { 36 | return host; 37 | } 38 | 39 | public void setHost(String host) { 40 | this.host = host; 41 | } 42 | 43 | public int getPort() { 44 | return port; 45 | } 46 | 47 | public void setPort(int port) { 48 | this.port = port; 49 | } 50 | 51 | @Override 52 | public OnAccept getOnAccept() { 53 | return this.onAccept; 54 | } 55 | 56 | public OnAcceptError getOnAcceptError() { 57 | return onAcceptError; 58 | } 59 | 60 | public void setOnAcceptError(OnAcceptError onAcceptError) { 61 | this.onAcceptError = onAcceptError; 62 | } 63 | 64 | @Override 65 | public OnConnect getOnConnection() { 66 | return null; 67 | } 68 | 69 | @Override 70 | public boolean isServerParam() { 71 | return true; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/rpc/FunctionAndParam.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | public class FunctionAndParam implements Serializable { 7 | private String function; 8 | private List params; 9 | 10 | public FunctionAndParam(String function, List params) { 11 | this.function = function; 12 | this.params = params; 13 | } 14 | 15 | public String getFunction() { 16 | return function; 17 | } 18 | 19 | public void setFunction(String function) { 20 | this.function = function; 21 | } 22 | 23 | public List getParams() { 24 | return params; 25 | } 26 | 27 | public void setParams(List params) { 28 | this.params = params; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/rpc/IRpcFunction.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import java.util.List; 4 | 5 | public interface IRpcFunction { 6 | public Object rpcCall(String function, List params); 7 | } 8 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/rpc/RpcClient.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import com.github.luohaha.client.LightCommClient; 4 | import com.github.luohaha.connection.Conn; 5 | import com.github.luohaha.exception.ConnectionCloseException; 6 | import com.github.luohaha.inter.OnConnectError; 7 | import com.github.luohaha.inter.OnRead; 8 | import com.github.luohaha.inter.OnReadError; 9 | import com.github.luohaha.inter.OnWriteError; 10 | import com.github.luohaha.param.ClientParam; 11 | import com.github.luohaha.tools.ObjectAndByteArray; 12 | 13 | import java.io.IOException; 14 | import java.nio.channels.ClosedChannelException; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.concurrent.CountDownLatch; 18 | import java.util.logging.Handler; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | public class RpcClient { 23 | 24 | private int ioThreadPoolSize = 1; 25 | private ClientParam param; 26 | private LightCommClient client; 27 | private String host; 28 | private int port; 29 | private Logger logger = Logger.getLogger("LightComm4J"); 30 | 31 | public RpcClient() { 32 | this.param = new ClientParam(); 33 | } 34 | 35 | public void start() { 36 | this.client = new LightCommClient(ioThreadPoolSize); 37 | } 38 | 39 | public void setLogLevel(Level level) { 40 | this.param.setLogLevel(level); 41 | } 42 | 43 | public void setIoThreadPoolSize(int ioThreadPoolSize) { 44 | this.ioThreadPoolSize = ioThreadPoolSize; 45 | } 46 | 47 | public void addLogHandler(Handler handler) { 48 | this.param.addLogHandler(handler); 49 | } 50 | 51 | public RpcClient remote(String host, int port) { 52 | this.host = host; 53 | this.port = port; 54 | return this; 55 | } 56 | 57 | /** 58 | * 远程调用,同步模式 59 | * @param function 60 | * 函数名 61 | * @param fparamList 62 | * 参数 63 | * @return 64 | * 返回结果 65 | */ 66 | public Object call(String function, Object... fparamList) { 67 | List fparams = new ArrayList<>(); 68 | for (Object each : fparamList) { 69 | fparams.add(each); 70 | } 71 | FunctionAndParam functionAndParam = new FunctionAndParam(function, fparams); 72 | this.param.setOnConnect(conn -> { 73 | try { 74 | conn.write(ObjectAndByteArray.toByteArray(functionAndParam)); 75 | } catch (ConnectionCloseException e) { 76 | this.logger.warning(e.toString()); 77 | } catch (ClosedChannelException e) { 78 | this.logger.warning(e.toString()); 79 | } 80 | }); 81 | RpcRead rpcRead = new RpcRead(); 82 | this.param.setOnRead(rpcRead); 83 | this.param.setOnConnectError(rpcRead); 84 | this.param.setOnReadError(rpcRead); 85 | this.param.setOnWriteError(rpcRead); 86 | this.client.connect(this.host, this.port, param); 87 | return rpcRead.getData(); 88 | } 89 | 90 | private class RpcRead implements OnRead, OnConnectError, OnReadError, OnWriteError { 91 | 92 | private Object data = null; 93 | private CountDownLatch countDownLatch = new CountDownLatch(1); 94 | 95 | @Override 96 | public void onRead(Conn connection, byte[] data) { 97 | this.data = ObjectAndByteArray.toObject(data); 98 | try { 99 | connection.doClose(); 100 | } catch (IOException e) { 101 | logger.warning(e.toString()); 102 | } 103 | countDownLatch.countDown(); 104 | } 105 | 106 | public Object getData() { 107 | do { 108 | try { 109 | countDownLatch.await(); 110 | break; 111 | } catch (InterruptedException e) { 112 | logger.warning(e.toString()); 113 | } 114 | } while (true); 115 | return data; 116 | } 117 | 118 | @Override 119 | public void onConnectError(Exception e) { 120 | logger.warning(e.toString()); 121 | countDownLatch.countDown(); 122 | } 123 | 124 | @Override 125 | public void onReadError(Conn conn, Exception e) { 126 | logger.warning(e.toString()); 127 | countDownLatch.countDown(); 128 | } 129 | 130 | @Override 131 | public void onWriteError(Conn conn, Exception e) { 132 | logger.warning(e.toString()); 133 | countDownLatch.countDown(); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/rpc/RpcServer.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import com.github.luohaha.connection.Conn; 4 | import com.github.luohaha.exception.ConnectionCloseException; 5 | import com.github.luohaha.inter.OnAcceptError; 6 | import com.github.luohaha.inter.OnReadError; 7 | import com.github.luohaha.inter.OnWriteError; 8 | import com.github.luohaha.param.ServerParam; 9 | import com.github.luohaha.server.LightCommServer; 10 | import com.github.luohaha.tools.ObjectAndByteArray; 11 | 12 | import java.io.IOException; 13 | import java.nio.channels.ClosedChannelException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.logging.Handler; 17 | import java.util.logging.Level; 18 | import java.util.logging.Logger; 19 | 20 | public class RpcServer { 21 | private Map functions = new HashMap<>(); 22 | private ServerParam param; 23 | private Logger logger = Logger.getLogger("LightComm4J"); 24 | private int ioThreadPoolSize = 1; 25 | 26 | public RpcServer(String host, int port) { 27 | this.param = new ServerParam(host, port); 28 | } 29 | 30 | public void start() { 31 | this.param.setOnRead((conn, data) -> { 32 | FunctionAndParam functionAndParam = (FunctionAndParam) ObjectAndByteArray.toObject(data); 33 | Object res = get(functionAndParam.getFunction()) 34 | .rpcCall(functionAndParam.getFunction(), functionAndParam.getParams()); 35 | try { 36 | conn.write(ObjectAndByteArray.toByteArray(res)); 37 | } catch (ConnectionCloseException e) { 38 | logger.warning(e.toString()); 39 | } catch (ClosedChannelException e) { 40 | logger.warning(e.toString()); 41 | } 42 | }); 43 | this.param.setOnClose(conn -> { 44 | try { 45 | conn.doClose(); 46 | } catch (IOException e) { 47 | this.logger.warning(e.toString()); 48 | } 49 | }); 50 | ErrorHandle errorHandle = new ErrorHandle(); 51 | this.param.setOnAcceptError(errorHandle); 52 | this.param.setOnReadError(errorHandle); 53 | this.param.setOnWriteError(errorHandle); 54 | LightCommServer server = new LightCommServer(param, ioThreadPoolSize); 55 | server.start(); 56 | } 57 | 58 | public void setBacklog(int backlog) { 59 | this.param.setBacklog(backlog); 60 | } 61 | 62 | public void setLogLevel(Level level) { 63 | this.param.setLogLevel(level); 64 | } 65 | 66 | public void setIoThreadPoolSize(int ioThreadPoolSize) { 67 | this.ioThreadPoolSize = ioThreadPoolSize; 68 | } 69 | 70 | public void addLogHandler(Handler handler) { 71 | this.param.addLogHandler(handler); 72 | } 73 | 74 | 75 | public void add(String function, IRpcFunction rpcFunction) { 76 | this.functions.put(function, rpcFunction); 77 | } 78 | 79 | public void remove(String function) { 80 | this.functions.remove(function); 81 | } 82 | 83 | public IRpcFunction get(String function) { 84 | return this.functions.get(function); 85 | } 86 | 87 | private class ErrorHandle implements OnAcceptError, OnReadError, OnWriteError { 88 | 89 | @Override 90 | public void onAcceptError(Exception e) { 91 | logger.warning(e.toString()); 92 | } 93 | 94 | @Override 95 | public void onReadError(Conn conn, Exception e) { 96 | logger.warning(e.toString()); 97 | try { 98 | conn.doClose(); 99 | } catch (IOException e1) { 100 | logger.warning(e1.toString()); 101 | } 102 | } 103 | 104 | @Override 105 | public void onWriteError(Conn conn, Exception e) { 106 | logger.warning(e.toString()); 107 | try { 108 | conn.doClose(); 109 | } catch (IOException e1) { 110 | logger.warning(e1.toString()); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/server/LightCommServer.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.server; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.ClosedChannelException; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.ServerSocketChannel; 8 | import java.nio.channels.SocketChannel; 9 | import java.util.ArrayDeque; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.Queue; 14 | import java.util.Set; 15 | import java.util.logging.Logger; 16 | import java.util.prefs.BackingStoreException; 17 | 18 | import com.github.luohaha.connection.Connection; 19 | import com.github.luohaha.connection.DataBag; 20 | import com.github.luohaha.handler.IoHandler; 21 | import com.github.luohaha.inter.OnAccept; 22 | import com.github.luohaha.inter.OnClose; 23 | import com.github.luohaha.inter.OnRead; 24 | import com.github.luohaha.inter.OnWrite; 25 | import com.github.luohaha.param.ServerParam; 26 | import com.github.luohaha.worker.Accepter; 27 | import com.github.luohaha.worker.IoWorker; 28 | 29 | public class LightCommServer { 30 | private ServerParam param; 31 | private int ioThreadPoolSize = 1; 32 | 33 | public LightCommServer(ServerParam serverParam, int ioThreadPoolSize) { 34 | this.param = serverParam; 35 | this.ioThreadPoolSize = ioThreadPoolSize; 36 | } 37 | 38 | /** 39 | * start server 40 | */ 41 | public void start() { 42 | Accepter accepter = new Accepter(this.param); 43 | for (int i = 0; i < ioThreadPoolSize; i++) { 44 | IoWorker ioWorker = new IoWorker(i); 45 | accepter.addIoWorker(ioWorker); 46 | new Thread(ioWorker).start(); 47 | this.param.getLogger().info("[IoWorker-" + i + "]" + " start..."); 48 | } 49 | new Thread(accepter).start(); 50 | this.param.getLogger().info("[Accepter] start..."); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/tools/ObjectAndByteArray.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.tools; 2 | 3 | import java.io.*; 4 | 5 | public class ObjectAndByteArray { 6 | 7 | public static byte[] toByteArray (Object obj) { 8 | byte[] bytes = null; 9 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 10 | try { 11 | ObjectOutputStream oos = new ObjectOutputStream(bos); 12 | oos.writeObject(obj); 13 | oos.flush(); 14 | bytes = bos.toByteArray (); 15 | oos.close(); 16 | bos.close(); 17 | } catch (IOException ex) { 18 | ex.printStackTrace(); 19 | } 20 | return bytes; 21 | } 22 | 23 | public static Object toObject (byte[] bytes) { 24 | Object obj = null; 25 | try { 26 | ByteArrayInputStream bis = new ByteArrayInputStream (bytes); 27 | ObjectInputStream ois = new ObjectInputStream (bis); 28 | obj = ois.readObject(); 29 | ois.close(); 30 | bis.close(); 31 | } catch (IOException ex) { 32 | ex.printStackTrace(); 33 | } catch (ClassNotFoundException ex) { 34 | ex.printStackTrace(); 35 | } 36 | return obj; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/worker/Accepter.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.worker; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.channels.ClosedChannelException; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.ArrayList; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | import com.github.luohaha.param.ServerParam; 16 | 17 | public class Accepter extends Worker implements Runnable { 18 | private ServerSocketChannel channel; 19 | private Selector selector; 20 | private ServerParam param; 21 | private List workers = new ArrayList<>(); 22 | private int workerIndex = 0; 23 | 24 | public Accepter(ServerParam param) { 25 | this.selector = openSelector("[Accepter] open selector"); 26 | this.channel = openServerSocketChannelNonBlocking("[Accepter] open server socket channel"); 27 | this.param = param; 28 | bindAddress(this.channel, this.param); 29 | } 30 | 31 | /** 32 | * add worker thread 33 | * 34 | * @param worker 35 | * worker 36 | */ 37 | public void addIoWorker(IoWorker worker) { 38 | workers.add(worker); 39 | } 40 | 41 | public void accept() { 42 | registerChannel(); 43 | // select 44 | while (true) { 45 | try { 46 | this.selector.select(); 47 | Set keys = this.selector.selectedKeys(); 48 | Iterator iterator = keys.iterator(); 49 | while (iterator.hasNext()) { 50 | SelectionKey key = iterator.next(); 51 | handle(key); 52 | iterator.remove(); 53 | } 54 | } catch (IOException e) { 55 | this.logger.warning("[Accepter] select : " + e.toString()); 56 | this.selector = openSelector("[Accepter] select : "); 57 | registerChannel(); 58 | } 59 | } 60 | } 61 | 62 | private void handle(SelectionKey key) { 63 | if (key.isAcceptable()) { 64 | /* 65 | * accept 66 | */ 67 | ServerSocketChannel server = (ServerSocketChannel) key.channel(); 68 | try { 69 | SocketChannel channel = server.accept(); 70 | this.logger.info("[Accept] accept : " + channel.getRemoteAddress().toString()); 71 | IoWorker worker = workers.get(workerIndex); 72 | worker.dispatch(new JobBean(channel, this.param)); 73 | workerIndex = (workerIndex + 1) % workers.size(); 74 | } catch (IOException e) { 75 | // accepter error 76 | this.logger.warning("[Accept] accept : " + e.toString()); 77 | if (param.getOnAcceptError() != null) { 78 | param.getOnAcceptError().onAcceptError(e); 79 | } 80 | registerChannel(); 81 | } 82 | } 83 | } 84 | 85 | @Override 86 | public void run() { 87 | accept(); 88 | } 89 | 90 | /** 91 | * bind address 92 | * 93 | * @param serverSocketChannel 94 | * serverSocketChannel 95 | * @param serverParam 96 | * serverParam 97 | */ 98 | private void bindAddress(ServerSocketChannel serverSocketChannel, ServerParam serverParam) { 99 | do { 100 | try { 101 | serverSocketChannel.socket().bind(new InetSocketAddress(serverParam.getHost(), serverParam.getPort()), 102 | serverParam.getBacklog()); 103 | break; 104 | } catch (IOException e) { 105 | this.logger.warning("[Accepter] bind address : " + e.toString()); 106 | } 107 | } while (true); 108 | } 109 | 110 | /** 111 | * register 112 | */ 113 | private void registerChannel() { 114 | // register 115 | do { 116 | try { 117 | this.channel.register(this.selector, SelectionKey.OP_ACCEPT); 118 | break; 119 | } catch (ClosedChannelException e) { 120 | this.logger.warning("[Accepter] register : " + e.toString()); 121 | this.channel = openServerSocketChannelNonBlocking("[Accepter] open server socket channel "); 122 | bindAddress(this.channel, this.param); 123 | } 124 | } while (true); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/worker/Connector.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.worker; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.SocketAddress; 6 | import java.nio.channels.ClosedChannelException; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.Selector; 9 | import java.nio.channels.SocketChannel; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.Iterator; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.concurrent.BlockingQueue; 17 | import java.util.concurrent.ConcurrentHashMap; 18 | import java.util.concurrent.ConcurrentMap; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.logging.Logger; 21 | 22 | import com.github.luohaha.connection.Connection; 23 | import com.github.luohaha.context.Context; 24 | import com.github.luohaha.handler.IoHandler; 25 | import com.github.luohaha.param.ClientParam; 26 | 27 | public class Connector extends Worker implements Runnable { 28 | private Selector selector; 29 | private List workers = new ArrayList<>(); 30 | private int workersIndex = 0; 31 | private ConcurrentMap chanToParam = new ConcurrentHashMap<>(); 32 | private BlockingQueue chanQueue = new LinkedBlockingQueue<>(); 33 | 34 | public Connector() { 35 | this.selector = openSelector("[Connector]" + " selector open : "); 36 | } 37 | 38 | /** 39 | * send msg to remote site 40 | * 41 | * @param host 42 | * host 43 | * @param port 44 | * port 45 | * @param param 46 | * param 47 | */ 48 | public void connect(String host, int port, ClientParam param) { 49 | // build socket channel 50 | SocketChannel socketChannel = openSocketChannelNonBlocking("[Connector]" + " socket channel open : "); 51 | // build connection 52 | connectToAddress(socketChannel, host, port, param); 53 | } 54 | 55 | @Override 56 | public void run() { 57 | 58 | while (true) { 59 | try { 60 | this.selector.select(); 61 | SocketChannel newChan = this.chanQueue.poll(); 62 | if (newChan != null) { 63 | try { 64 | newChan.register(selector, SelectionKey.OP_CONNECT); 65 | } catch (ClosedChannelException e) { 66 | logger.warning("[Connector] channel close : " + e.toString()); 67 | } 68 | } 69 | Set keys = this.selector.selectedKeys(); 70 | Iterator iterator = keys.iterator(); 71 | while (iterator.hasNext()) { 72 | SelectionKey key = iterator.next(); 73 | handle(key); 74 | iterator.remove(); 75 | } 76 | } catch (IOException e1) { 77 | this.logger.warning("[Connector] select error : " + e1.toString()); 78 | this.selector = openSelector("[Connector]" + " selector open : "); 79 | } 80 | } 81 | } 82 | 83 | private void handle(SelectionKey key) { 84 | SocketChannel channel = (SocketChannel) key.channel(); 85 | if (key.isConnectable()) { 86 | try { 87 | if (channel.finishConnect()) { 88 | //connect finish 89 | this.logger.info("[Connecter] finish connect " + channel.getRemoteAddress().toString()); 90 | IoWorker worker = this.workers.get(workersIndex); 91 | worker.dispatch(new JobBean(channel, this.chanToParam.get(channel))); 92 | workersIndex = (workersIndex + 1) % workers.size(); 93 | } 94 | } catch (IOException e) { 95 | this.logger.info("[Connecter] finish connect error : " + e.toString()); 96 | ClientParam clientParam = this.chanToParam.get(channel); 97 | if (clientParam.getOnConnectError() != null) { 98 | clientParam.getOnConnectError().onConnectError(e); 99 | } 100 | this.chanToParam.remove(channel); 101 | try { 102 | channel.close(); 103 | } catch (IOException e1) { 104 | // already close 105 | } 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * add io worker 112 | * 113 | * @param worker 114 | * worker 115 | */ 116 | public void addWorker(IoWorker worker) { 117 | this.workers.add(worker); 118 | } 119 | 120 | /** 121 | * connect to address 122 | * 123 | * @param socketChannel 124 | * socketChannel 125 | * @param address 126 | * address 127 | * @param param 128 | * param 129 | */ 130 | private void connectToAddress(SocketChannel socketChannel, String host, int port, ClientParam param) { 131 | SocketAddress address = new InetSocketAddress(host, port); 132 | try { 133 | socketChannel.connect(address); 134 | // connect success 135 | this.chanToParam.put(socketChannel, param); 136 | this.chanQueue.add(socketChannel); 137 | this.selector.wakeup(); 138 | } catch (IOException e) { 139 | this.logger.warning("[Connector] connect to " + host + ":" + port + " fail"); 140 | if (param.getOnConnectError() != null) 141 | param.getOnConnectError().onConnectError(e); 142 | } 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/worker/IoWorker.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.worker; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.ClosedChannelException; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.Selector; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.Iterator; 9 | import java.util.Set; 10 | import java.util.concurrent.BlockingQueue; 11 | import java.util.concurrent.LinkedBlockingQueue; 12 | import java.util.logging.Logger; 13 | 14 | import com.github.luohaha.connection.Connection; 15 | import com.github.luohaha.context.Context; 16 | import com.github.luohaha.context.ContextBean; 17 | import com.github.luohaha.handler.IoHandler; 18 | import com.github.luohaha.param.Param; 19 | 20 | public class IoWorker extends Worker implements Runnable { 21 | private int id; 22 | private Selector selector; 23 | private Context context; 24 | private IoHandler ioHandler; 25 | private BlockingQueue jobBeans = new LinkedBlockingQueue<>(); 26 | private Logger logger = Logger.getLogger("LightComm4J"); 27 | 28 | public IoWorker(int id) { 29 | this.id = id; 30 | this.context = new Context(); 31 | this.selector = openSelector("[IoWorker-" + this.id + "]" + " selector open : "); 32 | this.ioHandler = new IoHandler(this.selector, this.context); 33 | } 34 | 35 | @Override 36 | public void run() { 37 | while (true) { 38 | try { 39 | this.selector.select(); 40 | JobBean job = jobBeans.poll(); 41 | if (job != null) { 42 | this.logger.fine("[IoWorker-" + this.id + "]" + " handle new job"); 43 | initSocketChannel(job); 44 | } 45 | Set keys = this.selector.selectedKeys(); 46 | Iterator iterator = keys.iterator(); 47 | while (iterator.hasNext()) { 48 | SelectionKey key = iterator.next(); 49 | handle(key); 50 | iterator.remove(); 51 | } 52 | } catch (IOException e) { 53 | // select error 54 | this.logger.warning("[IoWorker-" + this.id + "]" + " selector : " + e.toString()); 55 | this.selector = openSelector("[IoWorker-" + this.id + "]" + " selector open : "); 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * handle read or write event 62 | * 63 | * @param key 64 | * key 65 | * @throws IOException 66 | * IOException 67 | */ 68 | private void handle(SelectionKey key) { 69 | SocketChannel channel = (SocketChannel) key.channel(); 70 | ContextBean bean = this.context.getChanToContextBean().get(channel); 71 | if (key.isReadable()) { 72 | if (bean.getParam().getOnRead() == null) 73 | return; 74 | try { 75 | String address = channel.getRemoteAddress().toString(); 76 | ioHandler.readDataFromRemoteSite(channel, bean.getParam().getOnRead(), bean.getParam().getOnClose()); 77 | this.logger.info("[IoWorker-" + this.id + "] read data from remote site " + address); 78 | } catch (IOException e) { 79 | this.logger.warning("[IoWorker-" + this.id + "] read data from remote site : " + e.toString()); 80 | if (bean.getParam().getOnReadError() != null) 81 | bean.getParam().getOnReadError().onReadError(bean.getConnection(), e); 82 | this.context.removeContextByChan(channel); 83 | try { 84 | channel.close(); 85 | } catch (IOException e1) { 86 | // if channel already close 87 | } 88 | } 89 | } else if (key.isWritable()) { 90 | try { 91 | String address = channel.getRemoteAddress().toString(); 92 | ioHandler.writeDataToRemoteSite(channel, bean.getParam().getOnWrite()); 93 | this.logger.info("[IoWorker-" + this.id + "] write data to remote site " + address); 94 | } catch (IOException e) { 95 | this.logger.warning("[IoWorker-" + this.id + "] write data to remote site : " + e.toString()); 96 | if (bean.getParam().getOnWriteError() != null) 97 | bean.getParam().getOnWriteError().onWriteError(bean.getConnection(), e); 98 | this.context.removeContextByChan(channel); 99 | try { 100 | channel.close(); 101 | } catch (IOException e1) { 102 | // if channel already close 103 | } 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * dispatch job to worker 110 | * 111 | * @param job 112 | * job 113 | */ 114 | public void dispatch(JobBean job) { 115 | this.jobBeans.add(job); 116 | this.selector.wakeup(); 117 | } 118 | 119 | /** 120 | * init new job 121 | * 122 | * @param jobBean 123 | * jobBean 124 | * @throws IOException 125 | * IOException 126 | */ 127 | private void initSocketChannel(JobBean jobBean) { 128 | SocketChannel channel = jobBean.getChannel(); 129 | Param param = jobBean.getParam(); 130 | try { 131 | channel.configureBlocking(false); 132 | } catch (IOException e) { 133 | // channel error 134 | this.logger.warning("[IoWorker-" + this.id + "] channel : " + e.toString()); 135 | return; 136 | } 137 | int ops = 0; 138 | if (param == null) 139 | throw new NullPointerException(); 140 | if (param.getOnRead() != null) { 141 | ops |= SelectionKey.OP_READ; 142 | } 143 | ops |= SelectionKey.OP_WRITE; 144 | try { 145 | channel.register(this.selector, ops); 146 | } catch (ClosedChannelException e) { 147 | // channel close 148 | this.logger.warning("[IoWorker-" + this.id + "] channel : " + e.toString()); 149 | return; 150 | } 151 | // new connection 152 | Connection connection = new Connection(this.context, channel, this.selector); 153 | // init context 154 | this.context.initContext(channel, connection, ops, param); 155 | // call on accept or on connection 156 | if (param.isServerParam()) { 157 | if (param.getOnAccept() != null) { 158 | param.getOnAccept().onAccept(connection); 159 | } 160 | } else { 161 | if (param.getOnConnection() != null) { 162 | param.getOnConnection().onConnect(connection); 163 | } 164 | } 165 | 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/worker/JobBean.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.worker; 2 | 3 | import java.nio.channels.SocketChannel; 4 | 5 | import com.github.luohaha.param.Param; 6 | 7 | public class JobBean { 8 | private SocketChannel channel; 9 | private Param param; 10 | public JobBean(SocketChannel channel, Param param) { 11 | super(); 12 | this.channel = channel; 13 | this.param = param; 14 | } 15 | public SocketChannel getChannel() { 16 | return channel; 17 | } 18 | public void setChannel(SocketChannel channel) { 19 | this.channel = channel; 20 | } 21 | public Param getParam() { 22 | return param; 23 | } 24 | public void setParam(Param param) { 25 | this.param = param; 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LightComm4J/src/main/java/com/github/luohaha/worker/Worker.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.worker; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.Selector; 5 | import java.nio.channels.ServerSocketChannel; 6 | import java.nio.channels.SocketChannel; 7 | import java.util.logging.Logger; 8 | 9 | public class Worker { 10 | public Logger logger = Logger.getLogger("LightComm4J"); 11 | /** 12 | * open selector 13 | * @param msg 14 | * msg 15 | * @return 16 | * selector 17 | */ 18 | public Selector openSelector(String msg) { 19 | Selector selector; 20 | do { 21 | try { 22 | selector = Selector.open(); 23 | // selector open 24 | return selector; 25 | } catch (IOException e) { 26 | this.logger.warning(msg + e.toString()); 27 | } 28 | } while (true); 29 | } 30 | 31 | /** 32 | * open non-blocking socket channel 33 | * @param msg 34 | * message 35 | * @return 36 | * SocketChannel 37 | */ 38 | public SocketChannel openSocketChannelNonBlocking(String msg) { 39 | SocketChannel socketChannel; 40 | do { 41 | try { 42 | socketChannel = SocketChannel.open(); 43 | socketChannel.configureBlocking(false); 44 | return socketChannel; 45 | } catch (IOException e) { 46 | this.logger.warning(msg + e.toString()); 47 | } 48 | } while (true); 49 | } 50 | 51 | /** 52 | * open server socket channel 53 | * @param msg 54 | * message 55 | * @return 56 | * ServerSocketChannel 57 | */ 58 | public ServerSocketChannel openServerSocketChannelNonBlocking(String msg) { 59 | ServerSocketChannel serverSocketChannel; 60 | do { 61 | try { 62 | serverSocketChannel = ServerSocketChannel.open(); 63 | serverSocketChannel.configureBlocking(false); 64 | return serverSocketChannel; 65 | } catch (IOException e) { 66 | this.logger.warning(msg + e.toString()); 67 | } 68 | } while (true); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/chatroom/ChatRoomClient.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.chatroom; 2 | 3 | import java.io.IOException; 4 | import java.util.Scanner; 5 | import java.util.logging.Level; 6 | 7 | import com.github.luohaha.client.LightCommClient; 8 | import com.github.luohaha.param.ClientParam; 9 | 10 | public class ChatRoomClient { 11 | public static void main(String[] args) { 12 | ClientParam param = new ClientParam(); 13 | param.setOnConnect(conn -> { 14 | new Thread(() -> { 15 | Scanner scanner = new Scanner(System.in); 16 | while (scanner.hasNext()) { 17 | String msg = scanner.nextLine(); 18 | try { 19 | conn.write(msg.getBytes()); 20 | } catch (Exception e) { 21 | // TODO Auto-generated catch block 22 | e.printStackTrace(); 23 | } 24 | } 25 | }).start(); 26 | }); 27 | param.setOnRead((conn, msg) -> { 28 | System.out.println("[chatroom] " + new String(msg)); 29 | }); 30 | param.setOnClose(conn -> { 31 | System.out.println("[chatroom] " + "chatroom close!"); 32 | }); 33 | LightCommClient client = new LightCommClient(4); 34 | client.connect("localhost", 8888, param); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/chatroom/ChatRoomServer.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.chatroom; 2 | 3 | import java.io.IOException; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | import java.util.logging.Level; 7 | 8 | import com.github.luohaha.connection.Conn; 9 | import com.github.luohaha.param.ServerParam; 10 | import com.github.luohaha.server.LightCommServer; 11 | 12 | public class ChatRoomServer { 13 | public static void main(String[] args) { 14 | ServerParam param = new ServerParam("localhost", 8888); 15 | Set conns = new HashSet<>(); 16 | param.setBacklog(128); 17 | param.setOnAccept(conn -> { 18 | try { 19 | String m = conn.getRemoteAddress().toString() + " " + "is online!"; 20 | conns.add(conn); 21 | conns.forEach(c -> { 22 | try { 23 | c.write(m.getBytes()); 24 | } catch (Exception e) { 25 | // TODO Auto-generated catch block 26 | e.printStackTrace(); 27 | } 28 | }); 29 | } catch (Exception e) { 30 | // TODO Auto-generated catch block 31 | e.printStackTrace(); 32 | } 33 | }); 34 | param.setOnRead((conn, msg) -> { 35 | try { 36 | String m = conn.getRemoteAddress().toString() + " : " + new String(msg); 37 | conns.forEach(c -> { 38 | try { 39 | c.write(m.getBytes()); 40 | } catch (Exception e) { 41 | // TODO Auto-generated catch block 42 | e.printStackTrace(); 43 | } 44 | }); 45 | } catch (Exception e) { 46 | // TODO Auto-generated catch block 47 | e.printStackTrace(); 48 | } 49 | }); 50 | param.setOnClose(conn -> { 51 | try { 52 | conns.remove(conn); 53 | String m = conn.getRemoteAddress().toString() + " " + "is offline!"; 54 | conns.forEach(c -> { 55 | try { 56 | c.write(m.getBytes()); 57 | } catch (Exception e) { 58 | // TODO Auto-generated catch block 59 | e.printStackTrace(); 60 | } 61 | }); 62 | } catch (Exception e) { 63 | // TODO Auto-generated catch block 64 | e.printStackTrace(); 65 | } 66 | }); 67 | param.setOnReadError((conn, err) -> { 68 | System.out.println(err.getMessage()); 69 | }); 70 | param.setOnWriteError((conn, err) -> { 71 | System.out.println(err.getMessage()); 72 | }); 73 | param.setOnAcceptError(err -> { 74 | System.out.println(err.getMessage()); 75 | }); 76 | 77 | LightCommServer server = new LightCommServer(param, 4); 78 | server.start(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/rpc/AddRpc.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import java.util.List; 4 | 5 | public class AddRpc implements IRpcFunction{ 6 | @Override 7 | public Object rpcCall(String function, List params) { 8 | int res = (int)params.get(0) + (int)params.get(1); 9 | return res; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/rpc/RpcClientTest.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | import java.util.Random; 4 | 5 | public class RpcClientTest { 6 | 7 | private static RpcClient client = new RpcClient(); 8 | 9 | public static int add(int a, int b) { 10 | return a + b; 11 | } 12 | 13 | public static int addRpc(int a, int b) { 14 | return (int)client.remote("localhost", 8888).call("add", a, b); 15 | } 16 | 17 | public static void main(String[] args) { 18 | client.start(); 19 | int succCount = 0; 20 | int totalCount = 1000; 21 | Random random = new Random(); 22 | for (int i = 0; i < totalCount; i++) { 23 | int a = random.nextInt(100); 24 | int b = random.nextInt(100); 25 | if (add(a, b) == addRpc(a, b)) { 26 | succCount++; 27 | } 28 | } 29 | if (succCount == totalCount) { 30 | System.out.println("Success"); 31 | } else { 32 | System.out.println("fail"); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/rpc/RpcServerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.rpc; 2 | 3 | public class RpcServerTest { 4 | public static void main(String[] args) { 5 | RpcServer rpcServer = new RpcServer("localhost", 8888); 6 | rpcServer.add("add", new AddRpc()); 7 | rpcServer.start(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/server/test/TestClient.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.server.test; 2 | 3 | import java.io.IOException; 4 | import java.net.Socket; 5 | import java.nio.ByteBuffer; 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Level; 9 | import java.util.logging.SimpleFormatter; 10 | 11 | import com.github.luohaha.client.LightCommClient; 12 | import com.github.luohaha.param.ClientParam; 13 | 14 | public class TestClient { 15 | 16 | public static void main(String[] args) throws IOException { 17 | AtomicInteger clientCount = new AtomicInteger(0); 18 | ClientParam param = new ClientParam(); 19 | param.setLogLevel(Level.WARNING); 20 | FileHandler fileHandler = new FileHandler("./test.log"); 21 | fileHandler.setFormatter(new SimpleFormatter()); 22 | param.addLogHandler(fileHandler); 23 | param.setOnWrite((conn) -> { 24 | try { 25 | conn.write("hello".getBytes()); 26 | } catch (Exception e) { 27 | // TODO Auto-generated catch block 28 | e.printStackTrace(); 29 | } 30 | }); 31 | param.setOnRead((conn, data) -> { 32 | //System.out.println(new String(data)); 33 | clientCount.incrementAndGet(); 34 | try { 35 | //System.out.println(System.currentTimeMillis()); 36 | conn.doClose(); 37 | } catch (Exception e) { 38 | // TODO Auto-generated catch block 39 | e.printStackTrace(); 40 | } 41 | }); 42 | param.setOnReadError((conn, err) -> { 43 | System.out.println(err.getMessage()); 44 | }); 45 | param.setOnWriteError((conn, err) -> { 46 | System.out.println(err.getMessage()); 47 | }); 48 | param.setOnConnectError(err -> { 49 | System.out.println(err.getMessage()); 50 | }); 51 | LightCommClient client = new LightCommClient(4); 52 | int count = 6000; 53 | for (int i = 0; i < count; i++) { 54 | client.connect("localhost", 8888, param); 55 | } 56 | try { 57 | Thread.sleep(5000); 58 | } catch (InterruptedException e) { 59 | // TODO Auto-generated catch block 60 | e.printStackTrace(); 61 | } 62 | fileHandler.flush(); 63 | System.out.println(count + " -> " + clientCount.get()); 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LightComm4J/src/test/java/com/github/luohaha/server/test/TestServer.java: -------------------------------------------------------------------------------- 1 | package com.github.luohaha.server.test; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketAddress; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | import java.util.logging.Level; 7 | 8 | import com.github.luohaha.param.ServerParam; 9 | import com.github.luohaha.server.LightCommServer; 10 | 11 | public class TestServer { 12 | 13 | public static void main(String[] args) { 14 | AtomicInteger count = new AtomicInteger(0); 15 | ServerParam param = new ServerParam("localhost", 8888); 16 | param.setLogLevel(Level.WARNING); 17 | param.setBacklog(128); 18 | param.setOnRead((conn, data) -> { 19 | try { 20 | conn.write(String.valueOf(count.incrementAndGet()).getBytes()); 21 | } catch (Exception e) { 22 | // TODO Auto-generated catch block 23 | e.printStackTrace(); 24 | } 25 | }); 26 | param.setOnClose(conn -> { 27 | try { 28 | //System.out.println(System.currentTimeMillis()); 29 | conn.doClose(); 30 | } catch (Exception e) { 31 | // TODO Auto-generated catch block 32 | e.printStackTrace(); 33 | } 34 | }); 35 | param.setOnReadError((conn, err) -> { 36 | System.out.println(err.getMessage()); 37 | }); 38 | param.setOnWriteError((conn, err) -> { 39 | System.out.println(err.getMessage()); 40 | }); 41 | param.setOnAcceptError(err -> { 42 | System.out.println(err.getMessage()); 43 | }); 44 | LightCommServer server = new LightCommServer(param, 4); 45 | server.start(); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LightComm4J 2 | 3 | Yet another asynchronous network library for java 4 | 5 | ## Install 6 | 7 | >Maven 8 | 9 | ```java 10 | 11 | com.github.luohaha 12 | LightComm4J 13 | 1.0.0 14 | 15 | ``` 16 | 17 | >Download jar 18 | 19 | [download](https://oss.sonatype.org/service/local/repositories/releases/content/com/github/luohaha/LightComm4J/1.0.0/LightComm4J-1.0.0.jar) 20 | 21 | ## How to use 22 | 23 | * server-side 24 | 25 | First, we need to creates a socket address from a hostname and a port number. And then, we should define some callback function when some event happens. We use `ServerParam` to store these informations. 26 | 27 | ```java 28 | ServerParam param = new ServerParam("localhost", 8888); 29 | // set backlog 30 | param.setBacklog(128); 31 | param.setOnAccept(conn -> { 32 | System.out.println("accept!"); 33 | }); 34 | param.setOnRead((conn, data) -> { 35 | System.out.println("read!"); 36 | }); 37 | param.setOnWrite(conn -> { 38 | System.out.println("write!"); 39 | }); 40 | param.setOnClose(conn -> { 41 | System.out.println("close!"); 42 | }); 43 | param.setOnReadError((conn, err) -> { 44 | System.out.println(err.getMessage()); 45 | }); 46 | param.setOnWriteError((conn, err) -> { 47 | System.out.println(err.getMessage()); 48 | }); 49 | param.setOnAcceptError(err -> { 50 | System.out.println(err.getMessage()); 51 | }); 52 | ``` 53 | 54 | `OnAccept` will be called when accepter return a new connection. `OnRead` will be called when socket's recv buffer isn't empty. `OnWrite` will be called at first time when socket's send buffer isn't empty and it will be call just once. When remote side close socket, then `OnClose` be called, but if we close server side socket first, this function will not be called forever. 55 | 56 | Finally, we can start our server using 'ServerParam' and need to decide the size of io thread pool. 57 | 58 | ```java 59 | // 4 is the io thread pool's size 60 | LightCommServer server = new LightCommServer(param, 4); 61 | server.start(); 62 | ``` 63 | 64 | * client-side 65 | 66 | The usage of client side is similar to server side. First, we also need to create a `ClientParam` which is just like `ServerParam`, and define some callback function in it. 67 | 68 | ```java 69 | ClientParam param = new ClientParam(); 70 | 71 | param.setOnConnection((conn) -> { 72 | System.out.println("connect!"); 73 | }); 74 | param.setOnWrite((conn) -> { 75 | System.out.println("write"); 76 | }); 77 | param.setOnRead((conn, data) -> { 78 | System.out.println("read"); 79 | }); 80 | param.setOnClose((conn) -> { 81 | System.out.println("close"); 82 | }); 83 | param.setOnReadError((conn, err) -> { 84 | System.out.println(err.getMessage()); 85 | }); 86 | param.setOnWriteError((conn, err) -> { 87 | System.out.println(err.getMessage()); 88 | }); 89 | param.setOnConnError(err -> { 90 | System.out.println(err.getMessage()); 91 | }); 92 | ``` 93 | 94 | `OnConnection` will be called when connection is built. `OnWrite`, `OnRead` and `OnClose` are as same as server side. 95 | 96 | Finally, we can use client to send message. 97 | 98 | ```java 99 | // 4 is the io thread pool's size 100 | LightCommClient client = new LightCommClient(4); 101 | client.connect("localhost", 8888, param); 102 | client.connect("localhost", 8889, param); 103 | ``` 104 | 105 | ## Interface 106 | 107 | * `OnAccept` 108 | 109 | ```java 110 | public interface OnAccept { 111 | public void onAccept(Conn connection); 112 | } 113 | ``` 114 | 115 | * `OnClose` 116 | 117 | ```java 118 | public interface OnClose { 119 | public void onClose(Conn connection); 120 | } 121 | ``` 122 | 123 | * `OnConnection` 124 | 125 | ```java 126 | public interface OnConnection { 127 | public void onConnection(Conn connection); 128 | } 129 | ``` 130 | 131 | * `OnRead` 132 | 133 | ```java 134 | public interface OnRead { 135 | public void onRead(Conn connection, byte[] data); 136 | } 137 | ``` 138 | 139 | * `OnWrite` 140 | 141 | ```java 142 | public interface OnWrite { 143 | public void onWrite(Conn connection); 144 | } 145 | ``` 146 | 147 | * `OnConnError` 148 | 149 | ```java 150 | public interface OnConnError { 151 | public void onConnError(Exception e); 152 | } 153 | ``` 154 | 155 | * `OnAcceptError` 156 | 157 | ```java 158 | public interface OnAcceptError { 159 | public void onAcceptError(Exception e); 160 | } 161 | ``` 162 | 163 | * `OnReadError` 164 | 165 | ```java 166 | public interface OnReadError { 167 | public void onReadError(Conn conn, Exception e); 168 | } 169 | ``` 170 | 171 | * `OnWriteError` 172 | 173 | ```java 174 | public interface OnWriteError { 175 | public void onWriteError(Conn conn, Exception e); 176 | } 177 | ``` 178 | 179 | * `Conn` 180 | 181 | We can send message, close connection and set tcp options by `Conn`. 182 | 183 | ```java 184 | public interface Conn { 185 | // send data to remote side 186 | public void write(byte[] data) throws ConnectionCloseException, ClosedChannelException; 187 | // close connection when nothing to send 188 | public void close() throws IOException; 189 | // close connection immediately 190 | public void doClose() throws IOException; 191 | // get local address 192 | public SocketAddress getLocalAddress() throws IOException; 193 | // get remote address 194 | public SocketAddress getRemoteAddress() throws IOException; 195 | // set socket's send buffer' size 196 | public void setSendBuffer(int size) throws IOException; 197 | // set socket's recv buffer' size 198 | public void setRecvBuffer(int size) throws IOException; 199 | // open keep-alive mode 200 | public void setKeepAlive(boolean flag) throws IOException; 201 | // open reuse address mode 202 | public void setReUseAddr(boolean flag) throws IOException; 203 | // no use nagle algorithm 204 | public void setNoDelay(boolean flag) throws IOException; 205 | // get socket's send buffer' size 206 | public int getSendBuffer() throws IOException; 207 | // get socket's recv buffer' size 208 | public int getRecvBuffer() throws IOException; 209 | public boolean getKeepAlive() throws IOException; 210 | public boolean getReUseAddr() throws IOException; 211 | public boolean getNoDelay() throws IOException; 212 | } 213 | ``` 214 | 215 | ## How to use rpc 216 | 217 | * server-side 218 | 219 | First, we need to create `RpcServer`, set host address and port. Then we need to add remote call function's name and `IRpcFunction`. Finally, start server. 220 | 221 | ```java 222 | RpcServer rpcServer = new RpcServer("localhost", 8888); 223 | rpcServer.add("add", new AddRpc()); 224 | rpcServer.start(); 225 | ``` 226 | 227 | ```java 228 | public class AddRpc implements IRpcFunction{ 229 | @Override 230 | public Object rpcCall(String function, List params) { 231 | int res = (int)params.get(0) + (int)params.get(1); 232 | return res; 233 | } 234 | } 235 | ``` 236 | 237 | * client-side 238 | 239 | First, we need to create `RpcClient`, then start it. Then we can use `remote` and `call` to finish remote process call. 240 | 241 | ```java 242 | RpcClient client = new RpcClient(); 243 | // need to start client, before remote call. 244 | client.start(); 245 | client.remote("localhost", 8888).call("add", 1, 2); 246 | ``` 247 | 248 | ## Example 249 | 250 | we use `LightComm4J` to build a simple chatroom. 251 | 252 | * server-side 253 | 254 | ```java 255 | public class ChatRoomServer { 256 | public static void main(String[] args) { 257 | ServerParam param = new ServerParam("localhost", 8888); 258 | Set conns = new HashSet<>(); 259 | param.setBacklog(128); 260 | param.setOnAccept(conn -> { 261 | try { 262 | String m = conn.getRemoteAddress().toString() + " " + "is online!"; 263 | conns.add(conn); 264 | conns.forEach(c -> { 265 | try { 266 | c.write(m.getBytes()); 267 | } catch (Exception e) { 268 | // TODO Auto-generated catch block 269 | e.printStackTrace(); 270 | } 271 | }); 272 | } catch (Exception e) { 273 | // TODO Auto-generated catch block 274 | e.printStackTrace(); 275 | } 276 | }); 277 | param.setOnRead((conn, msg) -> { 278 | try { 279 | String m = conn.getRemoteAddress().toString() + " : " + new String(msg); 280 | conns.forEach(c -> { 281 | try { 282 | c.write(m.getBytes()); 283 | } catch (Exception e) { 284 | // TODO Auto-generated catch block 285 | e.printStackTrace(); 286 | } 287 | }); 288 | } catch (Exception e) { 289 | // TODO Auto-generated catch block 290 | e.printStackTrace(); 291 | } 292 | }); 293 | param.setOnClose(conn -> { 294 | try { 295 | conns.remove(conn); 296 | String m = conn.getRemoteAddress().toString() + " " + "is offline!"; 297 | conns.forEach(c -> { 298 | try { 299 | c.write(m.getBytes()); 300 | } catch (Exception e) { 301 | // TODO Auto-generated catch block 302 | e.printStackTrace(); 303 | } 304 | }); 305 | } catch (Exception e) { 306 | // TODO Auto-generated catch block 307 | e.printStackTrace(); 308 | } 309 | }); 310 | param.setOnReadError((conn, err) -> { 311 | System.out.println(err.getMessage()); 312 | }); 313 | param.setOnWriteError((conn, err) -> { 314 | System.out.println(err.getMessage()); 315 | }); 316 | param.setOnAcceptError(err -> { 317 | System.out.println(err.getMessage()); 318 | }); 319 | 320 | LightCommServer server = new LightCommServer(param, 4); 321 | try { 322 | server.start(); 323 | } catch (IOException e) { 324 | // TODO Auto-generated catch block 325 | e.printStackTrace(); 326 | } 327 | } 328 | } 329 | 330 | ``` 331 | 332 | * client-side 333 | 334 | ```java 335 | public class ChatRoomClient { 336 | public static void main(String[] args) { 337 | ClientParam param = new ClientParam(); 338 | param.setOnConnection(conn -> { 339 | new Thread(() -> { 340 | Scanner scanner = new Scanner(System.in); 341 | while (scanner.hasNext()) { 342 | String msg = scanner.nextLine(); 343 | try { 344 | conn.write(msg.getBytes()); 345 | } catch (Exception e) { 346 | // TODO Auto-generated catch block 347 | e.printStackTrace(); 348 | } 349 | } 350 | }).start(); 351 | }); 352 | param.setOnRead((conn, msg) -> { 353 | System.out.println("[chatroom] " + new String(msg)); 354 | }); 355 | param.setOnClose(conn -> { 356 | System.out.println("[chatroom] " + "chatroom close!"); 357 | }); 358 | try { 359 | LightCommClient client = new LightCommClient(4); 360 | client.connect("localhost", 8888, param); 361 | } catch (IOException e) { 362 | // TODO Auto-generated catch block 363 | e.printStackTrace(); 364 | } 365 | } 366 | } 367 | 368 | ``` 369 | 370 | ## Example for RPC 371 | 372 | * server 373 | 374 | ```java 375 | public class RpcServerTest { 376 | public static void main(String[] args) { 377 | RpcServer rpcServer = new RpcServer("localhost", 8888); 378 | rpcServer.add("add", new AddRpc()); 379 | rpcServer.start(); 380 | } 381 | } 382 | ``` 383 | 384 | ```java 385 | public class AddRpc implements IRpcFunction{ 386 | @Override 387 | public Object rpcCall(String function, List params) { 388 | int res = (int)params.get(0) + (int)params.get(1); 389 | return res; 390 | } 391 | } 392 | ``` 393 | 394 | * client 395 | 396 | ```java 397 | public class RpcClientTest { 398 | 399 | private static RpcClient client = new RpcClient(); 400 | 401 | public static int add(int a, int b) { 402 | return a + b; 403 | } 404 | 405 | public static int addRpc(int a, int b) { 406 | return (int)client.remote("localhost", 8888).call("add", a, b); 407 | } 408 | 409 | public static void main(String[] args) { 410 | client.start(); 411 | System.out.println("1 + 2 = " + add(1, 2)); 412 | System.out.println("1 + 2 = " + addRpc(1, 2)); 413 | } 414 | 415 | } 416 | ``` --------------------------------------------------------------------------------