рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐
рдпрд╣ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рдореИрдиреБрдЕрд▓ рдХреЗ рдкрд╛рдардХ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА gen_server рдФрд░ gen_fsm рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░, TCP рд╕реЙрдХреЗрдЯ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреЗ рд╕рд╛рде gen_tcp рдореЙрдбреНрдпреВрд▓, рд╕рдХреНрд░рд┐рдп рдФрд░ рдирд┐рд╖реНрдХреНрд░рд┐рдп рд╕реЙрдХреЗрдЯ рдореЛрдб рдФрд░ "OTP рд╕реБрдкрд░рд╡рд╛рдЗрдЬрд░" рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВред
рдУрдЯреАрдкреА рдордЬрдмреВрдд рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдЙрдкрдХрд░рдг рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рднрд╛рдЧ рдореЗрдВ, рдпрд╣ рд╕рд╛рдорд╛рдиреНрдп рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ рдкреВрд░рд╛ рдХрд░рдХреЗ рдкреВрд░рд╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреИрд╕реЗ рдХрд┐ gen_server рдФрд░ gen_fsm , рдЬреЛ рдУрдЯреАрдкреА рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХреЗ рдПрдХ рдкрджрд╛рдиреБрдХреНрд░рдо рджреНрд╡рд╛рд░рд╛ рдЬреБрдбрд╝реЗ рд╣реЛрддреЗ рд╣реИрдВред
рдХрдИ рдкреНрд░рд╕рд┐рджреНрдз рдЯреАрд╕реАрдкреА рд╕рд░реНрд╡рд░ рдЯреЗрдореНрдкрд▓реЗрдЯ рд╣реИрдВред рд╣рдо рдЬрд┐рд╕ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ, рдЙрд╕рдореЗрдВ рдПрдХ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдЬреБрдбрд╝реЗ рдЧреНрд░рд╛рд╣рдХ рдХреЗ рд▓рд┐рдП рдПрдХ рдирдИ FSM рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдмрдирд╛рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╢рд╛рдорд┐рд▓ рд╣реИред рдпрджреНрдпрдкрд┐ gen_tcp рдореЙрдбреНрдпреВрд▓ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ OTP рдореЗрдВ TCP рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рд╕рдорд░реНрдерди рд╣реИ, рд▓реЗрдХрд┐рди OTP рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рдЯреАрд╕реАрдкреА рд╕рд░реНрд╡рд░ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдорд╛рдирдХ рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рд╣реИред рдПрдХ рдиреЙрди-рдмреНрд▓реЙрдХрд┐рдВрдЧ рд╕рд░реНрд╡рд░ рджреНрд╡рд╛рд░рд╛, рд╣рдорд╛рд░рд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдФрд░ FSM рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдХрд┐рд╕реА рднреА рдЕрд╡рд░реЛрдзрдХ рдХреЙрд▓ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдЖрдиреЗ рд╡рд╛рд▓реЗ рд╕рдВрджреЗрд╢реЛрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдкрд░рд┐рд╡рд░реНрддрди, рдкреБрдирд░рд╛рд░рдВрдн, рдЖрджрд┐) рдХреЗ рд▓рд┐рдП рдмрд┐рдирд╛ рдЯрд╛рдЗрдордЖрдЙрдЯ рдХреЗ рдЬрд╡рд╛рдм рджреЗрдирд╛ рдЪрд╛рд╣рд┐рдПред рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ Erlang рд╕рдВрджрд░реНрдн рдореЗрдВ рд▓реЙрдХ рдХрд░рдиреЗ рдХрд╛ рдЕрд░реНрде рд╣реИ, Erlang рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд▓реЙрдХ рдХрд░рдирд╛, рди рдХрд┐ рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо рдкреНрд░рдХреНрд░рд┐рдпрд╛ред
рдЗрд╕ рдЧрд╛рдЗрдб рдореЗрдВ, рд╣рдо рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ рдХрд┐ gen_server рдФрд░ gen_fsm рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рдЯреАрд╕реАрдкреА рд╕рд░реНрд╡рд░ рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдП , рдЬреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдкрд░ рдирд┐рдпрдВрддреНрд░рдг рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ рдФрд░ рдУрдЯреАрдкреА рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХрд╛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдкрд╛рд▓рди рдХрд░рддрд╛ рд╣реИред
рдПрдХ рдкрд╛рдардХ рдЬреЛ рдУрдЯреАрдкреА рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рдирд╣реАрдВ рд╣реИ, рдЙрд╕реЗ рд╕рд▓рд╛рд╣ рджреА рдЬрд╛рддреА рд╣реИ рдХрд┐ рдмреНрд▓реЙрдХрд┐рдВрдЧ рдХреЙрд▓ gen_tcp: connect / 3 рдФрд░ gen_tcp: acceept / 1 рдХрд╛ рдЙрдкрдпреЛрдЧ рдУрдЯреАрдкреА рдХреЗ рдмрд┐рдирд╛ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рджреЛрд╖-рд╕рд╣рд┐рд╖реНрдгреБ рд╕рд░реНрд╡рд░ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬреЛ рдЖрд░реНрдорд╕реНрдЯреНрд░рд╛рдВрдЧ рдХреЗ рдЧрд╛рдЗрдб рдкрд░ рдзреНрдпрд╛рди рджреЗрдВред
рд╕рд░реНрд╡рд░ рд╕рдВрд░рдЪрдирд╛
рд╣рдорд╛рд░реЗ рд╕рд░реНрд╡рд░ рдХреЗ рдбрд┐рдЬрд╝рд╛рдЗрди рдореЗрдВ tcp_server_app рдореБрдЦреНрдп рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдкреБрдирд░рд╛рд░рдВрдн рдХрд░рдиреЗ рдХреА рд░рдгрдиреАрддрд┐ one_for_one рдФрд░ рджреЛ рдмрдЪреНрдЪреЗ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЗ рд╕рд╛рде рд╢рд╛рдорд┐рд▓ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЬрд┐рдирдореЗрдВ рд╕реЗ рдкрд╣рд▓рд╛ рдЬреАрди_рд╕рд░реНрд╡рд░ рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд╛рдЧреВ рдПрдХ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╣реИ, рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд╕реВрдЪрдирд╛рдУрдВ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдЧрд╛ред рджреВрд╕рд░рд╛ рдПрдХ рдФрд░ tcp_client_sup рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рд╣реИ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ SAS рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП FSM рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рдорд╛рдирдХ рдПрд╕рдПрдПрд╕рдПрд▓ рддреНрд░реБрдЯрд┐ рд░рд┐рдкреЛрд░реНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрд╕рд╛рдорд╛рдиреНрдп рд╢рдЯрдбрд╛рдЙрди рд░рд┐рдХреЙрд░реНрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реИред
рдЗрд╕ рд╕рд╛рдордЧреНрд░реА рдХреА рд╕рд╛рджрдЧреА рдХреЗ рд▓рд┐рдП, рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ (tcp_echo_fsm) рдПрдХ "рдЗрдХреЛ" рд╕рд░реНрд╡рд░ рдкреНрд░рджрд╛рди рдХрд░реЗрдЧрд╛ рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдз рд╡рд╛рдкрд╕ рдХрд░реЗрдЧрд╛ред
рдЖрд╡реЗрджрди рд╡реНрдпрд╡рд╣рд╛рд░ рдФрд░ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ
рдЕрдкрдирд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ "рд╕реБрдкрд░рд╡рд╛рдЗрдЬрд╝рд░" рдФрд░ "рдПрдкреНрд▓рд┐рдХреЗрд╢рди" рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЙрд▓рдмреИрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдореЙрдбреНрдпреВрд▓ рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛ред рдпрджреНрдпрдкрд┐ рдкрд░рдВрдкрд░рд╛рдЧрдд рд░реВрдк рд╕реЗ рдЗрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЙрдирдХреА рд╕рдВрдХреНрд╖рд┐рдкреНрддрддрд╛ рдХреЛ рджреЗрдЦрддреЗ рд╣реБрдП, рд╣рдо рдЙрдиреНрд╣реЗрдВ рдПрдХ рдореЗрдВ рдЬреЛрдбрд╝ рджреЗрдВрдЧреЗред
рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдмреЛрдирд╕ рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдо get_app_env рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рджрд┐рдЦрд╛рддрд╛ рд╣реИ рдХрд┐ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдХреИрд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП, рд╕рд╛рде рд╣реА рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рдкрд░ рдПрдореБрд▓реЗрдЯрд░ рдХреА рдХрдорд╛рдВрдб рд▓рд╛рдЗрди рдкреИрд░рд╛рдореАрдЯрд░ред
рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдкрджрд╛рдиреБрдХреНрд░рдо рдХреЗ рджреЛ рд╕реНрддрд░реЛрдВ рдХреЗ рд▓рд┐рдП init / 1 рдлрд╝рдВрдХреНрд╢рди рдХреЗ рджреЛ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЪреВрдВрдХрд┐ рджреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреБрдирд░рд╛рд░рдВрдн рд░рдгрдиреАрддрд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЙрдиреНрд╣реЗрдВ рд╡рд┐рднрд┐рдиреНрди рд╕реНрддрд░реЛрдВ рдкрд░ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВред
рдЖрд╡реЗрджрди рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, tcp_server_app: start / 2 рдХреЙрд▓рдмреИрдХ рдлрд╝рдВрдХреНрд╢рди рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ : start_link / 2 рдлрд╝рдВрдХреНрд╢рди, рдЬреЛ tcp_server_app: init ([рдкреЛрд░реНрдЯ, рдореЙрдбреНрдпреВрд▓]) рдХреЛ рдХреЙрд▓ рдХрд░рдХреЗ рдореБрдЦреНрдп рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдмрдирд╛рддрд╛ рд╣реИред рдпрд╣ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ tcp_listener рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ tcp_client_sup рдЪрд╛рдЗрд▓реНрдб рд╕реБрдкрд░рд╡рд╛рдЗрдЬрд╝рд░ рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рдореНрдореЗрджрд╛рд░ рд╣реЛрддрд╛ рд╣реИред Init рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдореЙрдбреНрдпреВрд▓ рддрд░реНрдХ FSM рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рд╣реИрдВрдбрд▓рд░ рдХрд╛ рдирд╛рдо рд╣реИ (рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, tcp_echo_fsm )ред
рдЯреАрд╕реАрдкреА рд╕рд░реНрд╡рд░ рдЕрдиреБрдкреНрд░рдпреЛрдЧ (tcp_server_app.erl):
-module(tcp_server_app). -author('saleyn@gmail.com'). -behaviour(application). %% Internal API -export([start_client/0]). %% Application and Supervisor callbacks -export([start/2, stop/1, init/1]). -define(MAX_RESTART, 5). -define(MAX_TIME, 60). -define(DEF_PORT, 2222). %% A startup function for spawning new client connection handling FSM. %% To be called by the TCP listener process. start_client() -> supervisor:start_child(tcp_client_sup, []). %%---------------------------------------------------------------------- %% Application behaviour callbacks %%---------------------------------------------------------------------- start(_Type, _Args) -> ListenPort = get_app_env(listen_port, ?DEF_PORT), supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, tcp_echo_fsm]). stop(_S) -> ok. %%---------------------------------------------------------------------- %% Supervisor behaviour callbacks %%---------------------------------------------------------------------- init([Port, Module]) -> {ok, {_SupFlags = {one_for_one, ?MAX_RESTART, ?MAX_TIME}, [ % TCP Listener { tcp_server_sup, % Id = internal id {tcp_listener,start_link,[Port,Module]}, % StartFun = {M, F, A} permanent, % Restart = permanent | transient | temporary 2000, % Shutdown = brutal_kill | int() >= 0 | infinity worker, % Type = worker | supervisor [tcp_listener] % Modules = [Module] | dynamic }, % Client instance supervisor { tcp_client_sup, {supervisor,start_link,[{local, tcp_client_sup}, ?MODULE, [Module]]}, permanent, % Restart = permanent | transient | temporary infinity, % Shutdown = brutal_kill | int() >= 0 | infinity supervisor, % Type = worker | supervisor [] % Modules = [Module] | dynamic } ] } }; init([Module]) -> {ok, {_SupFlags = {simple_one_for_one, ?MAX_RESTART, ?MAX_TIME}, [ % TCP Client { undefined, % Id = internal id {Module,start_link,[]}, % StartFun = {M, F, A} temporary, % Restart = permanent | transient | temporary 2000, % Shutdown = brutal_kill | int() >= 0 | infinity worker, % Type = worker | supervisor [] % Modules = [Module] | dynamic } ] } }. %%---------------------------------------------------------------------- %% Internal functions %%---------------------------------------------------------------------- get_app_env(Opt, Default) -> case application:get_env(application:get_application(), Opt) of {ok, Val} -> Val; _ -> case init:get_argument(Opt) of [[Val | _]] -> Val; error -> Default end end.
рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛
Gen_tcp рдореЙрдбреНрдпреВрд▓ рдХреА рдХрдорд┐рдпреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдХреЗрд╡рд▓ рдЕрд╡рд░реБрджреНрдз рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдПрдХ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред
Prim_inet рдореЙрдбреНрдпреВрд▓ рдХреЗ рдкрд░реАрдХреНрд╖рдг рдиреЗ рдПрдХ рджрд┐рд▓рдЪрд╕реНрдк рддрдереНрдп рджрд┐рдЦрд╛рдпрд╛ рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреЗрдЯрд╡рд░реНрдХ рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рдХрдорд╛рдВрдб рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд╣реИред рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рдкреНрд░рд▓реЗрдЦрд┐рдд рдирд╣реАрдВ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рдУрдЯреАрдкреА рдЯреАрдо рдХрднреА рднреА рдЗрд╕реЗ рдмрджрд▓ рд╕рдХрддреА рд╣реИ, рд╣рдо рдЗрд╕ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдЕрдкрдиреЗ рд╕рд░реНрд╡рд░ рдХреЛ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░реЗрдВрдЧреЗред
рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ gen_server рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХреА рдЬрд╛рддреА рд╣реИ ред
рдЯреАрд╕реАрдкреА рд╢реНрд░реЛрддрд╛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ (tcp_listener.erl):
-module(tcp_listener). -author('saleyn@gmail.com'). -behaviour(gen_server). %% External API -export([start_link/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, { listener, % Listening socket acceptor, % Asynchronous acceptor's internal reference module % FSM handling module }). %%-------------------------------------------------------------------- %% @spec (Port::integer(), Module) -> {ok, Pid} | {error, Reason} % %% @doc Called by a supervisor to start the listening process. %% @end %%---------------------------------------------------------------------- start_link(Port, Module) when is_integer(Port), is_atom(Module) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Port, Module], []). %%%------------------------------------------------------------------------ %%% Callback functions from gen_server %%%------------------------------------------------------------------------ %%---------------------------------------------------------------------- %% @spec (Port::integer()) -> {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %% %% @doc Called by gen_server framework at process startup. %% Create listening socket. %% @end %%---------------------------------------------------------------------- init([Port, Module]) -> process_flag(trap_exit, true), Opts = [binary, {packet, 2}, {reuseaddr, true}, {keepalive, true}, {backlog, 30}, {active, false}], case gen_tcp:listen(Port, Opts) of {ok, Listen_socket} -> %%Create first accepting process {ok, Ref} = prim_inet:async_accept(Listen_socket, -1), {ok, #state{listener = Listen_socket, acceptor = Ref, module = Module}}; {error, Reason} -> {stop, Reason} end. %%------------------------------------------------------------------------- %% @spec (Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | %% {stop, Reason, State} %% @doc Callback for synchronous server calls. If `{stop, ...}' tuple %% is returned, the server is stopped and `terminate/2' is called. %% @end %% @private %%------------------------------------------------------------------------- handle_call(Request, _From, State) -> {stop, {unknown_call, Request}, State}. %%------------------------------------------------------------------------- %% @spec (Msg, State) ->{noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% @doc Callback for asyncrous server calls. If `{stop, ...}' tuple %% is returned, the server is stopped and `terminate/2' is called. %% @end %% @private %%------------------------------------------------------------------------- handle_cast(_Msg, State) -> {noreply, State}. %%------------------------------------------------------------------------- %% @spec (Msg, State) ->{noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} %% @doc Callback for messages sent directly to server's mailbox. %% If `{stop, ...}' tuple is returned, the server is stopped and %% `terminate/2' is called. %% @end %% @private %%------------------------------------------------------------------------- handle_info({inet_async, ListSock, Ref, {ok, CliSocket}}, #state{listener=ListSock, acceptor=Ref, module=Module} = State) -> try case set_sockopt(ListSock, CliSocket) of ok -> ok; {error, Reason} -> exit({set_sockopt, Reason}) end, %% New client connected - spawn a new process using the simple_one_for_one %% supervisor. {ok, Pid} = tcp_server_app:start_client(), gen_tcp:controlling_process(CliSocket, Pid), %% Instruct the new FSM that it owns the socket. Module:set_socket(Pid, CliSocket), %% Signal the network driver that we are ready to accept another connection case prim_inet:async_accept(ListSock, -1) of {ok, NewRef} -> ok; {error, NewRef} -> exit({async_accept, inet:format_error(NewRef)}) end, {noreply, State#state{acceptor=NewRef}} catch exit:Why -> error_logger:error_msg("Error in async accept: ~p.\n", [Why]), {stop, Why, State} end; handle_info({inet_async, ListSock, Ref, Error}, #state{listener=ListSock, acceptor=Ref} = State) -> error_logger:error_msg("Error in socket acceptor: ~p.\n", [Error]), {stop, Error, State}; handle_info(_Info, State) -> {noreply, State}. %%------------------------------------------------------------------------- %% @spec (Reason, State) -> any %% @doc Callback executed on server shutdown. It is only invoked if %% `process_flag(trap_exit, true)' is set by the server process. %% The return value is ignored. %% @end %% @private %%------------------------------------------------------------------------- terminate(_Reason, State) -> gen_tcp:close(State#state.listener), ok. %%------------------------------------------------------------------------- %% @spec (OldVsn, State, Extra) -> {ok, NewState} %% @doc Convert process state when code is changed. %% @end %% @private %%------------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%%------------------------------------------------------------------------ %%% Internal functions %%%------------------------------------------------------------------------ %% Taken from prim_inet. We are merely copying some socket options from the %% listening socket to the new client socket. set_sockopt(ListSock, CliSocket) -> true = inet_db:register_socket(CliSocket, inet_tcp), case prim_inet:getopts(ListSock, [active, nodelay, keepalive, delay_send, priority, tos]) of {ok, Opts} -> case prim_inet:setopts(CliSocket, Opts) of ok -> ok; Error -> gen_tcp:close(CliSocket), Error end; Error -> gen_tcp:close(CliSocket), Error end.
рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ, init / 1 рджреЛ рдкреИрд░рд╛рдореАрдЯрд░ рд▓реЗрддрд╛ рд╣реИ - рдкреЛрд░реНрдЯ рдирдВрдмрд░ рдЬрд┐рд╕реЗ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдЦреЛрд▓рдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рд╣реИрдВрдбрд▓рд░ рдХрд╛ рдирд╛рдоред рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдлрд╝рдВрдХреНрд╢рди рд╕реЙрдХреЗрдЯ рдХреЛ рдирд┐рд╖реНрдХреНрд░рд┐рдп рдореЛрдб рдореЗрдВ рдЦреЛрд▓рддрд╛ рд╣реИред рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рддрд╛рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдбреЗрдЯрд╛ рдХреЗ рдкреНрд░рд╡рд╛рд╣ рдкрд░ рд╣рдорд╛рд░рд╛ рдирд┐рдпрдВрддреНрд░рдг рд╣реЛред
рдЗрд╕ рдХреЛрдб рдХрд╛ рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣рд┐рд╕реНрд╕рд╛ prim_inet: async_accept / 2 рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рд╣реИред рдЗрд╕ рдХрд╛рдо рдХреЛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ set_sockopt / 2 рдлрд╝рдВрдХреНрд╢рди рд╕реЗ OTP рдЖрдВрддрд░рд┐рдХ рдХреЛрдб рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рдХреЙрдкреА рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬреЛ рд╕реЙрдХреЗрдЯ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЙрдХреЗрдЯ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреА рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдмрдирд╛рдиреЗ рдХрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред
рдЬреИрд╕реЗ рд╣реА рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЙрдХреЗрдЯ рдЬреБрдбрд╝рд╛ рд╣реЛрддрд╛ рд╣реИ, рдиреЗрдЯрд╡рд░реНрдХ рдбреНрд░рд╛рдЗрд╡рд░ рдЗрд╕ рд╕рдВрджреЗрд╢ рдХреА рд╢реНрд░рд╡рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ {inet_async, ListSock, Ref, {OK, CliSocket}} рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕реВрдЪрд┐рдд рдХрд░реЗрдЧрд╛ред рдлрд┐рд▓рд╣рд╛рд▓, рд╣рдо рдХреНрд▓рд╛рдЗрдВрдЯ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдФрд░ рдЗрд╕реЗ CliSocket рдкрд░ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд▓реЙрдиреНрдЪ рдХрд░ рд░рд╣реЗ рд╣реИрдВред
рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛
рдЬрдмрдХрд┐ tcp_listener рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, tcp_echo_fsm рдПрдХ TCP рд╕рд░реНрд╡рд░ рдмрдирд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП FSM рдареВрдВрда рд╕реЗ рдЕрдзрд┐рдХ рдХреБрдЫ рдирд╣реАрдВ рд╣реИред рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рд╕реЗ рджреЛ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдирд┐рд░реНрдпрд╛рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП - tcp_client_sup рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ рдФрд░ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП set_socket / 2 рдХреЗ рд▓рд┐рдП start_link / рддрд╛рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рдВрджреЗрд╢ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдмрд╛рдж рдореЗрдВ рд╕реВрдЪрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП рдХрд┐ рдпрд╣ рд╕реЙрдХреЗрдЯ рдХрд╛ рд╕реНрд╡рд╛рдореА рдмрди рдЬрд╛рддрд╛ рд╣реИ рдФрд░ {рд╕рдХреНрд░рд┐рдп, рдПрдХ рдмрд╛рд░} рд╕реЗрдЯ рдХрд░рдХреЗ рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рд╕рдХрддрд╛ рд╣реИред {рд╕рдХреНрд░рд┐рдп, рд╕рддреНрдп} рд╡рд┐рдХрд▓реНрдкред
рд╣рдо рд╢реНрд░рд╡рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдФрд░ рдЧреНрд░рд╛рд╣рдХ рдХреЗ рдмреАрдЪ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рд╕рд┐рдВрдХреНрд░рдирд╛рдЗрдЬрд╝реЗрд╢рди рдкреИрдЯрд░реНрди рдкрд░ рдЬреЛрд░ рджреЗрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддрд╛рдХрд┐ рдЧрд▓рдд (рд╕реБрдирдиреЗ) рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЙрдирдХреЗ рд╕реНрдерд╛рдирд╛рдВрддрд░рдг рдХреЗ рдХрд╛рд░рдг рд╕рдВрднрд╛рд╡рд┐рдд рд╕рдВрджреЗрд╢ рд╣рд╛рдирд┐ рд╕реЗ рдмрдЪрд╛ рдЬрд╛ рд╕рдХреЗред рд╕реЙрдХреЗрдЯ рдХрд╛ рд╕реНрд╡рд╛рдорд┐рддреНрд╡ рдХрд░рдиреЗ рд╡рд╛рд▓реА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдЗрд╕реЗ рдирд┐рд╖реНрдХреНрд░рд┐рдп рдореЛрдб рдореЗрдВ рдЦреБрд▓рд╛ рд░рдЦрддреА рд╣реИред рдЕрдЧрд▓рд╛, рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдПрдХ рд╕реЙрдХреЗрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреА рд╣реИ рдЬреЛ рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реЗ рд╡рд┐рдХрд▓реНрдк (рдирд┐рд╖реНрдХреНрд░рд┐рдп рдореЛрдб рд╕рд╣рд┐рдд) рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓рддреА рд╣реИред рд╕реЙрдХреЗрдЯ рд╕реНрд╡рд╛рдорд┐рддреНрд╡ рдХреЛ рдЧреНрд░рд╛рд╣рдХ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ gen_tcp: control_process / 2 рдФрд░ set_socket / 2 рдХрд╣рдХрд░ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╕реЙрдХреЗрдЯ рд╕реЗ рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рд╕рдХрддрд╛ рд╣реИред рдЬрдм рддрдХ рд╕реЙрдХреЗрдЯ рдХреЛ рд╕рдХреНрд░рд┐рдп рдореЛрдб рдкрд░ рд╕реЗрдЯ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддрдм рддрдХ рдкреНрд░рд╛рдкреНрдд рд╕рднреА рдбреЗрдЯрд╛ рд╕реЙрдХреЗрдЯ рдмрдлрд░ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред
рдЬрдм рд╕реЙрдХреЗрдЯ рдХрд╛ рд╕реНрд╡рд╛рдорд┐рддреНрд╡ WAIT_FOR_SOCKET рд░рд╛рдЬреНрдп рдореЗрдВ рдХреНрд▓рд╛рдЗрдВрдЯ FSM рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдиреЗрдЯрд╡рд░реНрдХ рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рдПрдХ рдмрд╛рд░ рдореЗрдВ рдПрдХ рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП {рд╕рдХреНрд░рд┐рдп, рдПрдХ рдмрд╛рд░} рдореЛрдб рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрд╣ рдУрдЯреАрдкреА рд╕рд┐рджреНрдзрд╛рдВрдд рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдбреЗрдЯрд╛ рдХреЗ рдкреНрд░рд╡рд╛рд╣ рдкрд░ рдирд┐рдпрдВрддреНрд░рдг рдмрдирд╛рдП рд░рдЦрдиреЗ рдФрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрддрд╛рд░ рдореЗрдВ рд╕рдВрджреЗрд╢реЛрдВ рдФрд░ рдЯреАрд╕реАрдкреА рдЯреНрд░реИрдлрд╝рд┐рдХ рдХреЛ рдорд┐рд▓рд╛рдиреЗ рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
FSM рд░рд╛рдЬреНрдпреЛрдВ рдХреЛ tcp_echo_fsm рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рд╡рд┐рд╢реЗрд╖ рдХрд╛рд░реНрдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдирд╛рдордХрд░рдг рд╕рдореНрдореЗрд▓рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред FSM рдореЗрдВ рджреЛ рд░рд╛рдЬреНрдп рд╣реЛрддреЗ рд╣реИрдВред WAIT_FOR_SOCKET рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЕрд╡рд╕реНрдерд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ FSM рд╕реЙрдХреЗрдЯ рдХреЗ рд╕реНрд╡рд╛рдорд┐рддреНрд╡ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░ рд░рд╣рд╛ рд╣реИ, рдФрд░ WAIT_FOR_DATA , рдЬреЛ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдПрдХ рдЯреАрд╕реАрдкреА рд╕рдВрджреЗрд╢ рдХреЗ рдЗрдВрддрдЬрд╛рд░ рдХреА рд╕реНрдерд┐рддрд┐ рд╣реИред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, FSM рдПрдХ рд╡рд┐рд╢реЗрд╖ "рдЯрд╛рдЗрдордЖрдЙрдЯ" рд╕рдВрджреЗрд╢ рднреА рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреА рдХреЛрдИ рдЧрддрд┐рд╡рд┐рдзрд┐ рдирд╣реАрдВ рд╣реИ рдФрд░ рдпрд╣ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд╕рд╛рде рдХрдиреЗрдХреНрд╢рди рдХреЛ рдмрдВрдж рдХрд░рдиреЗ рдХрд╛ рдХрд╛рд░рдг рдмрдирддрд╛ рд╣реИред
-module(tcp_echo_fsm). -author('saleyn@gmail.com'). -behaviour(gen_fsm). -export([start_link/0, set_socket/2]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). %% FSM States -export([ 'WAIT_FOR_SOCKET'/2, 'WAIT_FOR_DATA'/2 ]). -record(state, { socket, % client socket addr % client address }). -define(TIMEOUT, 120000). %%%------------------------------------------------------------------------ %%% API %%%------------------------------------------------------------------------ %%------------------------------------------------------------------------- %% @spec (Socket) -> {ok,Pid} | ignore | {error,Error} %% @doc To be called by the supervisor in order to start the server. %% If init/1 fails with Reason, the function returns {error,Reason}. %% If init/1 returns {stop,Reason} or ignore, the process is %% terminated and the function returns {error,Reason} or ignore, %% respectively. %% @end %%------------------------------------------------------------------------- start_link() -> gen_fsm:start_link(?MODULE, [], []). set_socket(Pid, Socket) when is_pid(Pid), is_port(Socket) -> gen_fsm:send_event(Pid, {socket_ready, Socket}). %%%------------------------------------------------------------------------ %%% Callback functions from gen_server %%%------------------------------------------------------------------------ %%------------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, StateName, StateData} | %% {ok, StateName, StateData, Timeout} | %% ignore | %% {stop, StopReason} %% @private %%------------------------------------------------------------------------- init([]) -> process_flag(trap_exit, true), {ok, 'WAIT_FOR_SOCKET', #state{}}. %%------------------------------------------------------------------------- %% Func: StateName/2 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %% @private %%------------------------------------------------------------------------- 'WAIT_FOR_SOCKET'({socket_ready, Socket}, State) when is_port(Socket) -> % Now we own the socket inet:setopts(Socket, [{active, once}, {packet, 2}, binary]), {ok, {IP, _Port}} = inet:peername(Socket), {next_state, 'WAIT_FOR_DATA', State#state{socket=Socket, addr=IP}, ?TIMEOUT}; 'WAIT_FOR_SOCKET'(Other, State) -> error_logger:error_msg("State: 'WAIT_FOR_SOCKET'. Unexpected message: ~p\n", [Other]), %% Allow to receive async messages {next_state, 'WAIT_FOR_SOCKET', State}. %% Notification event coming from client 'WAIT_FOR_DATA'({data, Data}, #state{socket=S} = State) -> ok = gen_tcp:send(S, Data), {next_state, 'WAIT_FOR_DATA', State, ?TIMEOUT}; 'WAIT_FOR_DATA'(timeout, State) -> error_logger:error_msg("~p Client connection timeout - closing.\n", [self()]), {stop, normal, State}; 'WAIT_FOR_DATA'(Data, State) -> io:format("~p Ignoring data: ~p\n", [self(), Data]), {next_state, 'WAIT_FOR_DATA', State, ?TIMEOUT}. %%------------------------------------------------------------------------- %% Func: handle_event/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %% @private %%------------------------------------------------------------------------- handle_event(Event, StateName, StateData) -> {stop, {StateName, undefined_event, Event}, StateData}. %%------------------------------------------------------------------------- %% Func: handle_sync_event/4 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {reply, Reply, NextStateName, NextStateData} | %% {reply, Reply, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} | %% {stop, Reason, Reply, NewStateData} %% @private %%------------------------------------------------------------------------- handle_sync_event(Event, _From, StateName, StateData) -> {stop, {StateName, undefined_event, Event}, StateData}. %%------------------------------------------------------------------------- %% Func: handle_info/3 %% Returns: {next_state, NextStateName, NextStateData} | %% {next_state, NextStateName, NextStateData, Timeout} | %% {stop, Reason, NewStateData} %% @private %%------------------------------------------------------------------------- handle_info({tcp, Socket, Bin}, StateName, #state{socket=Socket} = StateData) -> % Flow control: enable forwarding of next TCP message inet:setopts(Socket, [{active, once}]), ?MODULE:StateName({data, Bin}, StateData); handle_info({tcp_closed, Socket}, _StateName, #state{socket=Socket, addr=Addr} = StateData) -> error_logger:info_msg("~p Client ~p disconnected.\n", [self(), Addr]), {stop, normal, StateData}; handle_info(_Info, StateName, StateData) -> {noreply, StateName, StateData}. %%------------------------------------------------------------------------- %% Func: terminate/3 %% Purpose: Shutdown the fsm %% Returns: any %% @private %%------------------------------------------------------------------------- terminate(_Reason, _StateName, #state{socket=Socket}) -> (catch gen_tcp:close(Socket)), ok. %%------------------------------------------------------------------------- %% Func: code_change/4 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState, NewStateData} %% @private %%------------------------------------------------------------------------- code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}.
рдЖрд╡реЗрджрди рд╡рд┐рд╡рд░рдг
OTP рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдХрд╛ рдПрдХ рдФрд░ рдЖрд╡рд╢реНрдпрдХ рд╣рд┐рд╕реНрд╕рд╛ рдПрдХ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдирд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдирд╛рдо, рд╕рдВрд╕реНрдХрд░рдг, рдкреНрд░рд╛рд░рдВрдн рдореЙрдбреНрдпреВрд▓ рдФрд░ рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рд╢рд╛рдорд┐рд▓ рд╣реИрдВред
рдЖрд╡реЗрджрди рдлрд╝рд╛рдЗрд▓ (tcp_server.app):
{application, tcp_server, [ {description, "Demo TCP server"}, {vsn, "1.0"}, {id, "tcp_server"}, {modules, [tcp_listener, tcp_echo_fsm]}, {registered, [tcp_server_sup, tcp_listener]}, {applications, [kernel, stdlib]}, %% %% mod: Specify the module name to start the application, plus args %% {mod, {tcp_server_app, []}}, {env, []} ] }.
рд╕рдВрдХрд▓рди
рдЗрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП рдирд┐рдореНрди рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рд╕рдВрд░рдЪрдирд╛ рдмрдирд╛рдПрдВ:
./tcp_server ./tcp_server/ebin/ ./tcp_server/ebin/tcp_server.app ./tcp_server/src/tcp_server_app.erl ./tcp_server/src/tcp_listener.erl ./tcp_server/src/tcp_echo_fsm.erl
$ cd tcp_server/src $ for f in tcp*.erl ; do erlc +debug_info -o ../ebin $f
рд▓рд╛рдВрдЪ
рд╣рдо рдПрд╕рдПрдПрд╕рдПрд▓ рд╕рдорд░реНрдерди рдХреЗ рд╕рд╛рде рдПрдХ рдПрд░рд▓рд╛рдВрдЧ рд╢реЗрд▓ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ рддрд╛рдХрд┐ рд╣рдо рдЕрдкрдиреЗ рдЖрд╡реЗрджрди рдХреЗ рд▓рд┐рдП рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ рдФрд░ рддреНрд░реБрдЯрд┐ рд░рд┐рдкреЛрд░реНрдЯ рдХреА рд╕реНрдерд┐рддрд┐ рджреЗрдЦ рд╕рдХреЗрдВред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдо рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХреЛрдВ рдХреЗ рдкрджрд╛рдиреБрдХреНрд░рдо рдХреЛ рдиреЗрддреНрд░рд╣реАрди рд░реВрдк рд╕реЗ рдЬрд╛рдВрдЪрдиреЗ рдХреЗ рд▓рд┐рдП appmon рдРрдк рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред
$ cd ../ebin $ erl -boot start_sasl ... 1> appmon:start(). {ok,<0.44.0>} 2> application:start(tcp_server). ok
рдЕрдм рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕реБрдкрд░рд╡рд╛рдЗрдЬрд╝рд░ рдХреЗ рдкрджрд╛рдиреБрдХреНрд░рдо рдХреЛ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП appmon рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ tcp_server рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред
3> {ok,S} = gen_tcp:connect({127,0,0,1},2222,[{packet,2}]). {ok,#Port<0.150>}
рд╣рдордиреЗ рдЕрднреА рдПрдХ рдирдпрд╛ рдЗрдХреЛ рд╕рд░реНрд╡рд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рд╢реБрд░реВ рдХрд┐рдпрд╛ рд╣реИред
4> gen_tcp:send(S,<<"hello">>). ok 5> f(M), receive M -> M end. {tcp,#Port<0.150>,"hello"}
рд╣рдордиреЗ рдЬрд╛рдБрдЪ рдХреА рдХрд┐ рдЗрдХреЛ рд╕рд░реНрд╡рд░ рдЕрдкреЗрдХреНрд╖рд┐рдд рд░реВрдк рд╕реЗ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рдерд╛ред рдЕрдм рдЪрд▓реЛ рд╕рд░реНрд╡рд░ рдкрд░ рдПрдХ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рдХреЛ "рдбрд╛рд▓рдиреЗ" рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВ рдФрд░ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рдХреА рдкреАрдврд╝реА рдХреЛ рджреЗрдЦреЗрдВред
6> [{_,Pid,_,_}] = supervisor:which_children(tcp_client_sup). [{undefined,<0.64.0>,worker,[]}] 7> exit(Pid,kill). true =SUPERVISOR REPORT==== 31-Jul-2007::14:33:49 === Supervisor: {local,tcp_client_sup} Context: child_terminated Reason: killed Offender: [{pid,<0.77.0>}, {name,undefined}, {mfa,{tcp_echo_fsm,start_link,[]}}, {restart_type,temporary}, {shutdown,2000}, {child_type,worker}]
рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдпрджрд┐ рдЖрдк рд╕рд░реНрд╡рд░ рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдХрдиреЗрдХреНрд╢рди рдХреЗ рд╕рд╛рде рд▓реЛрдб рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕реАрдорд╛ рдХреЗ рдмрд╛рдж рд╕реБрдирдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдирдП рдХрдиреЗрдХреНрд╢рди рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреА рд╣реИред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдЖрдкрдХреЛ рдПрдХ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛:
"too many open files"
рдирд┐рд╖реНрдХрд░реНрд╖
OTP рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рдЯреАрд╕реАрдкреА рд╕рд░реНрд╡рд░ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдмрд┐рд▓реНрдбрд┐рдВрдЧ рдмреНрд▓реЙрдХ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдЧрд╛рдЗрдб рджрд┐рдЦрд╛рддрд╛ рд╣реИ рдХрд┐ рдорд╛рдирдХ рдУрдЯреАрдкреА рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕рд░рд▓ рд╕рд░реНрд╡рд░ рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдПред