└── common ├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs ├── org.eclipse.jdt.ui.prefs └── org.eclipse.m2e.core.prefs ├── doc ├── README.md ├── p2p开发项目.xls └── 聊天开发项目.xls ├── pom.xml └── src ├── main └── java │ └── org │ └── son │ └── chat │ └── common │ ├── net │ ├── config │ │ └── SocketChannelConfig.java │ ├── core │ │ ├── coder │ │ │ ├── ICoder.java │ │ │ ├── ICoderCtx.java │ │ │ ├── ICoderParserManager.java │ │ │ ├── IHandle.java │ │ │ ├── IPackageCoder.java │ │ │ └── impl │ │ │ │ ├── CoderParser.java │ │ │ │ ├── CoderParserManager.java │ │ │ │ └── CoderResult.java │ │ ├── handle │ │ │ ├── AbstractSocketHandle.java │ │ │ ├── ClientManagerHandle.java │ │ │ ├── EmptyHandle.java │ │ │ ├── HeartHandle.java │ │ │ ├── ISocketHandle.java │ │ │ ├── PipeHandle.java │ │ │ └── SessionHandle.java │ │ ├── session │ │ │ ├── ISession.java │ │ │ ├── ISessionFactory.java │ │ │ ├── Key.java │ │ │ ├── Session.java │ │ │ ├── SessionFactory.java │ │ │ └── SessionKey.java │ │ └── socket │ │ │ ├── IChannel.java │ │ │ ├── IClientSocketService.java │ │ │ ├── IPipeChannel.java │ │ │ ├── IServerSocketService.java │ │ │ ├── ISocketChannel.java │ │ │ ├── ISocketPool.java │ │ │ ├── ISocketService.java │ │ │ └── impl │ │ │ ├── AbstractISocketChannel.java │ │ │ ├── ClientPipeChannel.java │ │ │ ├── ClientSocket.java │ │ │ ├── ServerSocket.java │ │ │ ├── SocketChannelCtx.java │ │ │ └── SocketPool.java │ ├── exception │ │ ├── CoderException.java │ │ └── NetException.java │ └── util │ │ ├── ByteHelper.java │ │ ├── IpUtil.java │ │ ├── NamedThreadFactory.java │ │ └── NioUtil.java │ └── protocol │ ├── ChatHandle.java │ └── PackageDefaultCoder.java └── test └── java └── org └── son └── chat └── common ├── ChatTestServerHandle.java ├── TestByteBuffer.java ├── TestNioClient.java ├── TestNioServer.java └── TestSelector.java /common/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /common/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /log -------------------------------------------------------------------------------- /common/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | common 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /common/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=utf-8 3 | encoding//src/test/java=utf-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /common/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore 3 | org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull 4 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault 5 | org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable 6 | org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled 7 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 8 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 9 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 10 | org.eclipse.jdt.core.compiler.compliance=1.7 11 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 12 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 13 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 14 | org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning 15 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 16 | org.eclipse.jdt.core.compiler.problem.autoboxing=ignore 17 | org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning 18 | org.eclipse.jdt.core.compiler.problem.deadCode=warning 19 | org.eclipse.jdt.core.compiler.problem.deprecation=warning 20 | org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled 21 | org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled 22 | org.eclipse.jdt.core.compiler.problem.discouragedReference=warning 23 | org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore 24 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 25 | org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore 26 | org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore 27 | org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled 28 | org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore 29 | org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning 30 | org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning 31 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 32 | org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning 33 | org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled 34 | org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning 35 | org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning 36 | org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore 37 | org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore 38 | org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning 39 | org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore 40 | org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore 41 | org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled 42 | org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore 43 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore 44 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled 45 | org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning 46 | org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore 47 | org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning 48 | org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning 49 | org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore 50 | org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error 51 | org.eclipse.jdt.core.compiler.problem.nullReference=warning 52 | org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error 53 | org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning 54 | org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning 55 | org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore 56 | org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore 57 | org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore 58 | org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore 59 | org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning 60 | org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning 61 | org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore 62 | org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore 63 | org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore 64 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore 65 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore 66 | org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled 67 | org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning 68 | org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled 69 | org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled 70 | org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore 71 | org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning 72 | org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled 73 | org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning 74 | org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning 75 | org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore 76 | org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning 77 | org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore 78 | org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore 79 | org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore 80 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore 81 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled 82 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled 83 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled 84 | org.eclipse.jdt.core.compiler.problem.unusedImport=warning 85 | org.eclipse.jdt.core.compiler.problem.unusedLabel=warning 86 | org.eclipse.jdt.core.compiler.problem.unusedLocal=warning 87 | org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore 88 | org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore 89 | org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled 90 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled 91 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled 92 | org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning 93 | org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning 94 | org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning 95 | org.eclipse.jdt.core.compiler.source=1.7 96 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false 97 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 98 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 99 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 100 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 101 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 102 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 103 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0 104 | org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 105 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 106 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 107 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 108 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 109 | org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 110 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 111 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 112 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 113 | org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 114 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 115 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 116 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 117 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 118 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 119 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 120 | org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 121 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 122 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1 123 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0 124 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 125 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 126 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 127 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1 128 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 129 | org.eclipse.jdt.core.formatter.blank_lines_before_package=0 130 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 131 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 132 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line 133 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line 134 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line 135 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line 136 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line 137 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line 138 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line 139 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line 140 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line 141 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line 142 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line 143 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false 144 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false 145 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true 146 | org.eclipse.jdt.core.formatter.comment.format_header=false 147 | org.eclipse.jdt.core.formatter.comment.format_html=true 148 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true 149 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true 150 | org.eclipse.jdt.core.formatter.comment.format_source_code=true 151 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true 152 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=true 153 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert 154 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert 155 | org.eclipse.jdt.core.formatter.comment.line_length=80 156 | org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true 157 | org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true 158 | org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false 159 | org.eclipse.jdt.core.formatter.compact_else_if=true 160 | org.eclipse.jdt.core.formatter.continuation_indentation=2 161 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 162 | org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off 163 | org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on 164 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false 165 | org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true 166 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true 167 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true 168 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true 169 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true 170 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true 171 | org.eclipse.jdt.core.formatter.indent_empty_lines=false 172 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true 173 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true 174 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true 175 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false 176 | org.eclipse.jdt.core.formatter.indentation.size=4 177 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert 178 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert 179 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert 180 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert 181 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert 182 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert 183 | org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert 184 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert 185 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert 186 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert 187 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert 188 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert 189 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert 190 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert 191 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert 192 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert 193 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert 194 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert 195 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert 196 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert 197 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert 198 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert 199 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert 200 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert 201 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert 202 | org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert 203 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert 204 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert 205 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert 206 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert 207 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert 208 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert 209 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert 210 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert 211 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert 212 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert 213 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert 214 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert 215 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert 216 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert 217 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert 218 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert 219 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert 220 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert 221 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert 222 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert 223 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert 224 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert 225 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert 226 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert 227 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert 228 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert 229 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert 230 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert 231 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert 232 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert 233 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert 234 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert 235 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert 236 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert 237 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert 238 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert 239 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert 240 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert 241 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert 242 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert 243 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert 244 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert 245 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert 246 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert 247 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert 248 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert 249 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert 250 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert 251 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert 252 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert 253 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert 254 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert 255 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert 256 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert 257 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert 258 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert 259 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert 260 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert 261 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert 262 | org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert 263 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert 264 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert 265 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert 266 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert 267 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert 268 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert 269 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert 270 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert 271 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert 272 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert 273 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert 274 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert 275 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert 276 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert 277 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert 278 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert 279 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert 280 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert 281 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert 282 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert 283 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert 284 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert 285 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert 286 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert 287 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert 288 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert 289 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert 290 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert 291 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert 292 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert 293 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert 294 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert 295 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert 296 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert 297 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert 298 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert 299 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert 300 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert 301 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert 302 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert 303 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert 304 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert 305 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert 306 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert 307 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert 308 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert 309 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert 310 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert 311 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert 312 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert 313 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert 314 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert 315 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert 316 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert 317 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert 318 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert 319 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert 320 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert 321 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert 322 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert 323 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert 324 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert 325 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert 326 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert 327 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert 328 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert 329 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert 330 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert 331 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert 332 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert 333 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert 334 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert 335 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert 336 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert 337 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert 338 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert 339 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert 340 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert 341 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert 342 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert 343 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert 344 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert 345 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert 346 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert 347 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert 348 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert 349 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert 350 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert 351 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert 352 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert 353 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert 354 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert 355 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert 356 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert 357 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true 358 | org.eclipse.jdt.core.formatter.join_wrapped_lines=true 359 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false 360 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false 361 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false 362 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false 363 | org.eclipse.jdt.core.formatter.lineSplit=200 364 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false 365 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false 366 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 367 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 368 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true 369 | org.eclipse.jdt.core.formatter.tabulation.char=mixed 370 | org.eclipse.jdt.core.formatter.tabulation.size=8 371 | org.eclipse.jdt.core.formatter.use_on_off_tags=false 372 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false 373 | org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true 374 | org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true 375 | org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true 376 | -------------------------------------------------------------------------------- /common/.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | formatter_profile=_test 3 | formatter_settings_version=12 4 | org.eclipse.jdt.ui.javadoc=false 5 | org.eclipse.jdt.ui.text.custom_code_templates= 6 | -------------------------------------------------------------------------------- /common/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /common/doc/README.md: -------------------------------------------------------------------------------- 1 | * 链式编/解码 2 | * 链路层链式处理 3 | * 管道管理socket 4 | * 多协议处理非常方便 5 | * 仿netty NioEventLoop 单线程串行处理 6 | 7 | ======== 8 | 侍加功能 : 9 | * 自动化编/解码 10 | * rpc 接口增强使用 11 | 12 | 简单聊天例子 13 | ======== 14 | 15 | server 16 | ====== 17 | TestNioServer 18 | ``` 19 | //创建session管理工厂 20 | ISessionFactory sessionFactory = new SessionFactory(); 21 | //创建编/解码管理 22 | ICoderParserManager coderParserManager = new CoderParserManager(); 23 | //注册包编/解码,处理业务 24 | coderParserManager.register(CoderParser.valueOf("server chat", PackageDefaultCoder.valueOf(), new ChatTestServerHandle())); 25 | //创建ServerSocket 实例 26 | ServerSocket serverSocket=ServerSocket.valueOf(SocketChannelConfig.valueOf(6969), 10,20,coderParserManager, sessionFactory); 27 | 28 | //启动服务 29 | serverSocket.start(); 30 | //阻塞当前线程 31 | serverSocket.sync(); 32 | //关闭处理 33 | serverSocket.stop(); 34 | 35 | ``` 36 | client 37 | ====== 38 | TestNioClient 39 | 传统方式连接 40 | ``` 41 | //创建编/解码管理 42 | ICoderParserManager coderParserManager = new CoderParserManager(); 43 | //注册包编/解码,处理业务 44 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 45 | //创建ClientSocket 实例 46 | final ClientSocket clientSocket = ClientSocket.valueOf(SocketChannelConfig.valueOf(6969), new SocketPool("client", null), coderParserManager, new EmptyHandle()); 47 | 48 | //模拟连接之后发送消息 49 | Timer timer = new Timer(); 50 | timer.schedule(new TimerTask() { 51 | 52 | @Override 53 | public void run() { 54 | clientSocket.send("连接服务器成功"); 55 | System.out.println("send "); 56 | this.cancel(); 57 | } 58 | }, 1000); 59 | 60 | //启动服务 61 | clientSocket.start(); 62 | //阻塞当前线程 63 | clientSocket.sync(); 64 | //关闭处理 65 | clientSocket.stop(); 66 | ``` 67 | 68 | 服务器方式连接 69 | ``` 70 | //创建session管理工厂 71 | ISessionFactory sessionFactory = new SessionFactory(); 72 | //创建编/解码管理 73 | ICoderParserManager coderParserManager = new CoderParserManager(); 74 | //注册包编/解码,处理业务 75 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 76 | //创建ClientSocket 实例 77 | final ServerSocket serverSocket = ServerSocket.valueOf(SocketChannelConfig.valueOf(8888), 10, 20, coderParserManager, sessionFactory); 78 | 79 | //模拟连接之后发送消息 80 | Timer timer = new Timer(); 81 | timer.schedule(new TimerTask() { 82 | 83 | @Override 84 | public void run() { 85 | System.out.println("registerClientSocket"); 86 | //主动连接服务器 87 | ClientSocket clientSocket = serverSocket.registerClient(SocketChannelConfig.valueOf(6969)); 88 | clientSocket.send("连接服务器成功"); 89 | this.cancel(); 90 | } 91 | }, 1000); 92 | 93 | //启动服务 94 | serverSocket.start(); 95 | //阻塞当前线程 96 | serverSocket.sync(); 97 | //关闭处理 98 | serverSocket.stop(); 99 | ``` 100 | 101 | 102 | 103 | 源码实现过程 104 | ======== 105 | 链式编/解码 106 | 107 | * 由 多个 ICoder 输入/输出转换处理 108 | * CoderParser 类组装多个 ICoder 109 | * 编/码处理器 注意优先级
110 | * nio read -> packageCoder -> link coders -> handle
111 | * handle write -> link coders -> packageCoder -> nio write
112 | * 由 ICoderParserManager 管理调用处理 113 | 114 | ``` 115 | public interface ICoderParserManager { 116 | 117 | /** 118 | * 解码处理 119 | * 120 | * @return CoderResult 121 | * */ 122 | CoderResult decode(ByteBuffer buffer, ICoderCtx ctx); 123 | 124 | /** 125 | * 编码处理 126 | * */ 127 | ByteBuffer encode(Object message, ICoderCtx ctx); 128 | 129 | void error(ByteBuffer buffer, ICoderCtx ctx); 130 | 131 | /** 注册 编/码处理器 */ 132 | void register(CoderParser coderParser); 133 | } 134 | ``` 135 | 136 | 其中核心 137 | decode 138 | encode 139 | ``` 140 | @Override 141 | public CoderResult decode(ByteBuffer buffer, ICoderCtx ctx) { 142 | final SocketChannelCtx socketChannelCtx = (SocketChannelCtx) ctx; 143 | final ClientSocket clientSocket = socketChannelCtx.getClientSocket(); 144 | 145 | for (CoderParser coderParser : coderParsers.values()) { 146 | final IPackageCoder packageCoder = coderParser.getPackageCoder(); 147 | final ICoder[] linkCoders = coderParser.getCoders(); 148 | final IHandle handle = coderParser.getHandle(); 149 | Object value = null; 150 | synchronized (buffer) { 151 | // 已解析完 152 | if (socketChannelCtx.getCurrPackageIndex() >= buffer.limit()) { 153 | return CoderResult.valueOf(ResultValue.UNFINISHED); 154 | } 155 | // 包协议处理 156 | if (!packageCoder.verify(buffer, ctx)) { 157 | continue; 158 | } 159 | // 包解析 160 | value = packageCoder.decode(buffer, ctx); 161 | if (value == null) { 162 | // 包未读完整 163 | return CoderResult.valueOf(ResultValue.UNFINISHED); 164 | } 165 | } 166 | // 链式处理 167 | if (linkCoders != null) { 168 | for (ICoder coder : linkCoders) { 169 | value = coder.decode(value, ctx); 170 | if (value == null) { 171 | throw new CoderException("解码出错 : " + coder.getClass()); 172 | } 173 | } 174 | } 175 | // 业务解码处理 176 | value = handle.decode(value, ctx); 177 | clientSocket.readBefore(socketChannelCtx, value); 178 | handle.handle(value, ctx); 179 | clientSocket.readAfter(socketChannelCtx, value); 180 | 181 | return CoderResult.valueOf(ResultValue.SUCCEED); 182 | } 183 | return CoderResult.valueOf(ResultValue.NOT_FIND_CODER); 184 | } 185 | 186 | @Override 187 | public ByteBuffer encode(Object message, ICoderCtx ctx) { 188 | 189 | for (CoderParser coderParser : coderParsers.values()) { 190 | final IPackageCoder packageCoder = coderParser.getPackageCoder(); 191 | final ICoder[] linkCoders = coderParser.getCoders(); 192 | final IHandle handle = coderParser.getHandle(); 193 | // 业务检查 194 | if (!handle.verify(message, ctx)) { 195 | continue; 196 | } 197 | // 业务编码处理 198 | Object value = handle.encode(message, ctx); 199 | // 链式处理 200 | if (linkCoders != null) { 201 | for (int i = linkCoders.length - 1; i >= 0; i--) { 202 | ICoder coder = linkCoders[i]; 203 | value = coder.encode(value, ctx); 204 | if (value == null) { 205 | throw new CoderException("编码出错 : " + coder.getClass()); 206 | } 207 | } 208 | } 209 | // 打包消息处理 210 | value = packageCoder.encode(value, ctx); 211 | if (value != null) { 212 | return (ByteBuffer) value; 213 | } 214 | throw new CoderException("编码出错 :" + packageCoder.getClass()); 215 | } 216 | 217 | throw new CoderException("未找到编/解码处理器 "); 218 | } 219 | 220 | ``` 221 | 222 | * 半包/帖包处理 : AbstractISocketChannel doRead方法摘要,根据解码返回的状态做处理。 223 | * 半包:当不是完成状态时,继续解码,从最后一次包索引开始处理 224 | * 帖包:当完成包解码移动包索引,等侍下轮解码处理 225 | ``` 226 | boolean run = true; 227 | // 粘包处理 228 | while (run) { 229 | ByteBuffer cpbuffer = socketChannelCtx.coderBegin(); 230 | cpbuffer.mark(); 231 | CoderResult coderResult = coderParserManager.decode(cpbuffer, socketChannelCtx); 232 | switch (coderResult.getValue()) { 233 | case SUCCEED: 234 | break; 235 | case NOT_FIND_CODER: 236 | final int readySize = socketChannelCtx.getWriteIndex() - socketChannelCtx.getCurrPackageIndex(); 237 | final int headLimit = 255; 238 | if (readySize >= headLimit) { 239 | throw new CoderException("未找到编/解码处理器 "); 240 | } 241 | run = false; 242 | break; 243 | case UNFINISHED: 244 | case UNKNOWN: 245 | case ERROR: 246 | default: 247 | run = false; 248 | // TODO throw 249 | break; 250 | } 251 | } 252 | ``` 253 | 254 | 未完侍加 -------------------------------------------------------------------------------- /common/doc/p2p开发项目.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solq360/common/20bc6709a4012e1f7e78c8b74c5a1803ae60ca3b/common/doc/p2p开发项目.xls -------------------------------------------------------------------------------- /common/doc/聊天开发项目.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solq360/common/20bc6709a4012e1f7e78c8b74c5a1803ae60ca3b/common/doc/聊天开发项目.xls -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.con.chat 6 | common 7 | 1.0 8 | jar 9 | common 10 | http://maven.apache.org 11 | 12 | 13 | UTF-8 14 | 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.10 21 | test 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 31 | 1.7 32 | 1.7 33 | utf-8 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/config/SocketChannelConfig.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.config; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.net.SocketAddress; 5 | 6 | import org.son.chat.common.net.util.IpUtil; 7 | 8 | /** 9 | * @author solq 10 | */ 11 | public class SocketChannelConfig { 12 | private InetSocketAddress address; 13 | 14 | public static SocketChannelConfig valueOf(int remotePort) { 15 | SocketChannelConfig result = new SocketChannelConfig(); 16 | result.address = new InetSocketAddress(remotePort); 17 | return result; 18 | } 19 | 20 | public static SocketChannelConfig valueOf(String remoteHost, int remotePort) { 21 | SocketChannelConfig result = new SocketChannelConfig(); 22 | result.address = new InetSocketAddress(remoteHost, remotePort); 23 | return result; 24 | } 25 | 26 | public static SocketChannelConfig valueOf(SocketAddress remoteAddress) { 27 | SocketChannelConfig result = new SocketChannelConfig(); 28 | result.address = (InetSocketAddress) remoteAddress; 29 | return result; 30 | } 31 | 32 | public String toIp() { 33 | return IpUtil.getIp(address); 34 | } 35 | 36 | // getter 37 | 38 | public InetSocketAddress getAddress() { 39 | return address; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/ICoder.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder; 2 | 3 | /** 4 | * 编/解码处理器 5 | * 6 | * @author solq 7 | */ 8 | public interface ICoder { 9 | 10 | /** 编码 */ 11 | public OUT encode(INPUT value, ICoderCtx ctx); 12 | 13 | /** 解码 */ 14 | public INPUT decode(OUT value, ICoderCtx ctx); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/ICoderCtx.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder; 2 | 3 | /** 4 | * @author solq 5 | */ 6 | public interface ICoderCtx { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/ICoderParserManager.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.son.chat.common.net.core.coder.impl.CoderParser; 6 | import org.son.chat.common.net.core.coder.impl.CoderResult; 7 | 8 | /** 9 | * 编/码处理器管理 10 | * 11 | * @author solq 12 | */ 13 | public interface ICoderParserManager { 14 | 15 | /** 16 | * 解码处理 17 | * 18 | * @return CoderResult 19 | * */ 20 | CoderResult decode(ByteBuffer buffer, ICoderCtx ctx); 21 | 22 | /** 23 | * 编码处理 24 | * */ 25 | ByteBuffer encode(Object message, ICoderCtx ctx); 26 | 27 | void error(ByteBuffer buffer, ICoderCtx ctx); 28 | 29 | /** 注册 编/码处理器 */ 30 | void register(CoderParser coderParser); 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/IHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder; 2 | 3 | /** 4 | * @author solq 5 | */ 6 | public interface IHandle extends ICoder { 7 | /** 回写验证 */ 8 | public boolean verify(Object value, ICoderCtx ctx); 9 | 10 | /** 业务处理 */ 11 | public void handle(INPUT value, ICoderCtx ctx); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/IPackageCoder.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder; 2 | 3 | /** 4 | * @author solq 5 | */ 6 | public interface IPackageCoder extends ICoder { 7 | /** 包验证 */ 8 | public boolean verify(OUT value, ICoderCtx ctx); 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/impl/CoderParser.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder.impl; 2 | 3 | import org.son.chat.common.net.core.coder.ICoder; 4 | import org.son.chat.common.net.core.coder.IHandle; 5 | import org.son.chat.common.net.core.coder.IPackageCoder; 6 | 7 | /** 8 | * 编/码处理器 注意优先级
9 | * nio read -> packageCoder -> link coders -> handle
10 | * handle write -> link coders -> packageCoder -> nio write
11 | * 12 | * @author solq 13 | */ 14 | public class CoderParser { 15 | 16 | /** 业务处理分发 **/ 17 | private IHandle handle; 18 | /** 网络包编/解码 **/ 19 | private IPackageCoder packageCoder; 20 | /** 链式编/解码 **/ 21 | private ICoder[] coders; 22 | /** 名称标识 **/ 23 | private String name; 24 | 25 | public static CoderParser valueOf(String name, IPackageCoder packageCoder, IHandle handle, ICoder... coders) { 26 | CoderParser result = new CoderParser(name, packageCoder, handle, coders); 27 | return result; 28 | } 29 | 30 | private CoderParser(String name, IPackageCoder packageCoder, IHandle handle, ICoder... coders) { 31 | this.packageCoder = packageCoder; 32 | this.coders = coders; 33 | this.handle = handle; 34 | this.name = name; 35 | } 36 | 37 | // getter 38 | 39 | public ICoder[] getCoders() { 40 | return coders; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public IHandle getHandle() { 48 | return handle; 49 | } 50 | 51 | public IPackageCoder getPackageCoder() { 52 | return packageCoder; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/impl/CoderParserManager.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder.impl; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.LinkedHashMap; 5 | 6 | import org.son.chat.common.net.core.coder.ICoder; 7 | import org.son.chat.common.net.core.coder.ICoderCtx; 8 | import org.son.chat.common.net.core.coder.ICoderParserManager; 9 | import org.son.chat.common.net.core.coder.IHandle; 10 | import org.son.chat.common.net.core.coder.IPackageCoder; 11 | import org.son.chat.common.net.core.coder.impl.CoderResult.ResultValue; 12 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 13 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 14 | import org.son.chat.common.net.exception.CoderException; 15 | 16 | /** 17 | * @author solq 18 | */ 19 | @SuppressWarnings({ "unchecked", "rawtypes" }) 20 | public class CoderParserManager implements ICoderParserManager { 21 | 22 | private LinkedHashMap coderParsers = new LinkedHashMap<>(); 23 | 24 | @Override 25 | public CoderResult decode(ByteBuffer buffer, ICoderCtx ctx) { 26 | final SocketChannelCtx socketChannelCtx = (SocketChannelCtx) ctx; 27 | final ClientSocket clientSocket = socketChannelCtx.getClientSocket(); 28 | 29 | for (CoderParser coderParser : coderParsers.values()) { 30 | final IPackageCoder packageCoder = coderParser.getPackageCoder(); 31 | final ICoder[] linkCoders = coderParser.getCoders(); 32 | final IHandle handle = coderParser.getHandle(); 33 | Object value = null; 34 | synchronized (buffer) { 35 | // 已解析完 36 | if (socketChannelCtx.getCurrPackageIndex() >= buffer.limit()) { 37 | return CoderResult.valueOf(ResultValue.UNFINISHED); 38 | } 39 | // 包协议处理 40 | if (!packageCoder.verify(buffer, ctx)) { 41 | continue; 42 | } 43 | // 包解析 44 | value = packageCoder.decode(buffer, ctx); 45 | if (value == null) { 46 | // 包未读完整 47 | return CoderResult.valueOf(ResultValue.UNFINISHED); 48 | } 49 | } 50 | // 链式处理 51 | if (linkCoders != null) { 52 | for (ICoder coder : linkCoders) { 53 | value = coder.decode(value, ctx); 54 | if (value == null) { 55 | throw new CoderException("解码出错 : " + coder.getClass()); 56 | } 57 | } 58 | } 59 | // 业务解码处理 60 | value = handle.decode(value, ctx); 61 | clientSocket.readBefore(socketChannelCtx, value); 62 | handle.handle(value, ctx); 63 | clientSocket.readAfter(socketChannelCtx, value); 64 | 65 | return CoderResult.valueOf(ResultValue.SUCCEED); 66 | } 67 | return CoderResult.valueOf(ResultValue.NOT_FIND_CODER); 68 | } 69 | 70 | @Override 71 | public ByteBuffer encode(Object message, ICoderCtx ctx) { 72 | 73 | for (CoderParser coderParser : coderParsers.values()) { 74 | final IPackageCoder packageCoder = coderParser.getPackageCoder(); 75 | final ICoder[] linkCoders = coderParser.getCoders(); 76 | final IHandle handle = coderParser.getHandle(); 77 | // 业务检查 78 | if (!handle.verify(message, ctx)) { 79 | continue; 80 | } 81 | // 业务编码处理 82 | Object value = handle.encode(message, ctx); 83 | // 链式处理 84 | if (linkCoders != null) { 85 | for (int i = linkCoders.length - 1; i >= 0; i--) { 86 | ICoder coder = linkCoders[i]; 87 | value = coder.encode(value, ctx); 88 | if (value == null) { 89 | throw new CoderException("编码出错 : " + coder.getClass()); 90 | } 91 | } 92 | } 93 | // 打包消息处理 94 | value = packageCoder.encode(value, ctx); 95 | if (value != null) { 96 | return (ByteBuffer) value; 97 | } 98 | throw new CoderException("编码出错 :" + packageCoder.getClass()); 99 | } 100 | 101 | throw new CoderException("未找到编/解码处理器 "); 102 | } 103 | 104 | @Override 105 | public void error(ByteBuffer buffer, ICoderCtx ctx) { 106 | 107 | } 108 | 109 | @Override 110 | public synchronized void register(CoderParser coderParser) { 111 | if (coderParsers.containsKey(coderParser.getName())) { 112 | throw new CoderException("已注册编/解码处理器 : " + coderParser.getName()); 113 | } 114 | coderParsers.put(coderParser.getName(), coderParser); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/coder/impl/CoderResult.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.coder.impl; 2 | 3 | /** 4 | * @author solq 5 | */ 6 | public class CoderResult { 7 | 8 | public static enum ResultValue { 9 | /** 成功 **/ 10 | SUCCEED, 11 | /** 未完成 半包/帖包状态 **/ 12 | UNFINISHED, 13 | /** 非法数据 **/ 14 | ERROR, 15 | /** 未知错误 **/ 16 | UNKNOWN, 17 | /** 未找到编/解码 **/ 18 | NOT_FIND_CODER; 19 | } 20 | 21 | /** 处理后返回内容 **/ 22 | private Object content; 23 | 24 | /** 处理结果 **/ 25 | private ResultValue value; 26 | 27 | // getter 28 | 29 | public Object getContent() { 30 | return content; 31 | } 32 | 33 | public ResultValue getValue() { 34 | return value; 35 | } 36 | 37 | public static CoderResult valueOf(ResultValue resule) { 38 | CoderResult result = new CoderResult(); 39 | result.value = resule; 40 | return result; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/AbstractSocketHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import org.son.chat.common.net.core.coder.ICoderCtx; 4 | import org.son.chat.common.net.core.session.ISession; 5 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 6 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 7 | 8 | /** 9 | * @author solq 10 | * */ 11 | public abstract class AbstractSocketHandle implements ISocketHandle { 12 | 13 | protected static ClientSocket getClientSocket(ICoderCtx ctx){ 14 | return ((SocketChannelCtx) ctx).getClientSocket(); 15 | } 16 | 17 | protected static ISession getSession(ICoderCtx ctx){ 18 | return getClientSocket(ctx).getSession(); 19 | } 20 | 21 | @Override 22 | public void openBefore(ICoderCtx ctx) { 23 | 24 | } 25 | 26 | @Override 27 | public void openAfter(ICoderCtx ctx) { 28 | 29 | } 30 | 31 | @Override 32 | public void closeBefore(ICoderCtx ctx) { 33 | 34 | } 35 | 36 | @Override 37 | public void closeAfter(ICoderCtx ctx) { 38 | 39 | } 40 | 41 | @Override 42 | public void readBefore(ICoderCtx ctx, Object request) { 43 | 44 | } 45 | 46 | @Override 47 | public void readAfter(ICoderCtx ctx, Object request) { 48 | 49 | } 50 | 51 | @Override 52 | public void writeBefore(ICoderCtx ctx, Object response) { 53 | 54 | } 55 | 56 | @Override 57 | public void writeAfter(ICoderCtx ctx, Object response) { 58 | 59 | } 60 | 61 | @Override 62 | public void openError(ICoderCtx ctx) { 63 | 64 | } 65 | 66 | @Override 67 | public void writeError(ICoderCtx ctx, Object response) { 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/ClientManagerHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import org.son.chat.common.net.core.coder.ICoderCtx; 4 | import org.son.chat.common.net.core.socket.impl.ClientPipeChannel; 5 | 6 | /** 7 | * @author solq 8 | * */ 9 | public class ClientManagerHandle extends AbstractSocketHandle { 10 | private ClientPipeChannel channelClients; 11 | 12 | public ClientManagerHandle(ClientPipeChannel channelClients) { 13 | this.channelClients = channelClients; 14 | } 15 | 16 | @Override 17 | public void openAfter(ICoderCtx ctx) { 18 | this.channelClients.join(ClientPipeChannel.DEFAULT_CLIENT_CHANNEL, getClientSocket(ctx)); 19 | } 20 | 21 | @Override 22 | public void closeAfter(ICoderCtx ctx) { 23 | this.channelClients.eixt(getClientSocket(ctx)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/EmptyHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | /** 4 | * 无实现处理 5 | * 6 | * @author solq 7 | * */ 8 | public class EmptyHandle extends AbstractSocketHandle { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/HeartHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.Future; 5 | import java.util.concurrent.ScheduledThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.son.chat.common.net.core.coder.ICoderCtx; 9 | import org.son.chat.common.net.core.session.ISession; 10 | import org.son.chat.common.net.core.session.Key; 11 | import org.son.chat.common.net.core.session.SessionKey; 12 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 13 | import org.son.chat.common.net.util.NamedThreadFactory; 14 | 15 | /** 16 | * 心跳管理 17 | * 18 | * @author solq 19 | * */ 20 | public class HeartHandle extends AbstractSocketHandle { 21 | 22 | private final static ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("Heart")); 23 | 24 | private long delayTime; 25 | 26 | public HeartHandle(long delayTime) { 27 | this.delayTime = delayTime; 28 | } 29 | 30 | @Override 31 | public void openAfter(ICoderCtx ctx) { 32 | record(ctx, SessionKey.KEY_CONNECT_TIME); 33 | } 34 | 35 | @Override 36 | public void readAfter(ICoderCtx ctx, Object request) { 37 | record(ctx, SessionKey.KEY_READ_TIME); 38 | } 39 | 40 | @Override 41 | public void writeAfter(ICoderCtx ctx, Object response) { 42 | record(ctx, SessionKey.KEY_SEND_TIME); 43 | } 44 | 45 | private void record(ICoderCtx ctx, Key key) { 46 | final Date now = new Date(); 47 | ISession session = getSession(ctx); 48 | key.setAttr(session, now); 49 | SessionKey.KEY_HEART_TIME.setAttr(session, now); 50 | 51 | Future preTask = SessionKey.KEY_HEART_TASK.getAttr(session); 52 | if (preTask != null) { 53 | return; 54 | } 55 | 56 | synchronized (session) { 57 | preTask = SessionKey.KEY_HEART_TASK.getAttr(session); 58 | if (preTask != null) { 59 | return; 60 | } 61 | final long endTime = System.currentTimeMillis() + delayTime; 62 | final HeartTask task = new HeartTask(ctx, endTime); 63 | Future future = scheduled.schedule(task, delayTime, TimeUnit.MILLISECONDS); 64 | SessionKey.KEY_HEART_TASK.setAttr(session, future); 65 | } 66 | } 67 | 68 | private final class HeartTask implements Runnable { 69 | private ICoderCtx ctx; 70 | private long endTime; 71 | 72 | public HeartTask(ICoderCtx ctx, long endTime) { 73 | this.ctx = ctx; 74 | this.endTime = endTime; 75 | } 76 | 77 | @Override 78 | public void run() { 79 | final ISession session = getSession(ctx); 80 | final Date time = SessionKey.KEY_HEART_TIME.getAttr(session); 81 | final ClientSocket clientSocket = getClientSocket(ctx); 82 | if (clientSocket.isClose()) { 83 | return; 84 | } 85 | // close 86 | if (time == null || time.getTime() < this.endTime) { 87 | clientSocket.stop(); 88 | return; 89 | } 90 | // 下次触发时间-当前时间 = 延时执行偏移时间 91 | long delay = time.getTime() + getDelayTime() - System.currentTimeMillis(); 92 | if (delay <= 0) { 93 | clientSocket.stop(); 94 | return; 95 | } 96 | this.endTime = System.currentTimeMillis() + delay; 97 | Future future = scheduled.schedule(new HeartTask(ctx, endTime), delay, TimeUnit.MILLISECONDS); 98 | SessionKey.KEY_HEART_TASK.setAttr(session, future); 99 | } 100 | } 101 | 102 | // getter 103 | public long getDelayTime() { 104 | return delayTime; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/ISocketHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import org.son.chat.common.net.core.coder.ICoderCtx; 4 | 5 | /** 6 | * socket 链路逻辑处理 7 | * 8 | * @author solq 9 | */ 10 | public interface ISocketHandle { 11 | /** 12 | * 建立连接之前 13 | * */ 14 | void openBefore(ICoderCtx ctx); 15 | 16 | /** 17 | * 成功建立连接之后 18 | * */ 19 | void openAfter(ICoderCtx ctx); 20 | 21 | /** 22 | * 建立连接失败 23 | * */ 24 | void openError(ICoderCtx ctx); 25 | 26 | /** 27 | * 关闭连接之前 28 | * */ 29 | void closeBefore(ICoderCtx ctx); 30 | 31 | /** 32 | * 关闭连接之后 33 | * */ 34 | void closeAfter(ICoderCtx ctx); 35 | 36 | /** 37 | * socket read 之后,编码之前 38 | * */ 39 | void readBefore(ICoderCtx ctx, Object request); 40 | 41 | /** 42 | * socket read 之后,编码之后 43 | * */ 44 | void readAfter(ICoderCtx ctx, Object request); 45 | 46 | /** 47 | * socket write 之后,编码之前 48 | * */ 49 | void writeBefore(ICoderCtx ctx, Object response); 50 | 51 | /** 52 | * socket write 之后,编码之后 53 | * */ 54 | void writeAfter(ICoderCtx ctx, Object response); 55 | 56 | /** 57 | * socket write 失败 58 | * */ 59 | void writeError(ICoderCtx ctx, Object response); 60 | } 61 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/PipeHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import java.util.LinkedList; 4 | 5 | import org.son.chat.common.net.core.coder.ICoderCtx; 6 | 7 | /** 8 | * @author solq 9 | * */ 10 | public class PipeHandle extends AbstractSocketHandle { 11 | 12 | private LinkedList pipe = new LinkedList<>(); 13 | 14 | public synchronized void register(ISocketHandle... handles) { 15 | for (ISocketHandle handle : handles) { 16 | pipe.add(handle); 17 | } 18 | } 19 | 20 | @Override 21 | public void openBefore(ICoderCtx ctx) { 22 | for (ISocketHandle handle : pipe) { 23 | handle.openBefore(ctx); 24 | } 25 | } 26 | 27 | @Override 28 | public void openAfter(ICoderCtx ctx) { 29 | for (ISocketHandle handle : pipe) { 30 | handle.openAfter(ctx); 31 | } 32 | } 33 | 34 | @Override 35 | public void closeBefore(ICoderCtx ctx) { 36 | for (ISocketHandle handle : pipe) { 37 | handle.closeBefore(ctx); 38 | } 39 | } 40 | 41 | @Override 42 | public void closeAfter(ICoderCtx ctx) { 43 | for (ISocketHandle handle : pipe) { 44 | handle.closeAfter(ctx); 45 | } 46 | } 47 | 48 | @Override 49 | public void readBefore(ICoderCtx ctx, Object request) { 50 | for (ISocketHandle handle : pipe) { 51 | handle.readBefore(ctx, request); 52 | } 53 | } 54 | 55 | @Override 56 | public void readAfter(ICoderCtx ctx, Object request) { 57 | for (ISocketHandle handle : pipe) { 58 | handle.readAfter(ctx, request); 59 | } 60 | } 61 | 62 | @Override 63 | public void writeBefore(ICoderCtx ctx, Object response) { 64 | for (ISocketHandle handle : pipe) { 65 | handle.writeBefore(ctx, response); 66 | } 67 | } 68 | 69 | @Override 70 | public void writeAfter(ICoderCtx ctx, Object response) { 71 | for (ISocketHandle handle : pipe) { 72 | handle.writeAfter(ctx, response); 73 | } 74 | } 75 | 76 | @Override 77 | public void openError(ICoderCtx ctx) { 78 | for (ISocketHandle handle : pipe) { 79 | handle.openError(ctx); 80 | } 81 | } 82 | 83 | @Override 84 | public void writeError(ICoderCtx ctx, Object response) { 85 | for (ISocketHandle handle : pipe) { 86 | handle.writeError(ctx, response); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/handle/SessionHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.handle; 2 | 3 | import org.son.chat.common.net.core.coder.ICoderCtx; 4 | import org.son.chat.common.net.core.session.ISessionFactory; 5 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 6 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 7 | 8 | /** 9 | * 会话处理 10 | * 11 | * @author solq 12 | * */ 13 | public class SessionHandle extends AbstractSocketHandle { 14 | 15 | private ISessionFactory sessionFactory; 16 | 17 | public SessionHandle(ISessionFactory sessionFactory) { 18 | this.sessionFactory = sessionFactory; 19 | } 20 | 21 | @Override 22 | public void openAfter(ICoderCtx ctx) { 23 | if (!(ctx instanceof SocketChannelCtx)) { 24 | return; 25 | } 26 | ClientSocket clientSocket = getClientSocket(ctx); 27 | clientSocket.setSession(sessionFactory.createSession()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/ISession.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author solq 7 | * */ 8 | public interface ISession { 9 | public String getId(); 10 | public void replace(ISession session); 11 | 12 | public ISession setAttr(String key,Object value); 13 | public ISession removeAttr(String key); 14 | 15 | public T getAttr(String key); 16 | public Map getAttr(); 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/ISessionFactory.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | /** 4 | * 会话对象管理 5 | * 6 | * @author solq 7 | * */ 8 | public interface ISessionFactory { 9 | public ISession createSession(); 10 | 11 | public void destroySession(ISession session); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/Key.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | /** 4 | * session attr 托管 5 | * @author solq 6 | * */ 7 | public class Key { 8 | private String key; 9 | 10 | public Key(String key) { 11 | this.key = key; 12 | } 13 | 14 | public Key setAttr(ISession session, T value) { 15 | session.setAttr(key, value); 16 | return this; 17 | } 18 | 19 | public T getAttr(ISession session) { 20 | return session.getAttr(key); 21 | } 22 | } -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/Session.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | import java.util.Map; 4 | import java.util.Map.Entry; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 会话对象 9 | * 10 | * @author solq 11 | * */ 12 | public class Session implements ISession { 13 | 14 | public static Session valueOf(String id) { 15 | Session result = new Session(); 16 | result.id = id; 17 | return result; 18 | } 19 | 20 | private String id; 21 | 22 | private Map attr = new ConcurrentHashMap<>(); 23 | 24 | @Override 25 | public String getId() { 26 | return id; 27 | } 28 | 29 | @Override 30 | public void replace(ISession session) { 31 | for(Entry entry : session.getAttr().entrySet()){ 32 | attr.put(entry.getKey(), entry.getValue()); 33 | } 34 | } 35 | 36 | @Override 37 | public ISession setAttr(String key, Object value) { 38 | attr.put(key, value); 39 | return this; 40 | } 41 | 42 | @SuppressWarnings("unchecked") 43 | @Override 44 | public T getAttr(String key) { 45 | return (T) attr.get(key); 46 | } 47 | 48 | @Override 49 | public final Map getAttr() { 50 | return attr; 51 | } 52 | 53 | @Override 54 | public ISession removeAttr(String key) { 55 | attr.remove(key); 56 | return this; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/SessionFactory.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | import java.util.concurrent.atomic.AtomicLong; 4 | 5 | /** 6 | * 会话工厂 7 | * 8 | * @author solq 9 | * */ 10 | public class SessionFactory implements ISessionFactory { 11 | 12 | private final static AtomicLong NUM = new AtomicLong(); 13 | 14 | @Override 15 | public ISession createSession() { 16 | Session result = Session.valueOf(buildId()); 17 | return result; 18 | } 19 | 20 | @Override 21 | public void destroySession(ISession session) { 22 | 23 | } 24 | 25 | private static String buildId() { 26 | long value = NUM.getAndIncrement(); 27 | if (value > 10000000L) { 28 | synchronized (NUM) { 29 | if (NUM.getAndIncrement() > 10000000L) { 30 | NUM.set(0); 31 | } 32 | } 33 | } 34 | return String.valueOf(value); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/session/SessionKey.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.session; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.Future; 5 | 6 | /*** 7 | * 会话属性 key 8 | * 9 | * @author solq 10 | * */ 11 | public interface SessionKey { 12 | 13 | /*** 心跳时间 **/ 14 | public final static String HEART_TIME = "HEART_TIME"; 15 | 16 | /*** 最后发送数据时间 **/ 17 | public final static String SEND_TIME = "SEND_TIME"; 18 | 19 | /*** 最后读取数据时间 **/ 20 | public final static String READ_TIME = "READ_TIME"; 21 | 22 | /*** 最后连接数据时间 **/ 23 | public final static String CONNECT_TIME = "CONNECT_TIME"; 24 | 25 | public final static Key KEY_HEART_TIME = new Key<>(HEART_TIME); 26 | public final static Key KEY_SEND_TIME = new Key<>(SEND_TIME); 27 | public final static Key KEY_READ_TIME = new Key<>(READ_TIME); 28 | public final static Key KEY_CONNECT_TIME = new Key<>(CONNECT_TIME); 29 | public final static Key> KEY_HEART_TASK = new Key<>("KEY_HEART_TASK"); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/IChannel.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | /** 4 | * 管道接口 5 | * 6 | * @author solq 7 | */ 8 | public interface IChannel { 9 | public void setChannelName(String nameChannel); 10 | 11 | public String getChannelName(); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/IClientSocketService.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | import java.net.SocketAddress; 4 | import java.nio.ByteBuffer; 5 | 6 | import org.son.chat.common.net.core.handle.ISocketHandle; 7 | import org.son.chat.common.net.core.session.ISession; 8 | 9 | /** 10 | * 客户端socket服务 抽象 11 | * 12 | * @author solq 13 | */ 14 | public interface IClientSocketService extends ISocketHandle { 15 | 16 | public boolean isClose(); 17 | 18 | public boolean isConected(); 19 | 20 | public SocketAddress getLocalAddress(); 21 | public SocketAddress getRemoteAddress(); 22 | 23 | public void buildAddress(); 24 | 25 | public void setSession(ISession session); 26 | public ISession getSession(); 27 | 28 | // //////////////////消息处理行为////////////////////////// 29 | public void send(Object message); 30 | 31 | public void send(Object message, ByteBuffer byteBuffer); 32 | } 33 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/IPipeChannel.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | /** 4 | * @author solq 5 | * */ 6 | public interface IPipeChannel { 7 | 8 | /*** 9 | * 加入管道 10 | * 11 | * @param channelName 12 | *  管道名称 13 | * @param e 14 | * 元素 15 | * */ 16 | void join(String channelName, E e); 17 | 18 | /** 19 | * 退出管道 20 | * 21 | * @param e 22 | * */ 23 | void eixt(E e); 24 | 25 | /*** 26 | * 替换管道 27 | * 28 | * @param newChannelName 29 | *  管道名称 30 | * @param e 31 | * 元素 32 | * */ 33 | void replace(String newChannelName,E e); 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/IServerSocketService.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.son.chat.common.net.config.SocketChannelConfig; 6 | import org.son.chat.common.net.core.handle.ISocketHandle; 7 | import org.son.chat.common.net.core.session.ISession; 8 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 9 | 10 | /** 11 | * 服务端 socket 服务接口 12 | * 13 | * @author solq 14 | */ 15 | public interface IServerSocketService { 16 | 17 | // /////////////// 客户端发送消息处理//////////////////////// 18 | public void sendAll(Object message); 19 | 20 | public void send(String channelName, Object message); 21 | 22 | public void send(ClientSocket clientSocket, Object message); 23 | 24 | public void send(ClientSocket clientSocket, Object message, ByteBuffer byteBuffer); 25 | 26 | // ///////////////客户端注册处理//////////////////////// 27 | 28 | public ClientSocket registerClient(SocketChannelConfig config); 29 | 30 | public ISession createSession(); 31 | 32 | public ISocketPool getNextPool(); 33 | 34 | public ISocketPool[] getGroupPool(); 35 | 36 | // /////////////// 监控处理//////////////////////// 37 | 38 | public void registerHandle(ISocketHandle... handleArray); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/ISocketChannel.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | import java.nio.channels.SelectionKey; 4 | 5 | import org.son.chat.common.net.config.SocketChannelConfig; 6 | import org.son.chat.common.net.core.coder.ICoderParserManager; 7 | 8 | /** 9 | * 顶级接口抽象原则,必须的处理行为。细分的行为/属性不做抽象声明
10 | * socket 管理接口抽象 针对两socket 绑定处理 11 | * 12 | * @author solq 13 | */ 14 | public interface ISocketChannel { 15 | 16 | // ////////////////配置属性//////////////////////// 17 | public SocketChannelConfig getSocketChannelConfig(); 18 | public void setCoderParserManager(ICoderParserManager coderParserManager); 19 | public ISocketPool getPool(); 20 | 21 | public void doAccept(SelectionKey key); 22 | public void doConnect(SelectionKey key); 23 | public void doWrite(SelectionKey key); 24 | public void doRead(SelectionKey key); 25 | public void doClose(SelectionKey key); 26 | public boolean isClose(); 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/ISocketPool.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | import java.nio.channels.Selector; 4 | 5 | /** 6 | * socket 逻辑处理池子 7 | * 8 | * @author solq 9 | */ 10 | public interface ISocketPool { 11 | 12 | public void init(); 13 | /** 执行任务 **/ 14 | public void execute(Runnable task); 15 | 16 | /** 关闭处理 **/ 17 | public void shutdown(); 18 | 19 | /** 是否运行 **/ 20 | public boolean isRun(); 21 | 22 | /** 任务数 **/ 23 | public int taskCount(); 24 | 25 | /** select **/ 26 | public Selector getSelector(); 27 | 28 | /** 触发select **/ 29 | public void select(); 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/ISocketService.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket; 2 | 3 | /** 4 | * socket 服务接口 5 | * 6 | * @author solq 7 | */ 8 | public interface ISocketService { 9 | public void init(); 10 | 11 | public void start(); 12 | 13 | public void stop(); 14 | 15 | public void sync(); 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/AbstractISocketChannel.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.channels.SelectionKey; 5 | import java.nio.channels.Selector; 6 | import java.nio.channels.SocketChannel; 7 | 8 | import org.son.chat.common.net.config.SocketChannelConfig; 9 | import org.son.chat.common.net.core.coder.ICoderParserManager; 10 | import org.son.chat.common.net.core.coder.impl.CoderResult; 11 | import org.son.chat.common.net.core.handle.ISocketHandle; 12 | import org.son.chat.common.net.core.socket.ISocketChannel; 13 | import org.son.chat.common.net.core.socket.ISocketPool; 14 | import org.son.chat.common.net.core.socket.ISocketService; 15 | import org.son.chat.common.net.exception.CoderException; 16 | import org.son.chat.common.net.exception.NetException; 17 | import org.son.chat.common.net.util.NioUtil; 18 | 19 | /** 20 | * socketchannel 模板 21 | * 22 | * @author solq 23 | */ 24 | public abstract class AbstractISocketChannel implements ISocketChannel, ISocketService { 25 | 26 | protected boolean close = true; 27 | protected boolean init = false; 28 | 29 | protected ICoderParserManager coderParserManager; 30 | protected SocketChannelConfig socketChannelConfig; 31 | protected ISocketHandle handle; 32 | protected ISocketPool pool; 33 | 34 | /** 35 | * 读写分离 参考 : http://developer.51cto.com/art/201112/306532.htm
36 | * 也可以参考netty 37 | */ 38 | @Override 39 | public void start() { 40 | if (!init) { 41 | init(); 42 | } 43 | // TODO 44 | // while (!close) { 45 | // Selector selector = getSelector(); 46 | // try { 47 | // // TODO 占用CPU 处理 48 | // // 参考 : http://xw-z1985.iteye.com/blog/1928244 49 | // // http://www.tuicool.com/articles/36zimq 50 | // // http://xw-z1985.iteye.com/blog/1748660 51 | // int n = selector .select(10); 52 | // if (n < 0) { 53 | // continue; 54 | // } 55 | // } catch (IOException e) { 56 | // e.printStackTrace(); 57 | // break; 58 | // } 59 | // for (Iterator i = selector.selectedKeys().iterator(); 60 | // i.hasNext();) { 61 | // // 得到下一个Key 62 | // SelectionKey key = i.next(); 63 | // try { 64 | // handle(key); 65 | // } catch (Exception e) { 66 | // e.printStackTrace(); 67 | // } 68 | // i.remove(); 69 | // } 70 | // } 71 | // stop(); 72 | } 73 | 74 | @Override 75 | public void stop() { 76 | init = false; 77 | close = true; 78 | handle = null; 79 | socketChannelConfig = null; 80 | coderParserManager = null; 81 | // selector = null; 82 | } 83 | 84 | @Override 85 | public void doConnect(SelectionKey key) { 86 | System.out.println(" doConnect "); 87 | NioUtil.clearOps(key, SelectionKey.OP_CONNECT); 88 | NioUtil.setOps(key, SelectionKey.OP_READ); 89 | } 90 | 91 | @Override 92 | public void doClose(SelectionKey key) { 93 | System.out.println(" doClose "); 94 | this.stop(); 95 | } 96 | 97 | @Override 98 | public void doAccept(SelectionKey key) { 99 | System.out.println(" doAccept "); 100 | } 101 | 102 | @Override 103 | public void doWrite(SelectionKey key) { 104 | System.out.println(" doWrite "); 105 | // 服务端读到东西后,注册写事件。等写完东西后取消写事件的注册。 106 | NioUtil.clearOps(key, SelectionKey.OP_WRITE); 107 | } 108 | 109 | @Override 110 | public void doRead(SelectionKey key) { 111 | System.out.println(" doRead "); 112 | final SocketChannel clientChannel = (SocketChannel) key.channel(); 113 | final ClientSocket clientSocket = (ClientSocket) key.attachment(); 114 | final SocketChannelCtx socketChannelCtx = clientSocket.getCtx(); 115 | 116 | ByteBuffer buffer = null; 117 | long bytesRead = -1; 118 | try { 119 | buffer = socketChannelCtx.readBegin(); 120 | bytesRead = clientChannel.read(buffer); 121 | socketChannelCtx.readEnd(bytesRead); 122 | } catch (Exception e) { 123 | // 链路关闭,不清理读操作会造成死循环 124 | NioUtil.clearOps(key, SelectionKey.OP_READ); 125 | coderParserManager.error(buffer, socketChannelCtx); 126 | key.cancel(); 127 | clientSocket.stop(); 128 | throw new NetException("读取Socket数据异常 : ", e); 129 | } 130 | 131 | // 编码处理 132 | if (bytesRead == -1) { 133 | // 链路关闭,不清理读操作会造成死循环 134 | NioUtil.clearOps(key, SelectionKey.OP_READ); 135 | coderParserManager.error(buffer, socketChannelCtx); 136 | key.cancel(); 137 | clientSocket.stop(); 138 | } else { 139 | boolean run = true; 140 | // 粘包处理 141 | while (run) { 142 | ByteBuffer cpbuffer = socketChannelCtx.coderBegin(); 143 | cpbuffer.mark(); 144 | CoderResult coderResult = coderParserManager.decode(cpbuffer, socketChannelCtx); 145 | switch (coderResult.getValue()) { 146 | case SUCCEED: 147 | break; 148 | case NOT_FIND_CODER: 149 | final int readySize = socketChannelCtx.getWriteIndex() - socketChannelCtx.getCurrPackageIndex(); 150 | final int headLimit = 255; 151 | if (readySize >= headLimit) { 152 | throw new CoderException("未找到编/解码处理器 "); 153 | } 154 | run = false; 155 | break; 156 | case UNFINISHED: 157 | case UNKNOWN: 158 | case ERROR: 159 | default: 160 | run = false; 161 | // TODO throw 162 | break; 163 | } 164 | } 165 | } 166 | } 167 | 168 | // getter 169 | @Override 170 | public SocketChannelConfig getSocketChannelConfig() { 171 | return socketChannelConfig; 172 | } 173 | 174 | @Override 175 | public void setCoderParserManager(ICoderParserManager coderParserManager) { 176 | this.coderParserManager = coderParserManager; 177 | } 178 | 179 | @Override 180 | public boolean isClose() { 181 | return close; 182 | } 183 | 184 | public boolean isInit() { 185 | return init; 186 | } 187 | 188 | public ISocketHandle getHandle() { 189 | return handle; 190 | } 191 | 192 | @Override 193 | public ISocketPool getPool() { 194 | return this.pool; 195 | } 196 | 197 | public void register(ISocketPool pool) { 198 | this.pool = pool; 199 | } 200 | 201 | public Selector getSelector() { 202 | return this.pool.getSelector(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/ClientPipeChannel.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | import org.son.chat.common.net.core.socket.IPipeChannel; 9 | import org.son.chat.common.net.exception.NetException; 10 | import org.son.chat.common.net.util.IpUtil; 11 | 12 | /** 13 | * 客房端管道 14 | * */ 15 | public class ClientPipeChannel implements IPipeChannel { 16 | 17 | public final static String DEFAULT_CLIENT_CHANNEL = "DEFAULT_CLIENT_CHANNEL"; 18 | /** 已连接的客户端 */ 19 | private ConcurrentHashMap ipMapClients = new ConcurrentHashMap<>(); 20 | private ConcurrentHashMap> channelMapClients = new ConcurrentHashMap<>(); 21 | 22 | @Override 23 | public void join(String channelName, ClientSocket e) { 24 | e.buildAddress(); 25 | final String ip = IpUtil.getAddress(e.getRemoteAddress()); 26 | if (ip == null) { 27 | throw new NetException("ClientSocket 远程IP 未找到"); 28 | } 29 | 30 | if (null != e.getChannelName()) { 31 | replace(channelName, e); 32 | return; 33 | } 34 | 35 | e.setChannelName(channelName); 36 | Map channelClients = channelMapClients.get(channelName); 37 | if (channelClients == null) { 38 | ConcurrentHashMap newChannelClients = new ConcurrentHashMap(); 39 | ConcurrentHashMap now = channelMapClients.putIfAbsent(channelName, newChannelClients); 40 | channelClients = now != null ? now : newChannelClients; 41 | } 42 | 43 | ipMapClients.put(ip, e); 44 | channelClients.put(ip, e); 45 | 46 | } 47 | 48 | @Override 49 | public void eixt(ClientSocket e) { 50 | final String ip = IpUtil.getAddress(e.getRemoteAddress()); 51 | if (ip == null) { 52 | return; 53 | } 54 | ipMapClients.remove(ip); 55 | 56 | final String channle = e.getChannelName(); 57 | if (null == channle) { 58 | return; 59 | } 60 | 61 | ConcurrentHashMap channels = channelMapClients.get(channle); 62 | if (channels != null) { 63 | channels.remove(ip); 64 | } 65 | e.setChannelName(null); 66 | } 67 | 68 | @Override 69 | public void replace(String newChannelName, ClientSocket e) { 70 | eixt(e); 71 | join(newChannelName, e); 72 | } 73 | 74 | public List getAllClinetSockets() { 75 | List result = new LinkedList(); 76 | return result; 77 | } 78 | 79 | public List getChannelClinetSockets(String channelName) { 80 | List result = new LinkedList(); 81 | return result; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/ClientSocket.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.SocketChannel; 8 | 9 | import org.son.chat.common.net.config.SocketChannelConfig; 10 | import org.son.chat.common.net.core.coder.ICoderCtx; 11 | import org.son.chat.common.net.core.coder.ICoderParserManager; 12 | import org.son.chat.common.net.core.handle.ISocketHandle; 13 | import org.son.chat.common.net.core.session.ISession; 14 | import org.son.chat.common.net.core.socket.IChannel; 15 | import org.son.chat.common.net.core.socket.IClientSocketService; 16 | import org.son.chat.common.net.core.socket.ISocketPool; 17 | import org.son.chat.common.net.exception.NetException; 18 | import org.son.chat.common.net.util.NioUtil; 19 | 20 | /** 21 | * @author solq 22 | */ 23 | public class ClientSocket extends AbstractISocketChannel implements IClientSocketService, IChannel { 24 | 25 | public static ClientSocket valueOf(SocketChannelConfig socketChannelConfig, ISocketPool pool, ICoderParserManager coderParserManager, ISocketHandle socketHandle) { 26 | ClientSocket clientSocket = new ClientSocket(); 27 | clientSocket.pool = pool; 28 | clientSocket.socketChannelConfig = socketChannelConfig; 29 | clientSocket.coderParserManager = coderParserManager; 30 | clientSocket.handle = socketHandle; 31 | clientSocket.ctx = SocketChannelCtx.valueOf(clientSocket); 32 | return clientSocket; 33 | } 34 | 35 | public static ClientSocket valueOfServer(SocketChannelConfig socketChannelConfig, SocketChannel channel, ISocketPool pool, ICoderParserManager coderParserManager, ISocketHandle socketHandle) { 36 | ClientSocket clientSocket = new ClientSocket(); 37 | clientSocket.pool = pool; 38 | clientSocket.handle = socketHandle; 39 | clientSocket.channel = channel; 40 | clientSocket.coderParserManager = coderParserManager; 41 | clientSocket.socketChannelConfig = socketChannelConfig; 42 | clientSocket.connected = true; 43 | clientSocket.close = false; 44 | clientSocket.serverMode = true; 45 | clientSocket.ctx = SocketChannelCtx.valueOf(clientSocket); 46 | return clientSocket; 47 | } 48 | 49 | private SocketAddress localAddress; 50 | private SocketAddress remoteAddress; 51 | private SocketChannel channel; 52 | 53 | private SocketChannelCtx ctx; 54 | private SelectionKey selectionKey; 55 | private ISession session; 56 | 57 | private boolean connected = false; 58 | private String nameChannel; 59 | private boolean serverMode = false; 60 | 61 | @Override 62 | public void send(Object message) { 63 | ByteBuffer byteBuffer = coderParserManager.encode(message, ctx); 64 | byteBuffer.flip(); 65 | send(message, byteBuffer); 66 | } 67 | 68 | /** 69 | * nio channel 发送真恶心....
70 | * 发送数据参考 http://ericbaner.iteye.com/blog/1821798 71 | */ 72 | @Override 73 | public void send(final Object message, final ByteBuffer byteBuffer) { 74 | getPool().execute(new Runnable() { 75 | 76 | @Override 77 | public void run() { 78 | try { 79 | if (ClientSocket.this.close) { 80 | return; 81 | } 82 | ClientSocket.this.writeBefore(ctx, message); 83 | while (byteBuffer.hasRemaining()) { 84 | int len = ClientSocket.this.channel.write(byteBuffer); 85 | 86 | if (len < 0) { 87 | throw new NetException("发送消息出错 :" + len); 88 | } 89 | 90 | // 写半包处理 91 | if (len == 0) { 92 | System.out.println("写半包"); 93 | NioUtil.setOps(selectionKey, SelectionKey.OP_WRITE); 94 | // selector.wakeup(); 95 | break; 96 | } 97 | ClientSocket.this.writeAfter(ctx, message); 98 | } 99 | } catch (IOException e) { 100 | ClientSocket.this.writeError(ClientSocket.this.ctx, message); 101 | coderParserManager.error(byteBuffer, ClientSocket.this.ctx); 102 | throw new NetException("发送消息出错 :", e); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | @Override 109 | public void init() { 110 | try { 111 | this.init = true; 112 | this.openBefore(ctx); 113 | channel = SocketChannel.open(this.socketChannelConfig.getAddress()); 114 | channel.configureBlocking(false); 115 | this.connected = channel.isConnected(); 116 | if (!this.connected) { 117 | // this.channel.register(this.selector, 118 | // SelectionKey.OP_CONNECT); 119 | // TODO 定时检查连接 120 | } else { 121 | finishConnect(); 122 | } 123 | 124 | } catch (IOException e) { 125 | e.printStackTrace(); 126 | this.openError(ctx); 127 | stop(); 128 | } 129 | } 130 | 131 | private void finishConnect() throws IOException { 132 | 133 | while (!channel.finishConnect()) { 134 | // TODO 超时处理 135 | } 136 | 137 | selectionKey = channel.register(getSelector(),0, this); 138 | NioUtil.clearOps(this.selectionKey, SelectionKey.OP_ACCEPT); 139 | NioUtil.setOps(this.selectionKey, SelectionKey.OP_READ); 140 | this.close = false; 141 | this.openAfter(this.ctx); 142 | // NioUtil.setOps(selectionKey, SelectionKey.OP_WRITE); 143 | this.pool.init(); 144 | } 145 | 146 | @Override 147 | public synchronized void stop() { 148 | if (channel != null) { 149 | try { 150 | // 业务层通知 151 | this.closeBefore(ctx); 152 | if (channel.isConnected()) { 153 | channel.close(); 154 | } 155 | this.closeAfter(ctx); 156 | 157 | } catch (IOException e) { 158 | e.printStackTrace(); 159 | } 160 | } 161 | 162 | if (!serverMode) { 163 | try { 164 | getPool().shutdown(); 165 | } catch (Exception e) { 166 | e.printStackTrace(); 167 | } 168 | } 169 | 170 | ctx = null; 171 | channel = null; 172 | selectionKey = null; 173 | session = null; 174 | super.stop(); 175 | } 176 | 177 | @Override 178 | public void start() { 179 | if (serverMode) { 180 | return; 181 | } 182 | super.start(); 183 | } 184 | 185 | @Override 186 | public void openBefore(ICoderCtx ctx) { 187 | handle.openBefore(ctx); 188 | } 189 | 190 | @Override 191 | public void openAfter(ICoderCtx ctx) { 192 | handle.openAfter(ctx); 193 | } 194 | 195 | @Override 196 | public void closeBefore(ICoderCtx ctx) { 197 | handle.closeBefore(ctx); 198 | } 199 | 200 | @Override 201 | public void closeAfter(ICoderCtx ctx) { 202 | handle.closeAfter(ctx); 203 | } 204 | 205 | @Override 206 | public void readBefore(ICoderCtx ctx, Object request) { 207 | handle.readBefore(ctx, request); 208 | } 209 | 210 | @Override 211 | public void readAfter(ICoderCtx ctx, Object request) { 212 | handle.readAfter(ctx, request); 213 | } 214 | 215 | @Override 216 | public void writeBefore(ICoderCtx ctx, Object response) { 217 | handle.writeBefore(ctx, response); 218 | } 219 | 220 | @Override 221 | public void writeAfter(ICoderCtx ctx, Object response) { 222 | handle.writeAfter(ctx, response); 223 | } 224 | 225 | @Override 226 | public void openError(ICoderCtx ctx) { 227 | handle.openError(ctx); 228 | 229 | } 230 | 231 | @Override 232 | public void writeError(ICoderCtx ctx, Object response) { 233 | handle.writeError(ctx, response); 234 | } 235 | 236 | // getter 237 | 238 | public SelectionKey getSelectionKey() { 239 | return selectionKey; 240 | } 241 | 242 | public void setSelectionKey(SelectionKey selectionKey) { 243 | this.selectionKey = selectionKey; 244 | } 245 | 246 | public boolean isConnected() { 247 | return connected; 248 | } 249 | 250 | @Override 251 | public boolean isClose() { 252 | return this.close; 253 | } 254 | 255 | @Override 256 | public boolean isConected() { 257 | return this.connected; 258 | } 259 | 260 | public SocketChannelCtx getCtx() { 261 | return ctx; 262 | } 263 | 264 | @Override 265 | public void buildAddress() { 266 | this.localAddress = getLocalAddress(); 267 | this.remoteAddress = getRemoteAddress(); 268 | } 269 | 270 | @Override 271 | public SocketAddress getLocalAddress() { 272 | if (localAddress != null) { 273 | return localAddress; 274 | } 275 | try { 276 | return channel.getLocalAddress(); 277 | } catch (IOException e) { 278 | return null; 279 | } 280 | } 281 | 282 | @Override 283 | public SocketAddress getRemoteAddress() { 284 | if (remoteAddress != null) { 285 | return remoteAddress; 286 | } 287 | try { 288 | return channel.getRemoteAddress(); 289 | } catch (IOException e) { 290 | return null; 291 | } 292 | } 293 | 294 | @Override 295 | public void setChannelName(String nameChannel) { 296 | this.nameChannel = nameChannel; 297 | } 298 | 299 | @Override 300 | public String getChannelName() { 301 | return nameChannel; 302 | } 303 | 304 | @Override 305 | public void setSession(ISession session) { 306 | this.session = session; 307 | } 308 | 309 | @Override 310 | public ISession getSession() { 311 | return session; 312 | } 313 | 314 | public void openServerMode() { 315 | this.serverMode = true; 316 | } 317 | 318 | @Override 319 | public void sync() { 320 | while (pool.isRun()) { 321 | try { 322 | Thread.sleep(5000); 323 | } catch (InterruptedException e) { 324 | e.printStackTrace(); 325 | } 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/ServerSocket.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.ServerSocketChannel; 7 | import java.nio.channels.SocketChannel; 8 | import java.util.List; 9 | 10 | import org.son.chat.common.net.config.SocketChannelConfig; 11 | import org.son.chat.common.net.core.coder.ICoderParserManager; 12 | import org.son.chat.common.net.core.handle.ClientManagerHandle; 13 | import org.son.chat.common.net.core.handle.ISocketHandle; 14 | import org.son.chat.common.net.core.handle.PipeHandle; 15 | import org.son.chat.common.net.core.session.ISession; 16 | import org.son.chat.common.net.core.session.ISessionFactory; 17 | import org.son.chat.common.net.core.socket.IServerSocketService; 18 | import org.son.chat.common.net.core.socket.ISocketPool; 19 | import org.son.chat.common.net.exception.NetException; 20 | import org.son.chat.common.net.util.NioUtil; 21 | 22 | /** 23 | * 服务端socket 是一个管理多个 客户端 socket 处理
24 | * 客户端socket 是一个对一个 socket 处理
25 | * 所以我认为 服务端socket其实是维护管理 多个 客户端 socket 这样就大量简化编写
26 | * 27 | * @author solq 28 | */ 29 | public class ServerSocket extends AbstractISocketChannel implements IServerSocketService { 30 | 31 | public static ServerSocket valueOf(SocketChannelConfig socketChannelConfig, int minPoolSize, int maxPoolSize, ICoderParserManager coderParserManager, ISessionFactory sessionFactory) { 32 | ServerSocket serverSocket = new ServerSocket(); 33 | serverSocket.minPoolSize = minPoolSize; 34 | serverSocket.maxPoolSize = maxPoolSize; 35 | serverSocket.socketChannelConfig = socketChannelConfig; 36 | serverSocket.coderParserManager = coderParserManager; 37 | serverSocket.sessionFactory = sessionFactory; 38 | return serverSocket; 39 | } 40 | 41 | private ServerSocketChannel socketChannel; 42 | private ISessionFactory sessionFactory; 43 | private ClientPipeChannel channelClients = new ClientPipeChannel(); 44 | private Thread shutdownHook; 45 | 46 | private int count; 47 | private int minPoolSize; 48 | private int maxPoolSize; 49 | private ISocketPool[] groupPool; 50 | 51 | @Override 52 | public void init() { 53 | try { 54 | groupPool = new SocketPool[minPoolSize]; 55 | for (int i = 0; i < minPoolSize; i++) { 56 | groupPool[i] = new SocketPool("server : " + i, null); 57 | } 58 | pool = new SocketPool("server accept", this); 59 | socketChannel = ServerSocketChannel.open(); 60 | socketChannel.configureBlocking(false); 61 | socketChannel.bind(socketChannelConfig.getAddress()); 62 | socketChannel.register(getSelector(), SelectionKey.OP_ACCEPT); 63 | handle = new PipeHandle(); 64 | ((PipeHandle) handle).register(new ClientManagerHandle(channelClients)); 65 | this.close = false; 66 | this.init = true; 67 | pool.init(); 68 | registerShutdownHook(); 69 | } catch (IOException e) { 70 | throw new NetException("初始化 NIO服务器异常 :", e); 71 | } 72 | } 73 | 74 | @Override 75 | public void doAccept(SelectionKey key) { 76 | System.out.println(" handleAccept "); 77 | ClientSocket clientSocket = null; 78 | try { 79 | SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); 80 | clientChannel.configureBlocking(false); 81 | ISocketPool pool = getNextPool(); 82 | clientSocket = ClientSocket.valueOfServer(SocketChannelConfig.valueOf(clientChannel.getRemoteAddress()), clientChannel,pool, coderParserManager, handle); 83 | 84 | final SocketChannelCtx ctx = clientSocket.getCtx(); 85 | clientSocket.openBefore(ctx); 86 | // 必须是新注册的 SelectionKey 87 | SelectionKey sk = clientChannel.register(pool.getSelector(), 0, clientSocket); 88 | clientSocket.setSelectionKey(sk); 89 | NioUtil.setOps(sk, SelectionKey.OP_READ); 90 | clientSocket.openAfter(ctx); 91 | pool.init(); 92 | 93 | } catch (IOException e) { 94 | if (clientSocket != null) { 95 | clientSocket.openError(clientSocket.getCtx()); 96 | } 97 | throw new NetException("Socket连接异常 : ", e); 98 | } 99 | } 100 | 101 | @Override 102 | public synchronized void stop() { 103 | if (socketChannel != null) { 104 | try { 105 | // 业务层通知 106 | List clients = channelClients.getAllClinetSockets(); 107 | for (ClientSocket client : clients) { 108 | client.stop(); 109 | } 110 | socketChannel.close(); 111 | } catch (IOException e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | pool.shutdown(); 116 | socketChannel = null; 117 | super.stop(); 118 | } 119 | 120 | @Override 121 | public void sendAll(Object message) { 122 | ByteBuffer byteBuffer = coderParserManager.encode(message, null); 123 | List clients = channelClients.getAllClinetSockets(); 124 | for (ClientSocket client : clients) { 125 | send(client, byteBuffer); 126 | } 127 | } 128 | 129 | @Override 130 | public void send(String channelName, Object message) { 131 | ByteBuffer byteBuffer = coderParserManager.encode(message, null); 132 | List clients = channelClients.getChannelClinetSockets(channelName); 133 | for (ClientSocket client : clients) { 134 | send(client, message, byteBuffer); 135 | } 136 | } 137 | 138 | @Override 139 | public void send(ClientSocket clientSocket, Object message) { 140 | clientSocket.send(message); 141 | } 142 | 143 | @Override 144 | public void send(ClientSocket clientSocket, Object message, ByteBuffer byteBuffer) { 145 | clientSocket.send(message, byteBuffer); 146 | } 147 | 148 | @Override 149 | public ClientSocket registerClient(SocketChannelConfig config) { 150 | System.out.println(" registerClient "); 151 | try { 152 | ClientSocket clientSocket = ClientSocket.valueOf(config, getNextPool(), this.coderParserManager, this.handle); 153 | clientSocket.openServerMode(); 154 | clientSocket.init(); 155 | return clientSocket; 156 | } catch (Exception e) { 157 | throw new NetException("Socket连接异常 : ", e); 158 | } 159 | } 160 | 161 | @Override 162 | public void registerHandle(ISocketHandle... handleArray) { 163 | for (ISocketHandle handle : handleArray) { 164 | ((PipeHandle) this.handle).register(handle); 165 | } 166 | } 167 | 168 | @Override 169 | public ISession createSession() { 170 | return sessionFactory.createSession(); 171 | } 172 | 173 | private void registerShutdownHook() { 174 | if (this.shutdownHook == null) { 175 | this.shutdownHook = new Thread() { 176 | @Override 177 | public void run() { 178 | ServerSocket.this.stop(); 179 | } 180 | }; 181 | Runtime.getRuntime().addShutdownHook(this.shutdownHook); 182 | } 183 | } 184 | 185 | @Override 186 | public ISocketPool getNextPool() { 187 | ISocketPool pool = getGroupPool()[count++ % getGroupPool().length]; 188 | return pool; 189 | } 190 | 191 | @Override 192 | public ISocketPool[] getGroupPool() { 193 | return groupPool; 194 | } 195 | 196 | public int getMinPoolSize() { 197 | return minPoolSize; 198 | } 199 | 200 | public int getMaxPoolSize() { 201 | return maxPoolSize; 202 | } 203 | 204 | @Override 205 | public void sync() { 206 | while (pool.isRun()) { 207 | try { 208 | Thread.sleep(5000); 209 | } catch (InterruptedException e) { 210 | e.printStackTrace(); 211 | } 212 | } 213 | while (true) { 214 | boolean sleep = false; 215 | for (ISocketPool sp : groupPool) { 216 | if (sp.isRun()) { 217 | sleep = true; 218 | break; 219 | } 220 | } 221 | if(!sleep){ 222 | break; 223 | } 224 | try { 225 | Thread.sleep(5000); 226 | } catch (InterruptedException e) { 227 | e.printStackTrace(); 228 | } 229 | } 230 | 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/SocketChannelCtx.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.son.chat.common.net.core.coder.ICoderCtx; 6 | 7 | /** 8 | * 主要负责记录读取包索引,会话数据等 9 | * 10 | * @author solq 11 | */ 12 | public class SocketChannelCtx implements ICoderCtx { 13 | 14 | public static SocketChannelCtx valueOf(ClientSocket clientSocket) { 15 | SocketChannelCtx result = new SocketChannelCtx(); 16 | result.clientSocket = clientSocket; 17 | result.readBuffer = result.createByteBuffer(result.maxReadBufferSize); 18 | return result; 19 | } 20 | 21 | /******** jdk nio byteBuffer ************/ 22 | private ByteBuffer readBuffer; 23 | 24 | private ClientSocket clientSocket; 25 | private int maxReadBufferSize = 1024 * 20; 26 | private int minReadBufferSize = 1024 * 8; 27 | 28 | private final static int MIN_MUT = 1422; 29 | private final static int DOUBLE_MUT = MIN_MUT * 2; 30 | /** socket read 索引 */ 31 | private int writeIndex = 0; 32 | 33 | /** 包索引 */ 34 | private int currPackageIndex = 0; 35 | 36 | public void send(Object message) { 37 | clientSocket.send(message); 38 | } 39 | 40 | public void close() { 41 | clientSocket.stop(); 42 | } 43 | 44 | /** 45 | * 更改下个数据包索引 46 | */ 47 | public synchronized void nextPackageIndex(long len) { 48 | if (len > 0) { 49 | currPackageIndex += len; 50 | } 51 | } 52 | 53 | /** 54 | * 准备开始读数据处理 55 | */ 56 | public synchronized ByteBuffer readBegin() { 57 | readBuffer.clear(); 58 | readBuffer.position(writeIndex); 59 | 60 | final int unUseSize = readBuffer.capacity() - writeIndex; 61 | boolean isExt = false; 62 | int bufferSize = maxReadBufferSize; 63 | 64 | // 剩余容量低于最少边界 65 | if (DOUBLE_MUT > unUseSize) { 66 | // 2倍方式扩容 67 | final int usePackageSize = writeIndex - this.currPackageIndex; 68 | final int doubleUsePackageSize = usePackageSize * 2; 69 | if (doubleUsePackageSize > readBuffer.capacity()) { 70 | bufferSize = readBuffer.capacity() << 1; 71 | } else { 72 | bufferSize = Math.max(doubleUsePackageSize, maxReadBufferSize); 73 | } 74 | isExt = true; 75 | 76 | } else if (unUseSize > maxReadBufferSize) { // 剩余容量高于最大边界 缩容处理 77 | final int usePackageSize = writeIndex - this.currPackageIndex; 78 | final int doubleUsePackageSize = usePackageSize * 2; 79 | if (maxReadBufferSize > doubleUsePackageSize) { 80 | bufferSize = maxReadBufferSize; 81 | isExt = true; 82 | } 83 | } 84 | 85 | if (isExt) { 86 | extendByteBuffer(bufferSize); 87 | } 88 | 89 | return readBuffer; 90 | } 91 | 92 | private void extendByteBuffer(int bufferSize) { 93 | ByteBuffer newReadBuffer = createByteBuffer(bufferSize); 94 | // 从最后包标记开始复制 95 | newReadBuffer.put(readBuffer.array(), this.currPackageIndex, this.writeIndex - this.currPackageIndex); 96 | writeIndex = 0; 97 | currPackageIndex = 0; 98 | readBuffer = newReadBuffer; 99 | } 100 | 101 | public synchronized ByteBuffer coderBegin() { 102 | readBuffer.clear().position(currPackageIndex).limit(writeIndex); 103 | return readBuffer.asReadOnlyBuffer(); 104 | } 105 | 106 | // 模拟socket read 107 | public synchronized int readBuffer(ByteBuffer buffer) { 108 | buffer.flip(); 109 | readBuffer.clear(); 110 | readBuffer.position(writeIndex); 111 | readBuffer.put(buffer); 112 | return buffer.limit(); 113 | } 114 | 115 | /** 116 | * 更改NIO 每次读索引 117 | */ 118 | public synchronized void readEnd(long len) { 119 | if (len > 0) { 120 | writeIndex += len; 121 | readBuffer.limit(writeIndex); 122 | } 123 | } 124 | 125 | private ByteBuffer createByteBuffer(int maxReadBufferSize) { 126 | return ByteBuffer.allocate(maxReadBufferSize); 127 | } 128 | 129 | // getter 130 | 131 | public int getWriteIndex() { 132 | return writeIndex; 133 | } 134 | 135 | public int getMaxReadBufferSize() { 136 | return maxReadBufferSize; 137 | } 138 | 139 | public int getMinReadBufferSize() { 140 | return minReadBufferSize; 141 | } 142 | 143 | public int getCurrPackageIndex() { 144 | return currPackageIndex; 145 | } 146 | 147 | public ClientSocket getClientSocket() { 148 | return clientSocket; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/core/socket/impl/SocketPool.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.core.socket.impl; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.CancelledKeyException; 5 | import java.nio.channels.SelectionKey; 6 | import java.nio.channels.Selector; 7 | import java.util.Iterator; 8 | import java.util.LinkedList; 9 | import java.util.concurrent.locks.ReentrantLock; 10 | 11 | import org.son.chat.common.net.core.socket.ISocketChannel; 12 | import org.son.chat.common.net.core.socket.ISocketPool; 13 | 14 | /** 15 | * 单线程,参考NETTY 单线程组串行处理 16 | * 17 | * @author solq 18 | */ 19 | public class SocketPool implements ISocketPool, Runnable { 20 | 21 | private static final int SELECTOR_AUTO_REBUILD_THRESHOLD = 512; 22 | private Selector selector; 23 | private Thread thread; 24 | private LinkedList queue = new LinkedList<>(); 25 | private ReentrantLock lock = new ReentrantLock(); 26 | 27 | private ISocketChannel serverChannel; 28 | 29 | private String name; 30 | /** 执行标志 */ 31 | private boolean run = false; 32 | private boolean init = false; 33 | 34 | private long lastExecutionTime; 35 | /** 每提交个任务,触发 {@link Selector#selectNow()} */ 36 | private boolean needsToSelectAgain; 37 | 38 | public SocketPool(String name, ISocketChannel serverChannel) { 39 | this.serverChannel = serverChannel; 40 | this.name = name; 41 | try { 42 | selector = Selector.open(); 43 | } catch (IOException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | @Override 49 | public void execute(Runnable task) { 50 | if (!run) { 51 | return; 52 | } 53 | lock.lock(); 54 | try { 55 | queue.add(task); 56 | } finally { 57 | lock.unlock(); 58 | } 59 | if (needsToSelectAgain) { 60 | needsToSelectAgain = false; 61 | try { 62 | selector.selectNow(); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | } 68 | 69 | private Runnable take() { 70 | lock.lock(); 71 | try { 72 | if (queue.size() == 0) { 73 | return null; 74 | } 75 | return queue.removeFirst(); 76 | } finally { 77 | lock.unlock(); 78 | } 79 | } 80 | 81 | private void processNio() { 82 | select(); 83 | processKeys(); 84 | } 85 | 86 | private void processKeys() { 87 | for (Iterator i = selector.selectedKeys().iterator(); i.hasNext();) { 88 | // 得到下一个Key 89 | SelectionKey key = i.next(); 90 | try { 91 | handle(key); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | i.remove(); 96 | } 97 | } 98 | 99 | /** 100 | * 参考 netty : NioEventLoop 101 | * */ 102 | @Override 103 | public void select() { 104 | // select 105 | // http://goldendoc.iteye.com/blog/1152079 106 | // http://www.iteye.com/topic/650195 107 | 108 | try { 109 | int selectCnt = 0; 110 | long currentTimeNanos = System.nanoTime(); 111 | long selectDeadLineNanos = currentTimeNanos+1000000L*1000; 112 | for (;;) { 113 | long timeoutMillis = (selectDeadLineNanos-currentTimeNanos + 500000L) / 1000000L; 114 | if (timeoutMillis <= 0) { 115 | if (selectCnt == 0) { 116 | //System.out.println("timeoutMillis : " +timeoutMillis + " name :" +this.name + " selectCnt :" + selectCnt); 117 | 118 | selector.selectNow(); 119 | selectCnt = 1; 120 | } 121 | break; 122 | } 123 | //System.out.println("timeoutMillis : " +timeoutMillis + " name :" +this.name + " selectCnt :" + selectCnt); 124 | int selectedKeys = selector.select(timeoutMillis); 125 | selectCnt++; 126 | // || oldWakenUp || wakenUp.get() || hasTasks() 127 | if (selectedKeys != 0 || taskCount()>0) { 128 | break; 129 | } 130 | 131 | // cpu 100%处理 132 | if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { 133 | 134 | // rebuildSelector(); 135 | // selector = this.selector; 136 | // 137 | // // Select again to populate selectedKeys. 138 | // selector.selectNow(); 139 | // selectCnt = 1; 140 | // break; 141 | System.out.println("selectCnt 超过 SELECTOR_AUTO_REBUILD_THRESHOLD"); 142 | } 143 | 144 | currentTimeNanos = System.nanoTime(); 145 | } 146 | } catch (IOException e) { 147 | e.printStackTrace(); 148 | } 149 | 150 | } 151 | 152 | private void handle(SelectionKey key) { 153 | ISocketChannel channel = (ISocketChannel) key.attachment(); 154 | if (!key.isValid()) { 155 | System.out.println("error key"); 156 | if (channel != null) { 157 | channel.doClose(key); 158 | } 159 | return; 160 | } 161 | // 可能存在一次在channel 注册多个操作 不能用 elseif 162 | try { 163 | if (key.isAcceptable()) { 164 | serverChannel.doAccept(key); 165 | } 166 | if (key.isConnectable()) { 167 | if (serverChannel != null) { 168 | serverChannel.doConnect(key); 169 | } else { 170 | channel.doConnect(key); 171 | } 172 | } 173 | if (key.isReadable()) { 174 | if (serverChannel != null) { 175 | serverChannel.doRead(key); 176 | } else { 177 | channel.doRead(key); 178 | } 179 | } 180 | // 不清楚直接 isWritable 会抛异常 181 | if (key.isValid() && key.isWritable()) { 182 | if (serverChannel != null) { 183 | serverChannel.doWrite(key); 184 | } else { 185 | channel.doWrite(key); 186 | } 187 | } 188 | } catch (CancelledKeyException e) { // key 消除事件 189 | e.printStackTrace(); 190 | if (channel != null) { 191 | channel.doClose(key); 192 | } 193 | } 194 | } 195 | 196 | private void processTask(long timeoutNanos) { 197 | //System.out.println("processTask"); 198 | 199 | // 处理任务计算器 200 | int count = 0; 201 | final long deadline = System.nanoTime() + timeoutNanos; 202 | while (true) { 203 | try { 204 | Runnable task = take(); 205 | if (task == null) { 206 | break; 207 | } 208 | task.run(); 209 | } catch (Exception e) { 210 | // logger.error("同步队列[" + key + "]处理线程出现未知错误", e); 211 | } 212 | // 延时退出 213 | if ((count++ % 255) == 0) { 214 | if (System.nanoTime() > deadline) { 215 | break; 216 | } 217 | } 218 | } 219 | this.lastExecutionTime = System.nanoTime(); 220 | } 221 | 222 | /*** 223 | * 参考 netty : NioEventLoop 224 | * */ 225 | @Override 226 | public void run() { 227 | while (true) { 228 | 229 | /** 230 | * 优先级处理io任务 -> 非IO任务 -> 停服处理
231 | * 1.先判断是否有非IO任务 ? 马上映醒selectNow() : 阻塞 线程
232 | * 2.当连继出现 select(timeOut) ==0 超过512默认边界即认为出现 epoll的cpu 100% bug 进行 233 | * rebuild selector 处理
234 | * 3.计算执行IO任务时间,然后按比例预计非 io任务执行时间
235 | * 4.非io任务执行次数超过64 并且超过执行时间退出
236 | * */ 237 | final long ioStartTime = System.nanoTime(); 238 | processNio(); 239 | needsToSelectAgain = false; 240 | 241 | final long ioTime = System.nanoTime() - ioStartTime; 242 | final int ioRatio = 50; 243 | // task 执行时间占nio 时间比 244 | final long taskTime = ioTime * (100 - ioRatio) / ioRatio; 245 | processTask(taskTime); 246 | // 判断关闭退出 247 | if (!run) { 248 | queue.clear(); 249 | break; 250 | } 251 | 252 | } 253 | } 254 | 255 | @Override 256 | public void shutdown() { 257 | run = false; 258 | } 259 | 260 | @Override 261 | public Selector getSelector() { 262 | return selector; 263 | } 264 | 265 | @Override 266 | public int taskCount() { 267 | return queue.size(); 268 | } 269 | 270 | @Override 271 | public boolean isRun() { 272 | return run; 273 | } 274 | 275 | @Override 276 | public void init() { 277 | if (this.init) { 278 | return; 279 | } 280 | synchronized (this) { 281 | if (this.init) { 282 | return; 283 | } 284 | this.init = true; 285 | run = true; 286 | thread = new Thread(this, name); 287 | thread.setDaemon(true); 288 | thread.start(); 289 | } 290 | } 291 | 292 | } 293 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/exception/CoderException.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.exception; 2 | 3 | /** 4 | * 编/解码模块异常 5 | * 6 | * @author solq 7 | */ 8 | public class CoderException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = -5467773602825684956L; 11 | 12 | public CoderException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public CoderException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public CoderException(String message) { 21 | super(message); 22 | } 23 | 24 | public CoderException() { 25 | super(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/exception/NetException.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.exception; 2 | 3 | /** 4 | * 网络模块异常 5 | * 6 | * @author solq 7 | */ 8 | public class NetException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = -8019221448806371216L; 11 | 12 | public NetException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public NetException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public NetException(String message) { 21 | super(message); 22 | } 23 | 24 | public NetException() { 25 | super(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/util/ByteHelper.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.util; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author solq 7 | */ 8 | public abstract class ByteHelper { 9 | public static byte[] int32Tobyte(int value) { 10 | byte[] bytes = new byte[4]; 11 | bytes[0] = (byte) ((value >>> 24) & 0xff); 12 | bytes[1] = (byte) ((value >>> 16) & 0xff); 13 | bytes[2] = (byte) ((value >>> 8) & 0xff); 14 | bytes[3] = (byte) (value & 0xff); 15 | return bytes; 16 | } 17 | 18 | public static int byteToint32(byte[] bytes) { 19 | int result = (bytes[0] & 0xff) << 24; 20 | result |= (bytes[1] & 0xff) << 16; 21 | result |= (bytes[2] & 0xff) << 8; 22 | result |= bytes[3] & 0xff; 23 | return result; 24 | } 25 | 26 | public static byte[] int16Tobyte(int value) { 27 | byte[] bytes = new byte[2]; 28 | bytes[0] = (byte) ((value >>> 8) & 0xff); 29 | bytes[1] = (byte) (value & 0xff); 30 | return bytes; 31 | } 32 | 33 | public static int byteToint16(byte[] bytes) { 34 | int result = (bytes[0] & 0xff) << 8; 35 | result |= bytes[1] & 0xff; 36 | return result; 37 | } 38 | 39 | public static byte[] readBytes(byte[] bytes, int start, int offSet) { 40 | byte[] result = new byte[offSet]; 41 | System.arraycopy(bytes, start, result, 0, offSet); 42 | return result; 43 | } 44 | 45 | public static byte[] merge(List array) { 46 | int size = 0; 47 | int len = array.size(); 48 | for (int i = 0; i < len; i++) { 49 | size += array.get(i).length; 50 | } 51 | byte[] result = new byte[size]; 52 | int count = 0; 53 | for (int i = 0; i < len; i++) { 54 | System.arraycopy(array.get(i), 0, result, count, array.get(i).length); 55 | count += array.get(i).length; 56 | } 57 | return result; 58 | } 59 | 60 | public static byte[] merge(byte[]... array) { 61 | int size = 0; 62 | for (int i = 0; i < array.length; i++) { 63 | size += array[i].length; 64 | } 65 | byte[] result = new byte[size]; 66 | int count = 0; 67 | for (int i = 0; i < array.length; i++) { 68 | System.arraycopy(array[i], 0, result, count, array[i].length); 69 | count += array[i].length; 70 | } 71 | return result; 72 | } 73 | 74 | public static final String bytesToHexString(byte[] bArray, int begin, int end) { 75 | StringBuffer sb = new StringBuffer(bArray.length); 76 | String sTemp; 77 | for (int i = begin; i < end; i++) { 78 | sTemp = Integer.toHexString(0xFF & bArray[i]); 79 | if (sTemp.length() < 2) 80 | sb.append(0); 81 | sb.append(sTemp.toUpperCase()); 82 | sb.append(" "); 83 | } 84 | return sb.toString(); 85 | } 86 | 87 | /** 88 | * @param ip 89 | * ip的字节数组形式 90 | * @return 字符串形式的ip 91 | */ 92 | public static String byteToIp(byte[] ip) { 93 | StringBuilder sb = new StringBuilder(); 94 | sb.append(ip[0] & 0xFF); 95 | sb.append('.'); 96 | sb.append(ip[1] & 0xFF); 97 | sb.append('.'); 98 | sb.append(ip[2] & 0xFF); 99 | sb.append('.'); 100 | sb.append(ip[3] & 0xFF); 101 | return sb.toString(); 102 | } 103 | 104 | /** 105 | * A 是否包含 B 106 | */ 107 | public static boolean contains(byte[] A, byte[] B) { 108 | return indexOf(A, B) > -1; 109 | } 110 | 111 | public static int indexOf(byte[] A, byte[] B) { 112 | if (B.length > A.length) { 113 | return -1; 114 | } 115 | int result = 0; 116 | TO: for (int lastIndex = 0; lastIndex < A.length; lastIndex++) { 117 | result = lastIndex; 118 | if (B[0] == A[lastIndex]) { 119 | // next 120 | for (int n = 1; n < B.length; n++) { 121 | lastIndex++; 122 | if (lastIndex >= A.length) { 123 | return -1; 124 | } 125 | if (B[n] != A[lastIndex]) { 126 | continue TO; 127 | } 128 | } 129 | return result; 130 | } 131 | } 132 | return -1; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.util; 2 | 3 | import java.net.SocketAddress; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @author solq 8 | * */ 9 | public abstract class IpUtil { 10 | private static final int INDEX_NOT_FOUND = -1; 11 | 12 | /** 13 | * 获取会话的IP地址 14 | */ 15 | public static String getIp(SocketAddress address) { 16 | if (address == null) { 17 | return null; 18 | } 19 | String ip = address.toString(); 20 | return substringBetween(ip, "/", ":"); 21 | } 22 | 23 | public static String getAddress(SocketAddress address) { 24 | if (address == null) { 25 | return null; 26 | } 27 | String ip = address.toString(); 28 | if (ip == null) { 29 | return null; 30 | } 31 | int start = ip.indexOf("/"); 32 | if (start != INDEX_NOT_FOUND) { 33 | return ip.substring(start + 1); 34 | } 35 | return null; 36 | } 37 | 38 | /*** ip地址正则配置 **/ 39 | public final static Pattern ipPattern = Pattern.compile("^([0-9\\*]+\\.){3}[0-9\\*]+$"); 40 | 41 | /** 42 | * 验证是否合法的IP 43 | */ 44 | public static boolean isValidIp(String ip) { 45 | return ipPattern.matcher(ip).matches(); 46 | } 47 | 48 | public static String substringBetween(String str, String open, String close) { 49 | if (str == null || open == null || close == null) { 50 | return null; 51 | } 52 | int start = str.indexOf(open); 53 | if (start != INDEX_NOT_FOUND) { 54 | int end = str.indexOf(close, start + open.length()); 55 | if (end != INDEX_NOT_FOUND) { 56 | return str.substring(start + open.length(), end); 57 | } 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/util/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.util; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | /** 7 | * 可命名线程 8 | * 9 | * @author solq 10 | */ 11 | public class NamedThreadFactory implements ThreadFactory { 12 | 13 | private final String name; 14 | private final AtomicInteger count = new AtomicInteger(); 15 | 16 | public NamedThreadFactory(String name) { 17 | this.name = name; 18 | } 19 | 20 | @Override 21 | public Thread newThread(Runnable r) { 22 | final String name = this.name + ":" + count.getAndIncrement(); 23 | return new Thread(r, name); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/net/util/NioUtil.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.net.util; 2 | 3 | import java.nio.channels.ClosedChannelException; 4 | import java.nio.channels.SelectionKey; 5 | import java.nio.channels.Selector; 6 | import java.nio.channels.SocketChannel; 7 | 8 | /** 9 | * @author solq 10 | */ 11 | public abstract class NioUtil { 12 | public static void registerSelectionKey(SelectionKey key, Selector selector, int ops) { 13 | SocketChannel channel = (SocketChannel) key.channel(); 14 | try { 15 | channel.register(selector, ops); 16 | } catch (ClosedChannelException e) { 17 | e.printStackTrace(); 18 | } 19 | } 20 | 21 | public static void registerSelectionKey(SocketChannel channel, Selector selector, int ops) { 22 | try { 23 | channel.register(selector, ops); 24 | } catch (ClosedChannelException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | /** 30 | * @param key 31 | * @param ops 32 | * {@link SelectionKey} 33 | */ 34 | public static void setOps(SelectionKey key, int... ops) { 35 | final int interestOps = key.interestOps(); 36 | for (int op : ops) { 37 | if ((interestOps & op) == 0) { 38 | key.interestOps(interestOps | op); 39 | } 40 | } 41 | } 42 | 43 | /*** 44 | * 清除操作标志,让选择器不做处理 45 | */ 46 | public static void clearOps(SelectionKey key, int... ops) { 47 | final int interestOps = key.interestOps(); 48 | for (int op : ops) { 49 | if ((interestOps & op) != 0) { 50 | key.interestOps(interestOps & ~op); 51 | } 52 | } 53 | } 54 | 55 | public static void setOpWrite(SelectionKey key) { 56 | setOps(key, SelectionKey.OP_WRITE); 57 | } 58 | 59 | public static void clearOpWrite(SelectionKey key) { 60 | clearOps(key, SelectionKey.OP_WRITE); 61 | } 62 | 63 | public static void printlnOps(SelectionKey key) { 64 | final boolean acceptable = key.isAcceptable(); 65 | final boolean connectable = key.isConnectable(); 66 | final boolean readable = key.isReadable(); 67 | final boolean writable = key.isWritable(); 68 | System.out.println("acceptable : " + acceptable + " connectable : " + connectable + " readable : " + readable + " writable : " + writable); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/protocol/ChatHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.protocol; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import org.son.chat.common.net.core.coder.ICoderCtx; 6 | import org.son.chat.common.net.core.coder.IHandle; 7 | 8 | /** 9 | * 简单聊天业务处理 10 | * @author solq 11 | */ 12 | public class ChatHandle implements IHandle { 13 | 14 | @Override 15 | public byte[] encode(String value, ICoderCtx ctx) { 16 | try { 17 | return value.getBytes("utf-8"); 18 | } catch (UnsupportedEncodingException e) { 19 | e.printStackTrace(); 20 | return null; 21 | } 22 | } 23 | 24 | @Override 25 | public String decode(byte[] value, ICoderCtx ctx) { 26 | try { 27 | return new String(value, "utf-8"); 28 | } catch (UnsupportedEncodingException e) { 29 | e.printStackTrace(); 30 | return null; 31 | } 32 | } 33 | 34 | @Override 35 | public boolean verify(Object value, ICoderCtx ctx) { 36 | // 测试不做验证 37 | return true; 38 | } 39 | 40 | @Override 41 | public void handle(String value, ICoderCtx ctx) { 42 | System.out.println("接收到信息 : " + value); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /common/src/main/java/org/son/chat/common/protocol/PackageDefaultCoder.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common.protocol; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.son.chat.common.net.core.coder.ICoderCtx; 6 | import org.son.chat.common.net.core.coder.IPackageCoder; 7 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 8 | import org.son.chat.common.net.util.ByteHelper; 9 | 10 | /** 11 | * 包封装 [包头标识] + [包长度] + [包内容] 校验值暂时不处理 12 | * 13 | * @author solq 14 | */ 15 | public class PackageDefaultCoder implements IPackageCoder { 16 | 17 | /** 包头标识 **/ 18 | private byte[] HEAD_MARK = { 1, 0 }; 19 | /** 请求包最大长度 **/ 20 | private int MAX_REQUEST_LENGTH = 1024 * 1024 * 5; 21 | 22 | // //////////////////////静态构造////////////////////////////// 23 | public static PackageDefaultCoder valueOf() { 24 | PackageDefaultCoder result = new PackageDefaultCoder(); 25 | return result; 26 | } 27 | 28 | public static PackageDefaultCoder valueOf(int maxRequestLength, byte[] headMak) { 29 | PackageDefaultCoder result = new PackageDefaultCoder(); 30 | result.HEAD_MARK = headMak; 31 | result.MAX_REQUEST_LENGTH = maxRequestLength; 32 | return result; 33 | } 34 | 35 | /** 36 | * 将消息打包给NIO 发送 37 | */ 38 | @Override 39 | public ByteBuffer encode(byte[] value, ICoderCtx ctx) { 40 | final int bufSize = HEAD_MARK.length + 4 + value.length; 41 | ByteBuffer bb = ByteBuffer.allocate(bufSize); 42 | bb.put(HEAD_MARK); 43 | bb.putInt(value.length); 44 | bb.put(value); 45 | return bb; 46 | } 47 | 48 | /** 49 | * 把包提取出消息给下一次解码器处理 50 | */ 51 | @Override 52 | public byte[] decode(ByteBuffer value, ICoderCtx ctx) { 53 | SocketChannelCtx socketChannelCtx = (SocketChannelCtx) ctx; 54 | value.reset(); 55 | // 包长度判断 56 | final int readSize = value.remaining() - HEAD_MARK.length; 57 | // 半包 58 | if (readSize <= 0) { 59 | return null; 60 | } 61 | 62 | // 草,getInt 不会移动索引 63 | final int bodyLen = value.getInt(value.position() + HEAD_MARK.length); 64 | // 半包 65 | if (bodyLen > readSize) { 66 | System.out.println("半包"); 67 | return null; 68 | } 69 | // TODO 70 | if (bodyLen > MAX_REQUEST_LENGTH) { 71 | // TODO 非法请求 72 | } 73 | 74 | byte[] result = new byte[bodyLen]; 75 | // 不同 ByteBuffer getBytes 的时候不会移动当前索引 真 恶心 76 | final int bodyPosition = value.position() + HEAD_MARK.length + 4; 77 | value.position(bodyPosition); 78 | value.get(result); 79 | 80 | final int used = HEAD_MARK.length + 4 + bodyLen; 81 | // 更新已读索引 82 | socketChannelCtx.nextPackageIndex(used); 83 | return result; 84 | } 85 | 86 | @Override 87 | public boolean verify(ByteBuffer value, ICoderCtx ctx) { 88 | value.reset(); 89 | // 包长度判断 90 | final int readSize = value.remaining() - HEAD_MARK.length; 91 | // 半包 92 | if (readSize <= 0) { 93 | return false; 94 | } 95 | byte[] headMark = new byte[HEAD_MARK.length]; 96 | value.get(headMark); 97 | // 头标识判断 98 | final boolean isRight = ByteHelper.contains(headMark, HEAD_MARK); 99 | return isRight; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /common/src/test/java/org/son/chat/common/ChatTestServerHandle.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common; 2 | 3 | import java.util.Date; 4 | import java.util.Timer; 5 | import java.util.TimerTask; 6 | 7 | import org.son.chat.common.net.core.coder.IHandle; 8 | import org.son.chat.common.net.core.coder.ICoderCtx; 9 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 10 | 11 | /** 12 | * 简单聊天业务处理 13 | * 14 | * @author solq 15 | */ 16 | public class ChatTestServerHandle implements IHandle { 17 | 18 | @Override 19 | public byte[] encode(String value, ICoderCtx ctx) { 20 | return value.getBytes(); 21 | } 22 | 23 | @Override 24 | public String decode(byte[] value, ICoderCtx ctx) { 25 | return new String(value); 26 | } 27 | 28 | @Override 29 | public boolean verify(Object value, ICoderCtx ctx) { 30 | // 测试不做验证 31 | return true; 32 | } 33 | 34 | @Override 35 | public void handle(String value, ICoderCtx ctx) { 36 | System.out.println("接收到信息 : "); 37 | final SocketChannelCtx socketChannelCtx = (SocketChannelCtx) ctx; 38 | // 回写自己处理 39 | Timer timer = new Timer(); 40 | timer.scheduleAtFixedRate(new TimerTask() { 41 | 42 | private int sendCount = 0; 43 | 44 | @Override 45 | public void run() { 46 | sendCount++; 47 | for (int i = 0; i < 10; i++) { 48 | socketChannelCtx.send(" 发送数据 : " + i + " 次数 : " + sendCount); 49 | } 50 | System.out.println(" push message"); 51 | } 52 | }, new Date(), 5000L); 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /common/src/test/java/org/son/chat/common/TestByteBuffer.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.son.chat.common.net.core.coder.impl.CoderParser; 8 | import org.son.chat.common.net.core.coder.impl.CoderParserManager; 9 | import org.son.chat.common.net.core.coder.impl.CoderResult; 10 | import org.son.chat.common.net.core.socket.impl.SocketChannelCtx; 11 | import org.son.chat.common.net.exception.CoderException; 12 | import org.son.chat.common.net.util.ByteHelper; 13 | import org.son.chat.common.protocol.ChatHandle; 14 | import org.son.chat.common.protocol.PackageDefaultCoder; 15 | 16 | /** 17 | * @author solq 18 | */ 19 | public class TestByteBuffer { 20 | 21 | @Test 22 | public void testByteBuffer() { 23 | ByteBuffer bb = ByteBuffer.allocate(50); 24 | bb.put((byte) 1); 25 | bb.put((byte) 2); 26 | bb.put((byte) 3); 27 | bb.put((byte) 4); 28 | bb.put((byte) 5); 29 | Assert.assertEquals(3, bb.get(2)); 30 | } 31 | 32 | @Test 33 | public void testAutoExtend() { 34 | ByteBuffer bb = ByteBuffer.allocate(1); 35 | bb.put((byte) 1); 36 | bb.put((byte) 2); 37 | bb.put((byte) 3); 38 | bb.put((byte) 4); 39 | bb.put((byte) 5); 40 | } 41 | 42 | @Test 43 | public void testSharedBuffer() { 44 | ByteBuffer bb = ByteBuffer.allocate(50); 45 | bb.put((byte) 1); 46 | bb.put((byte) 2); 47 | bb.put((byte) 3); 48 | 49 | ByteBuffer cloneBB = bb.asReadOnlyBuffer(); 50 | bb.put((byte) 4); 51 | 52 | Assert.assertEquals(4, cloneBB.get(3)); 53 | Assert.assertEquals(4, cloneBB.get(3)); 54 | 55 | cloneBB.position(3); 56 | bb.position(5); 57 | Assert.assertEquals(4, cloneBB.get()); 58 | 59 | } 60 | 61 | @Test 62 | public void testByteContains() { 63 | byte[] b1 = { 48, 1, 0, 0 }; 64 | byte[] b2 = { 0, 1 }; 65 | boolean flag = ByteHelper.contains(b2, b1); 66 | Assert.assertEquals(false, flag); 67 | } 68 | 69 | @Test 70 | public void testByteBufferPutInt() { 71 | String body = "发送数据 : 11"; 72 | int bodyLength = body.getBytes().length; 73 | ByteBuffer bb = ByteBuffer.allocate(2 + 4 + bodyLength); 74 | bb.put((byte) 1); 75 | bb.put((byte) 2); 76 | bb.putInt(bodyLength); 77 | bb.put(body.getBytes()); 78 | 79 | bb.flip(); 80 | 81 | Assert.assertEquals(bodyLength, bb.getInt(2)); 82 | 83 | } 84 | 85 | @Test 86 | public void testSend() throws InterruptedException { 87 | CoderParserManager coderParserManager = new CoderParserManager(); 88 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 89 | SocketChannelCtx socketChannelCtx = SocketChannelCtx.valueOf(null); 90 | 91 | int count = 0; 92 | while (true) { 93 | count++; 94 | ByteBuffer sendBuffer = coderParserManager.encode("发送数据 : " + count, socketChannelCtx); 95 | ByteBuffer buffer = socketChannelCtx.readBegin(); 96 | // 模拟socket read 97 | int writeSize = socketChannelCtx.readBuffer(sendBuffer); 98 | socketChannelCtx.readEnd(writeSize); 99 | boolean run = true; 100 | // 粘包处理 101 | while (run) { 102 | buffer = socketChannelCtx.coderBegin(); 103 | buffer.mark(); 104 | CoderResult coderResult = coderParserManager.decode(buffer, socketChannelCtx); 105 | switch (coderResult.getValue()) { 106 | case SUCCEED: 107 | break; 108 | case NOT_FIND_CODER: 109 | final int readySize = socketChannelCtx.getWriteIndex() - socketChannelCtx.getCurrPackageIndex(); 110 | final int headLimit = 1024; 111 | if (readySize >= headLimit) { 112 | throw new CoderException("未找到编/解码处理器 "); 113 | } 114 | run = false; 115 | 116 | break; 117 | case UNFINISHED: 118 | case UNKNOWN: 119 | case ERROR: 120 | default: 121 | run = false; 122 | // TODO throw 123 | break; 124 | } 125 | } 126 | 127 | if (count % 500 == 0) { 128 | System.out.println("readBuffer capacity :" + buffer.capacity()); 129 | Thread.sleep(200); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /common/src/test/java/org/son/chat/common/TestNioClient.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | 6 | import org.junit.Test; 7 | import org.son.chat.common.net.config.SocketChannelConfig; 8 | import org.son.chat.common.net.core.coder.ICoderParserManager; 9 | import org.son.chat.common.net.core.coder.impl.CoderParser; 10 | import org.son.chat.common.net.core.coder.impl.CoderParserManager; 11 | import org.son.chat.common.net.core.handle.EmptyHandle; 12 | import org.son.chat.common.net.core.handle.HeartHandle; 13 | import org.son.chat.common.net.core.handle.SessionHandle; 14 | import org.son.chat.common.net.core.session.ISessionFactory; 15 | import org.son.chat.common.net.core.session.SessionFactory; 16 | import org.son.chat.common.net.core.session.SessionKey; 17 | import org.son.chat.common.net.core.socket.impl.ClientSocket; 18 | import org.son.chat.common.net.core.socket.impl.ServerSocket; 19 | import org.son.chat.common.net.core.socket.impl.SocketPool; 20 | import org.son.chat.common.net.util.IpUtil; 21 | import org.son.chat.common.protocol.ChatHandle; 22 | import org.son.chat.common.protocol.PackageDefaultCoder; 23 | 24 | public class TestNioClient { 25 | 26 | @Test 27 | public void normal() { 28 | ICoderParserManager coderParserManager = new CoderParserManager(); 29 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 30 | final ClientSocket clientSocket = ClientSocket.valueOf(SocketChannelConfig.valueOf(6969), new SocketPool("client", null), coderParserManager, new EmptyHandle()); 31 | 32 | Timer timer = new Timer(); 33 | timer.schedule(new TimerTask() { 34 | 35 | @Override 36 | public void run() { 37 | clientSocket.send("连接服务器成功"); 38 | System.out.println("send "); 39 | this.cancel(); 40 | } 41 | }, 1000); 42 | 43 | clientSocket.start(); 44 | clientSocket.sync(); 45 | clientSocket.stop(); 46 | } 47 | 48 | @Test 49 | public void serverMode() { 50 | ICoderParserManager coderParserManager = new CoderParserManager(); 51 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 52 | ISessionFactory sessionFactory = new SessionFactory(); 53 | final ServerSocket serverSocket = ServerSocket.valueOf(SocketChannelConfig.valueOf(8888), 10, 20, coderParserManager, sessionFactory); 54 | 55 | Timer timer = new Timer(); 56 | timer.schedule(new TimerTask() { 57 | 58 | @Override 59 | public void run() { 60 | System.out.println("registerClientSocket"); 61 | ClientSocket clientSocket = serverSocket.registerClient(SocketChannelConfig.valueOf(6969)); 62 | clientSocket.send("连接服务器成功"); 63 | this.cancel(); 64 | } 65 | }, 1000); 66 | 67 | serverSocket.start(); 68 | serverSocket.sync(); 69 | serverSocket.stop(); 70 | } 71 | 72 | @Test 73 | public void testGetIp() { 74 | ICoderParserManager coderParserManager = new CoderParserManager(); 75 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 76 | ISessionFactory sessionFactory = new SessionFactory(); 77 | final ServerSocket serverSocket = ServerSocket.valueOf(SocketChannelConfig.valueOf(8888), 10, 20, coderParserManager, sessionFactory); 78 | 79 | Timer timer = new Timer(); 80 | timer.schedule(new TimerTask() { 81 | 82 | @Override 83 | public void run() { 84 | System.out.println("registerClientSocket"); 85 | ClientSocket clientSocket = serverSocket.registerClient(SocketChannelConfig.valueOf(6969)); 86 | 87 | clientSocket.stop(); 88 | System.out.println(" ip : " + IpUtil.getAddress(clientSocket.getRemoteAddress())); 89 | this.cancel(); 90 | } 91 | }, 1000); 92 | 93 | serverSocket.start(); 94 | serverSocket.sync(); 95 | serverSocket.stop(); 96 | } 97 | 98 | @Test 99 | public void testHeart() { 100 | ICoderParserManager coderParserManager = new CoderParserManager(); 101 | coderParserManager.register(CoderParser.valueOf("chat", PackageDefaultCoder.valueOf(), new ChatHandle())); 102 | ISessionFactory sessionFactory = new SessionFactory(); 103 | final ServerSocket serverSocket = ServerSocket.valueOf(SocketChannelConfig.valueOf(8888), 10, 20, coderParserManager, sessionFactory); 104 | serverSocket.init(); 105 | serverSocket.registerHandle(new SessionHandle(sessionFactory), new HeartHandle(5000)); 106 | 107 | Timer timer = new Timer(); 108 | timer.schedule(new TimerTask() { 109 | 110 | @Override 111 | public void run() { 112 | System.out.println("registerClientSocket"); 113 | ClientSocket clientSocket = serverSocket.registerClient(SocketChannelConfig.valueOf(6969)); 114 | clientSocket.send("连接服务器成功"); 115 | 116 | System.out.println("heart time : " + SessionKey.KEY_HEART_TIME.getAttr(clientSocket.getSession())); 117 | this.cancel(); 118 | } 119 | }, 1000); 120 | 121 | serverSocket.start(); 122 | serverSocket.sync(); 123 | serverSocket.stop(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /common/src/test/java/org/son/chat/common/TestNioServer.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common; 2 | 3 | import org.junit.Test; 4 | import org.son.chat.common.net.config.SocketChannelConfig; 5 | import org.son.chat.common.net.core.coder.ICoderParserManager; 6 | import org.son.chat.common.net.core.coder.impl.CoderParser; 7 | import org.son.chat.common.net.core.coder.impl.CoderParserManager; 8 | import org.son.chat.common.net.core.session.ISessionFactory; 9 | import org.son.chat.common.net.core.session.SessionFactory; 10 | import org.son.chat.common.net.core.socket.impl.ServerSocket; 11 | import org.son.chat.common.protocol.PackageDefaultCoder; 12 | 13 | /** 14 | * @author solq 15 | */ 16 | public class TestNioServer { 17 | @Test 18 | public void normal() { 19 | ISessionFactory sessionFactory = new SessionFactory(); 20 | 21 | ICoderParserManager coderParserManager = new CoderParserManager(); 22 | coderParserManager.register(CoderParser.valueOf("server chat", PackageDefaultCoder.valueOf(), new ChatTestServerHandle())); 23 | ServerSocket serverSocket=ServerSocket.valueOf(SocketChannelConfig.valueOf(6969), 10,20,coderParserManager, sessionFactory); 24 | serverSocket.start(); 25 | serverSocket.sync(); 26 | serverSocket.stop(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /common/src/test/java/org/son/chat/common/TestSelector.java: -------------------------------------------------------------------------------- 1 | package org.son.chat.common; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.nio.channels.CancelledKeyException; 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.ConcurrentModificationException; 11 | import java.util.Iterator; 12 | import java.util.concurrent.atomic.AtomicBoolean; 13 | 14 | import org.junit.Test; 15 | import org.son.chat.common.net.core.socket.ISocketChannel; 16 | import org.son.chat.common.net.util.NioUtil; 17 | 18 | /** 19 | * 20 | * @author solq 21 | */ 22 | public class TestSelector { 23 | 24 | /*** 25 | * Selector: 获取键集 Set keys()
26 | * Set selectedKeys()
27 | * 以上方法返回选择器的不同键集。keys()方法返回当前已注册的所有键。
28 | * 返回的键集是不可修改的:任何对其进行直接修改的尝试(如,调用其remove()方法) 29 | * 都将抛出UnsupportedOperationException异常。
30 | * selectedKeys()方法用于返回上次调用select()方法时,被"选中"的已准备好进行I/O操作的键。
31 | * 重要提示:selectedKeys()方法返回的键集是可修改的,实际上在两次调用select()方法之间,都必须"手工"将其清空。
32 | * 换句话说,select方法只会在已有的所选键集上添加键,它们不会创建新的键集。
33 | * 34 | * @throws InterruptedException 35 | */ 36 | @Test 37 | public void testResigter() throws IOException, InterruptedException { 38 | final Selector selector = Selector.open(); 39 | Selector selector2 = Selector.open(); 40 | 41 | ServerSocketChannel socketChannel = ServerSocketChannel.open(); 42 | socketChannel.configureBlocking(false); 43 | socketChannel.bind(new InetSocketAddress(6969)); 44 | socketChannel.register(selector, SelectionKey.OP_ACCEPT); 45 | new Thread(new Runnable() { 46 | 47 | @Override 48 | public void run() { 49 | while (true) { 50 | try { 51 | int n = selector.select(); 52 | if (n <= 0) { 53 | System.out.println("xxxxxxxxxxxxx"); 54 | continue; 55 | } 56 | for (Iterator i = selector.selectedKeys().iterator(); i.hasNext();) { 57 | // 得到下一个Key 58 | SelectionKey key = i.next(); 59 | 60 | // 要手动移除 SelectionKey 61 | i.remove(); 62 | NioUtil.printlnOps(key); 63 | } 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | 68 | } 69 | 70 | } 71 | }).start(); 72 | // socketChannel.register(selector2, SelectionKey.OP_ACCEPT); 73 | while (true) { 74 | Thread.sleep(5000); 75 | } 76 | } 77 | 78 | @Test 79 | public void testResigter1() throws IOException, InterruptedException { 80 | final Selector selector = Selector.open(); 81 | new Thread(new Runnable() { 82 | 83 | @Override 84 | public void run() { 85 | while (true) { 86 | try { 87 | int n = selector.select(); 88 | if (n < 0) { 89 | System.out.println(n); 90 | continue; 91 | } 92 | for (Iterator i = selector.selectedKeys().iterator(); i.hasNext();) { 93 | // 得到下一个Key 94 | SelectionKey key = i.next(); 95 | 96 | // 要手动移除 SelectionKey 97 | i.remove(); 98 | NioUtil.printlnOps(key); 99 | } 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | 104 | } 105 | } 106 | }).start(); 107 | 108 | SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(6969)); 109 | socketChannel.configureBlocking(false); 110 | socketChannel.register(selector, 0); 111 | while (true) { 112 | Thread.sleep(5000); 113 | } 114 | } 115 | 116 | // 关服时执行 SelectionKey.cancel() 117 | // 关闭时 或者read 操作异常 也要执行 118 | 119 | /*** 120 | * 通过Selector选择通道
121 | * 一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写) 122 | * 已经准备就绪的那些通道。
123 | * 换句话说,如果你对“读就绪”的通道感兴趣,select()方法会返回读事件已经就绪的那些通道。
124 | * 下面是select()方法:(该方法是阻塞方法)
125 | * int select()
126 | * int select(long timeout)
127 | * int selectNow()
128 | * select()阻塞到至少有一个通道在你注册的事件上就绪了。
129 | * select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)。 130 | */ 131 | /*** 132 | * Selector.open();占用双TCP
133 | * http://developer.51cto.com/art/201112/306870.htm
134 | * http://zhhphappy.iteye.com/blog/2032893 135 | * 136 | * @throws IOException 137 | * @throws InterruptedException 138 | */ 139 | @Test 140 | public void testSelector() throws IOException, InterruptedException { 141 | int count = 10; 142 | for (int i = 0; i < count; i++) { 143 | Selector.open(); 144 | } 145 | Thread.sleep(30000); 146 | } 147 | 148 | @Test 149 | public void testCompareAndSet() { 150 | AtomicBoolean wakenUp = new AtomicBoolean(); 151 | wakenUp.set(true); 152 | boolean result = wakenUp.compareAndSet(false, true); 153 | System.out.println("one : " + result); 154 | 155 | wakenUp.set(false); 156 | result = wakenUp.compareAndSet(false, true); 157 | System.out.println("two : " + result); 158 | } 159 | } 160 | --------------------------------------------------------------------------------