рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рдкрд░рд┐рдЪрдп

I/O рд░рд┐рдПрдХреНрдЯрд░ (рдПрдХрд▓ рдкрд┐рд░реЛрдпрд╛ рд╣реБрдЖ рдЗрд╡реЗрдВрдЯ рд▓реВрдк) рдЙрдЪреНрдЪ-рд▓реЛрдб рд╕реЙрдлрд╝реНрдЯрд╡реЗрдпрд░ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдПрдХ рдкреИрдЯрд░реНрди рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрдИ рд▓реЛрдХрдкреНрд░рд┐рдп рд╕рдорд╛рдзрд╛рдиреЛрдВ рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдо I/O рд░рд┐рдПрдХреНрдЯрд░ рдХреЗ рдЕрдВрджрд░ рдФрд░ рдмрд╛рд╣рд░ рджреЗрдЦреЗрдВрдЧреЗ рдФрд░ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдХреЛрдб рдХреА 200 рд╕реЗ рдХрдо рд▓рд╛рдЗрдиреЛрдВ рдореЗрдВ рдПрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд▓рд┐рдЦреЗрдВрдЧреЗ, рдФрд░ 40 рдорд┐рд▓рд┐рдпрди рдЕрдиреБрд░реЛрдз/рдорд┐рдирдЯ рд╕реЗ рдЕрдзрд┐рдХ рдХреА рдПрдХ рд╕рд░рд▓ HTTP рд╕рд░реНрд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдмрдирд╛рдПрдВрдЧреЗред

рдкреНрд░рд╕реНрддрд╛рд╡рдирд╛

  • рдпрд╣ рд▓реЗрдЦ I/O рд░рд┐рдПрдХреНрдЯрд░ рдХреА рдХрд╛рд░реНрдпрдкреНрд░рдгрд╛рд▓реА рдХреЛ рд╕рдордЭрдиреЗ рдореЗрдВ рдорджрдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдерд╛, рдФрд░ рдЗрд╕рд▓рд┐рдП рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдЬреЛрдЦрд┐рдореЛрдВ рдХреЛ рд╕рдордЭреЗрдВред
  • рд▓реЗрдЦ рдХреЛ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП рдмреБрдирд┐рдпрд╛рджреА рдмрд╛рддреЛрдВ рдХрд╛ рдЬреНрдЮрд╛рди рдЖрд╡рд╢реНрдпрдХ рд╣реИред рд╕реА рднрд╛рд╖рд╛ рдФрд░ рдиреЗрдЯрд╡рд░реНрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╡рд┐рдХрд╛рд╕ рдореЗрдВ рдХреБрдЫ рдЕрдиреБрднрд╡ред
  • рд╕рднреА рдХреЛрдб рд╕рдЦреНрддреА рд╕реЗ рд╕реА рднрд╛рд╖рд╛ рдореЗрдВ рд▓рд┐рдЦреЗ рдЧрдП рд╣реИрдВ (рд╕рд╛рд╡рдзрд╛рдиреА: рд▓рдВрдмреА рдкреАрдбреАрдПрдл) C11 рдорд╛рдирдХ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рд▓рд┐рдирдХреНрд╕ рдХреЗ рд▓рд┐рдП рдФрд░ рдкрд░ рдЙрдкрд▓рдмреНрдз рд╣реИ GitHub.

рдпрд╣ рдХреНрдпреЛрдВ рдХрд░рддреЗ рд╣реИрдВ?

рдЗрдВрдЯрд░рдиреЗрдЯ рдХреА рдмрдврд╝рддреА рд▓реЛрдХрдкреНрд░рд┐рдпрддрд╛ рдХреЗ рд╕рд╛рде, рд╡реЗрдм рд╕рд░реНрд╡рд░реЛрдВ рдХреЛ рдПрдХ рд╕рд╛рде рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдХрдиреЗрдХреНрд╢рдиреЛрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдиреЗ рд▓рдЧреА, рдФрд░ рдЗрд╕рд▓рд┐рдП рджреЛ рддрд░реАрдХреЛрдВ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рдЧрдИ: рдмрдбрд╝реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдУрдПрд╕ рдереНрд░реЗрдбреНрд╕ рдкрд░ I/O рдХреЛ рдмреНрд▓реЙрдХ рдХрд░рдирд╛ рдФрд░ I/O рдХреЛ рдиреЙрди-рдмреНрд▓реЙрдХ рдХрд░рдирд╛ред рдПрдХ рдИрд╡реЗрдВрдЯ рдЕрдзрд┐рд╕реВрдЪрдирд╛ рдкреНрд░рдгрд╛рд▓реА, рдЬрд┐рд╕реЗ "рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛" рднреА рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ (рдПрдкреЛрд▓/рдХреНрдпреВрдХреНрдпреВ/рдЖрдИрдУрд╕реАрдкреА/рдЖрджрд┐)ред

рдкрд╣рд▓реЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдЖрдиреЗ рд╡рд╛рд▓реЗ рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рдУрдПрд╕ рдереНрд░реЗрдб рдмрдирд╛рдирд╛ рд╢рд╛рдорд┐рд▓ рдерд╛ред рдЗрд╕рдХрд╛ рдиреБрдХрд╕рд╛рди рдЦрд░рд╛рдм рд╕реНрдХреЗрд▓реЗрдмрд┐рд▓рд┐рдЯреА рд╣реИ: рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдХрдИ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рд╕рдВрджрд░реНрдн рдкрд░рд┐рд╡рд░реНрддрди ╨╕ рд╕рд┐рд╕реНрдЯрдо рдХреЙрд▓. рд╡реЗ рдорд╣рдВрдЧреЗ рдСрдкрд░реЗрд╢рди рд╣реИрдВ рдФрд░ рдкреНрд░рднрд╛рд╡рд╢рд╛рд▓реА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рдХрдиреЗрдХреНрд╢рди рдХреЗ рд╕рд╛рде рдореБрдлреНрдд рд░реИрдо рдХреА рдХрдореА рд╣реЛ рд╕рдХрддреА рд╣реИред

рд╕рдВрд╢реЛрдзрд┐рдд рд╕рдВрд╕реНрдХрд░рдг рдкрд░ рдкреНрд░рдХрд╛рд╢ рдбрд╛рд▓рд╛ рдЧрдпрд╛ рдзрд╛рдЧреЛрдВ рдХреА рдирд┐рд╢реНрдЪрд┐рдд рд╕рдВрдЦреНрдпрд╛ (рдереНрд░реЗрдб рдкреВрд▓), рдЬрд┐рд╕рд╕реЗ рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рдирд┐рд░рд╕реНрдд рдХрд░рдиреЗ рд╕реЗ рд░реЛрдХрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╕рд╛рде рд╣реА рдПрдХ рдирдИ рд╕рдорд╕реНрдпрд╛ рднреА рдкреЗрд╢ рдХреА рдЬрд╛ рд╕рдХрддреА рд╣реИ: рдпрджрд┐ рдереНрд░реЗрдб рдкреВрд▓ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдкрдврд╝рдиреЗ рдХреЗ рд╕рдВрдЪрд╛рд▓рди рд╕реЗ рдЕрд╡рд░реБрджреНрдз рд╣реИ, рддреЛ рдЕрдиреНрдп рд╕реЙрдХреЗрдЯ рдЬреЛ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИрдВ, рд╡реЗ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реЛрдВрдЧреЗ рдРрд╕рд╛ рдХрд░реЛред

рджреВрд╕рд░рд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ рдШрдЯрдирд╛ рдЕрдзрд┐рд╕реВрдЪрдирд╛ рдкреНрд░рдгрд╛рд▓реА (рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛) рдУрдПрд╕ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдЧрдпрд╛ред рдпрд╣ рдЖрд▓реЗрдЦ рд╕рдмрд╕реЗ рд╕рд╛рдорд╛рдиреНрдп рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рдкрд░ рдЪрд░реНрдЪрд╛ рдХрд░рддрд╛ рд╣реИ, рдЬреЛ I/O рд╕рдВрдЪрд╛рд▓рди рдХреЗ рд▓рд┐рдП рддрддреНрдкрд░рддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрд▓рд░реНрдЯ (рдШрдЯрдирд╛рдУрдВ, рд╕реВрдЪрдирд╛рдУрдВ) рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ, рди рдХрд┐ рдЙрдирдХреЗ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реВрдЪрдирд╛рдПрдВ. рдЗрд╕рдХреЗ рдЙрдкрдпреЛрдЧ рдХрд╛ рдПрдХ рд╕рд░рд▓ рдЙрджрд╛рд╣рд░рдг рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдмреНрд▓реЙрдХ рдЖрд░реЗрдЦ рджреНрд╡рд╛рд░рд╛ рджрд░реНрд╢рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рдЗрди рджреГрд╖реНрдЯрд┐рдХреЛрдгреЛрдВ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ:

  • I/O рдкрд░рд┐рдЪрд╛рд▓рди рдХреЛ рдЕрд╡рд░реБрджреНрдз рдХрд░рдирд╛ рдирд┐рд▓рдВрдмрд┐рдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░рд╡рд╛рд╣ рдЬрдм рддрдХрдЬрдм рддрдХ рдУрдПрд╕ рдареАрдХ рд╕реЗ рди рд╣реЛ рдЬрд╛рдП рд╡рд┐рдЦрдВрдбрди рдЖрдиреЗ рд╡рд╛рд▓реА рдЖрдИрдкреА тАЛтАЛрдкреИрдХреЗрдЯ рдмрд╛рдЗрдЯ рд╕реНрдЯреНрд░реАрдо рдХреЗ рд▓рд┐рдП (рдЯреАрд╕реАрдкреА, рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛) рдпрд╛ рдмрд╛рдж рдореЗрдВ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдВрддрд░рд┐рдХ рд▓реЗрдЦрди рдмрдлрд╝рд░реНрд╕ рдореЗрдВ рдкрд░реНрдпрд╛рдкреНрдд рд╕реНрдерд╛рди рдЙрдкрд▓рдмреНрдз рдирд╣реАрдВ рд╣реЛрдЧрд╛ рдПрдирдЖрдИрд╕реА (рдбреЗрдЯрд╛ рднреЗрдЬ рд░рд╣рд╛ рд╣реИ)ред
  • рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рдЕрдзрд┐рдХ рд╕рдордп рддрдХ рдкреНрд░реЛрдЧреНрд░рд╛рдо рдХреЛ рд╕реВрдЪрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ OS рдкрд╣рд▓реЗ рд╣реА рдбреАрдлреНрд░реИрдЧреНрдореЗрдВрдЯреЗрдб рдЖрдИрдкреА рдкреИрдХреЗрдЯ (рдЯреАрд╕реАрдкреА, рдбреЗрдЯрд╛ рд░рд┐рд╕реЗрдкреНрд╢рди) рдпрд╛ рдЖрдВрддрд░рд┐рдХ рд░рд╛рдЗрдЯ рдмрдлрд╝рд░реНрд╕ рдореЗрдВ рдкрд░реНрдпрд╛рдкреНрдд рдЬрдЧрд╣ рдкрд╣рд▓реЗ рд╣реА рдЙрдкрд▓рдмреНрдз (рдбреЗрдЯрд╛ рднреЗрдЬрдирд╛)ред

рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдкреНрд░рддреНрдпреЗрдХ I/O рдХреЗ рд▓рд┐рдП рдПрдХ OS рдереНрд░реЗрдб рдЖрд░рдХреНрд╖рд┐рдд рдХрд░рдирд╛ рдХрдВрдкреНрдпреВрдЯрд┐рдВрдЧ рд╢рдХреНрддрд┐ рдХреА рдмрд░реНрдмрд╛рджреА рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдереНрд░реЗрдб рдЙрдкрдпреЛрдЧреА рдХрд╛рд░реНрдп рдирд╣реАрдВ рдХрд░ рд░рд╣реЗ рд╣реИрдВ (рдЗрд╕рд▓рд┐рдП рдпрд╣ рд╢рдмреНрдж "рд╕реЙрдлрд╝реНрдЯрд╡реЗрдпрд░ рд╡реНрдпрд╡рдзрд╛рди"). рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░реЛрдЧреНрд░рд╛рдо рд╕реАрдкреАрдпреВ рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХреЛ рдЕрдзрд┐рдХ рдХрд┐рдлрд╛рдпрддреА рд░реВрдк рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддрд╛ рд╣реИред

I/O рд░рд┐рдПрдХреНрдЯрд░ рдореЙрдбрд▓

I/O рд░рд┐рдПрдХреНрдЯрд░ рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛрдб рдХреЗ рдмреАрдЪ рдПрдХ рдкрд░рдд рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рд╕рдВрдЪрд╛рд▓рди рдХрд╛ рд╕рд┐рджреНрдзрд╛рдВрдд рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдмреНрд▓реЙрдХ рдЖрд░реЗрдЦ рджреНрд╡рд╛рд░рд╛ рд╡рд░реНрдгрд┐рдд рд╣реИ:

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

  • рдореИрдВ рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛ рджреВрдВ рдХрд┐ рдПрдХ рдЗрд╡реЗрдВрдЯ рдПрдХ рдЕрдзрд┐рд╕реВрдЪрдирд╛ рд╣реИ рдХрд┐ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕реЙрдХреЗрдЯ рдПрдХ рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз I/O рдСрдкрд░реЗрд╢рди рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИред
  • рдЗрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд╣реИ рдЬрд┐рд╕реЗ I/O рд░рд┐рдПрдХреНрдЯрд░ рджреНрд╡рд╛рд░рд╛ рдХрд┐рд╕реА рдЗрд╡реЗрдВрдЯ рдХреЗ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдиреЗ рдкрд░ рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рддрдм рдПрдХ рдиреЙрди-рдмреНрд▓реЙрдХрд┐рдВрдЧ I/O рдСрдкрд░реЗрд╢рди рдХрд░рддрд╛ рд╣реИред

рдпрд╣ рдзреНрдпрд╛рди рд░рдЦрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ I/O рд░рд┐рдПрдХреНрдЯрд░ рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдПрдХрд▓-рдереНрд░реЗрдбреЗрдб рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕ рдЕрд╡рдзрд╛рд░рдгрд╛ рдХреЛ 1 рдереНрд░реЗрдб: 1 рд░рд┐рдПрдХреНрдЯрд░ рдХреЗ рдЕрдиреБрдкрд╛рдд рдореЗрдВ рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╕реЗ рдХреЛрдИ рдирд╣реАрдВ рд░реЛрдХ рд╕рдХрддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рд╕рднреА рд╕реАрдкреАрдпреВ рдХреЛрд░ рдХреЛ рд░реАрд╕рд╛рдЗрдХреНрд▓рд┐рдВрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди

рд╣рдо рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд░рдЦреЗрдВрдЧреЗ reactor.h, рдФрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди - рдореЗрдВ reactor.c. reactor.h рдЗрд╕рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдШреЛрд╖рдгрд╛рдПрдБ рд╢рд╛рдорд┐рд▓ рд╣реЛрдВрдЧреА:

рд░рд┐рдПрдХреНрдЯрд░.рдПрдЪ рдореЗрдВ рдШреЛрд╖рдгрд╛рдПрдБ рджрд┐рдЦрд╛рдПрдБ

typedef struct reactor Reactor;

/*
 * ╨г╨║╨░╨╖╨░╤В╨╡╨╗╤М ╨╜╨░ ╤Д╤Г╨╜╨║╤Ж╨╕╤О, ╨║╨╛╤В╨╛╤А╨░╤П ╨▒╤Г╨┤╨╡╤В ╨▓╤Л╨╖╤Л╨▓╨░╤В╤М╤Б╤П I/O ╤А╨╡╨░╨║╤В╨╛╤А╨╛╨╝ ╨┐╤А╨╕ ╨┐╨╛╤Б╤В╤Г╨┐╨╗╨╡╨╜╨╕╨╕
 * ╤Б╨╛╨▒╤Л╤В╨╕╤П ╨╛╤В ╤Б╨╕╤Б╤В╨╡╨╝╨╜╨╛╨│╨╛ ╤Б╨╡╨╗╨╡╨║╤В╨╛╤А╨░.
 */
typedef void (*Callback)(void *arg, int fd, uint32_t events);

/*
 * ╨Т╨╛╨╖╨▓╤А╨░╤Й╨░╨╡╤В `NULL` ╨▓ ╤Б╨╗╤Г╤З╨░╨╡ ╨╛╤И╨╕╨▒╨║╨╕, ╨╜╨╡-`NULL` ╤Г╨║╨░╨╖╨░╤В╨╡╨╗╤М ╨╜╨░ `Reactor` ╨▓
 * ╨┐╤А╨╛╤В╨╕╨▓╨╜╨╛╨╝ ╤Б╨╗╤Г╤З╨░╨╡.
 */
Reactor *reactor_new(void);

/*
 * ╨Ю╤Б╨▓╨╛╨▒╨╛╨╢╨┤╨░╨╡╤В ╤Б╨╕╤Б╤В╨╡╨╝╨╜╤Л╨╣ ╤Б╨╡╨╗╨╡╨║╤В╨╛╤А, ╨▓╤Б╨╡ ╨╖╨░╤А╨╡╨│╨╕╤Б╤В╤А╨╕╤А╨╛╨▓╨░╨╜╨╜╤Л╨╡ ╤Б╨╛╨║╨╡╤В╤Л ╨▓ ╨┤╨░╨╜╨╜╤Л╨╣ ╨╝╨╛╨╝╨╡╨╜╤В
 * ╨▓╤А╨╡╨╝╨╡╨╜╨╕ ╨╕ ╤Б╨░╨╝ I/O ╤А╨╡╨░╨║╤В╨╛╤А.
 *
 * ╨б╨╗╨╡╨┤╤Г╤О╤Й╨╕╨╡ ╤Д╤Г╨╜╨║╤Ж╨╕╨╕ ╨▓╨╛╨╖╨▓╤А╨░╤Й╨░╤О╤В -1 ╨▓ ╤Б╨╗╤Г╤З╨░╨╡ ╨╛╤И╨╕╨▒╨║╨╕, 0 ╨▓ ╤Б╨╗╤Г╤З╨░╨╡ ╤Г╤Б╨┐╨╡╤Е╨░.
 */
int reactor_destroy(Reactor *reactor);

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg);
int reactor_deregister(const Reactor *reactor, int fd);
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg);

/*
 * ╨Ч╨░╨┐╤Г╤Б╨║╨░╨╡╤В ╤Ж╨╕╨║╨╗ ╤Б╨╛╨▒╤Л╤В╨╕╨╣ ╤Б ╤В╨░╨╣╨╝-╨░╤Г╤В╨╛╨╝ `timeout`.
 *
 * ╨н╤В╨░ ╤Д╤Г╨╜╨║╤Ж╨╕╤П ╨┐╨╡╤А╨╡╨┤╨░╤Б╤В ╤Г╨┐╤А╨░╨▓╨╗╨╡╨╜╨╕╨╡ ╨▓╤Л╨╖╤Л╨▓╨░╤О╤Й╨╡╨╝╤Г ╨║╨╛╨┤╤Г ╨╡╤Б╨╗╨╕ ╨╛╤В╨▓╨╡╨┤╤С╨╜╨╜╨╛╨╡ ╨▓╤А╨╡╨╝╤П ╨▓╤Л╤И╨╗╨╛
 * ╨╕╨╗╨╕/╨╕ ╨┐╤А╨╕ ╨╛╤В╤Б╤Г╤В╤Б╤В╨▓╨╕╨╕ ╨╖╨░╤А╨╡╨│╨╕╤Б╤В╤А╨╕╤А╨╛╨▓╨░╨╜╨╜╤Л╤Е ╤Б╨╛╨║╨╡╤В╨╛╨▓.
 */
int reactor_run(const Reactor *reactor, time_t timeout);

I/O рд░рд┐рдПрдХреНрдЯрд░ рд╕рдВрд░рдЪрдирд╛ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ рдлрд╝рд╛рдЗрд▓ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдЪрдпрдирдХрд░реНрддрд╛ рдПрдкреЛрд▓ ╨╕ рд╣реИрд╢ рдЯреЗрдмрд▓ GHashTable, рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рд╕реЙрдХреЗрдЯ рдХреЛ рдореИрдк рдХрд░рддрд╛ рд╣реИ CallbackData (рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреА рд╕рдВрд░рдЪрдирд╛ рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рддрд░реНрдХ)ред

рд░рд┐рдПрдХреНрдЯрд░ рдФрд░ рдХреЙрд▓рдмреИрдХрдбреЗрдЯрд╛ рджрд┐рдЦрд╛рдПрдБ

struct reactor {
    int epoll_fd;
    GHashTable *table; // (int, CallbackData)
};

typedef struct {
    Callback callback;
    void *arg;
} CallbackData;

рдХреГрдкрдпрд╛ рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рд╣рдордиреЗ рд╕рдВрднрд╛рд▓рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рд╕рдХреНрд╖рдо рдХрд░ рджреА рд╣реИ рдЕрдкреВрд░реНрдг рдкреНрд░рдХрд╛рд░ рд╕реВрдЪрдХрд╛рдВрдХ рдХреЗ рдЕрдиреБрд╕рд╛рд░. рдореЗрдВ reactor.h рд╣рдо рд╕рдВрд░рдЪрдирд╛ рдХреА рдШреЛрд╖рдгрд╛ рдХрд░рддреЗ рд╣реИрдВ reactorрдФрд░ рдореЗрдВ reactor.c рд╣рдо рдЗрд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдЗрд╕рдХреЗ рдлрд╝реАрд▓реНрдб рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдмрджрд▓рдиреЗ рд╕реЗ рд░реЛрдХрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣ рдкреИрдЯрд░реНрди рдореЗрдВ рд╕реЗ рдПрдХ рд╣реИ рдбреЗрдЯрд╛ рдЫрд┐рдкрд╛рдирд╛, рдЬреЛ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рд╕реА рд╢рдмреНрджрд╛рд░реНрде рдореЗрдВ рдлрд┐рдЯ рдмреИрдарддрд╛ рд╣реИред

рдХрд╛рд░реНрдп reactor_register, reactor_deregister ╨╕ reactor_reregister рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рдФрд░ рд╣реИрд╢ рддрд╛рд▓рд┐рдХрд╛ рдореЗрдВ рд░реБрдЪрд┐ рдХреЗ рд╕реЙрдХреЗрдЯ рдФрд░ рд╕рдВрдмрдВрдзрд┐рдд рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреА рд╕реВрдЪреА рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВред

рдкрдВрдЬреАрдХрд░рдг рдХрд╛рд░реНрдп рджрд┐рдЦрд╛рдПрдБ

#define REACTOR_CTL(reactor, op, fd, interest)                                 
    if (epoll_ctl(reactor->epoll_fd, op, fd,                                   
                  &(struct epoll_event){.events = interest,                    
                                        .data = {.fd = fd}}) == -1) {          
        perror("epoll_ctl");                                                   
        return -1;                                                             
    }

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_ADD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

int reactor_deregister(const Reactor *reactor, int fd) {
    REACTOR_CTL(reactor, EPOLL_CTL_DEL, fd, 0)
    g_hash_table_remove(reactor->table, &fd);
    return 0;
}

int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_MOD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

I/O рд░рд┐рдПрдХреНрдЯрд░ рджреНрд╡рд╛рд░рд╛ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдХреЗ рд╕рд╛рде рдШрдЯрдирд╛ рдХреЛ рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж fd, рдпрд╣ рд╕рдВрдмрдВрдзрд┐рдд рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдпрд╣ рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реИ fd, рдмрд┐рдЯреЛрд╡реБрдпреВ рдорд╛рд╕реНрдХреБ рдЙрддреНрдкрдиреНрди рдИрд╡реЗрдВрдЯ рдФрд░ рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реВрдЪрдХ void.

рд░рд┐рдПрдХреНрдЯрд░_рд░рди() рдлрд╝рдВрдХреНрд╢рди рджрд┐рдЦрд╛рдПрдБ

int reactor_run(const Reactor *reactor, time_t timeout) {
    int result;
    struct epoll_event *events;
    if ((events = calloc(MAX_EVENTS, sizeof(*events))) == NULL)
        abort();

    time_t start = time(NULL);

    while (true) {
        time_t passed = time(NULL) - start;
        int nfds =
            epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, timeout - passed);

        switch (nfds) {
        // ╨Ю╤И╨╕╨▒╨║╨░
        case -1:
            perror("epoll_wait");
            result = -1;
            goto cleanup;
        // ╨Т╤А╨╡╨╝╤П ╨▓╤Л╤И╨╗╨╛
        case 0:
            result = 0;
            goto cleanup;
        // ╨г╤Б╨┐╨╡╤И╨╜╨░╤П ╨╛╨┐╨╡╤А╨░╤Ж╨╕╤П
        default:
            // ╨Т╤Л╨╖╨▓╨░╤В╤М ╨╛╨▒╤А╨░╨▒╨╛╤В╤З╨╕╨║╨╛╨▓ ╤Б╨╛╨▒╤Л╤В╨╕╨╣
            for (int i = 0; i < nfds; i++) {
                int fd = events[i].data.fd;

                CallbackData *callback =
                    g_hash_table_lookup(reactor->table, &fd);
                callback->callback(callback->arg, fd, events[i].events);
            }
        }
    }

cleanup:
    free(events);
    return result;
}

рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛрдб рдореЗрдВ рдлрд╝рдВрдХреНрд╢рди рдХреЙрд▓ рдХреА рд╢реНрд░реГрдВрдЦрд▓рд╛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд░реВрдк рд▓реЗрдЧреА:

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рд╕рд┐рдВрдЧрд▓ рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░

рдЙрдЪреНрдЪ рд▓реЛрдб рдХреЗ рддрд╣рдд I/O рд░рд┐рдПрдХреНрдЯрд░ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдПрдХ рд╕рд░рд▓ HTTP рд╡реЗрдм рд╕рд░реНрд╡рд░ рд▓рд┐рдЦреЗрдВрдЧреЗ рдЬреЛ рдПрдХ рдЫрд╡рд┐ рдХреЗ рд╕рд╛рде рдХрд┐рд╕реА рднреА рдЕрдиреБрд░реЛрдз рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИред

HTTP рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рдПрдХ рддреНрд╡рд░рд┐рдд рд╕рдВрджрд░реНрдн

HTTP - рдпрд╣ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рд╣реИ рдЖрд╡реЗрджрди рд╕реНрддрд░, рдореБрдЦреНрдп рд░реВрдк рд╕реЗ рд╕рд░реНрд╡рд░-рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

HTTP рдХрд╛ рдЙрдкрдпреЛрдЧ рдЖрд╕рд╛рдиреА рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдкрд░рд┐рд╡рд╣рди рдорд╕рд╡рд┐рджрд╛ рдмрдирд╛рдирд╛ рдЯреАрд╕реАрдкреА, рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╕рдВрджреЗрд╢ рднреЗрдЬрдирд╛ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢.

рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рд░реВрдк

<╨Ъ╨Ю╨Ь╨Р╨Э╨Ф╨Р> <URI> <╨Т╨Х╨а╨б╨Ш╨п HTTP>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ 1>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ 2>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ N>CRLF CRLF
<╨Ф╨Р╨Э╨Э╨л╨Х>

  • CRLF рджреЛ рд╡рд░реНрдгреЛрдВ рдХрд╛ рдПрдХ рдХреНрд░рдо рд╣реИ: r ╨╕ n, рдЕрдиреБрд░реЛрдз рдХреА рдкрд╣рд▓реА рдкрдВрдХреНрддрд┐, рд╣реЗрдбрд░ рдФрд░ рдбреЗрдЯрд╛ рдХреЛ рдЕрд▓рдЧ рдХрд░рдирд╛ред
  • <╨Ъ╨Ю╨Ь╨Р╨Э╨Ф╨Р> - рдореЗрдВ рд╕реЗ рдПрдХ CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. рдмреНрд░рд╛рдЙрдЬрд╝рд░ рд╣рдорд╛рд░реЗ рд╕рд░реНрд╡рд░ рдХреЛ рдПрдХ рдХрдорд╛рдВрдб рднреЗрдЬреЗрдЧрд╛ GET, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ "рдореБрдЭреЗ рдлрд╝рд╛рдЗрд▓ рдХреА рд╕рд╛рдордЧреНрд░реА рднреЗрдЬреЗрдВред"
  • <URI> - рдпреВрдирд┐рдлреЙрд░реНрдо рд░рд┐рд╕реЛрд░реНрд╕ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛. рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдпреВрдЖрд░рдЖрдИ = /index.html, рдлрд┐рд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдЯ рдХреЗ рдореБрдЦреНрдп рдкреГрд╖реНрда рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реИред
  • <╨Т╨Х╨а╨б╨Ш╨п HTTP> - рдкреНрд░рд╛рд░реВрдк рдореЗрдВ HTTP рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рд╕рдВрд╕реНрдХрд░рдг HTTP/X.Y. рдЖрдЬ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдиреЗ рд╡рд╛рд▓рд╛ рд╕рдВрд╕реНрдХрд░рдг рд╣реИ HTTP/1.1.
  • <╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ N> рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдПрдХ рдХреБрдВрдЬреА-рдореВрд▓реНрдп рдЬреЛрдбрд╝реА рд╣реИ <╨Ъ╨Ы╨о╨з>: <╨Ч╨Э╨Р╨з╨Х╨Э╨Ш╨Х>, рдЖрдЧреЗ рдХреЗ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рдкрд░ рднреЗрдЬрд╛ рдЧрдпрд╛ред
  • <╨Ф╨Р╨Э╨Э╨л╨Х> - рдСрдкрд░реЗрд╢рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рджреНрд╡рд╛рд░рд╛ рдЖрд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ред рдЕрдХреНрд╕рд░ рдпрд╣ рд╕рд░рд▓ рд╣реЛрддрд╛ рд╣реИ JSON рдпрд╛ рдХреЛрдИ рдЕрдиреНрдп рдкреНрд░рд╛рд░реВрдк.

рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдкреНрд░рд╛рд░реВрдк

<╨Т╨Х╨а╨б╨Ш╨п HTTP> <╨Ъ╨Ю╨Ф ╨б╨в╨Р╨в╨г╨б╨Р> <╨Ю╨Я╨Ш╨б╨Р╨Э╨Ш╨Х ╨б╨в╨Р╨в╨г╨б╨Р>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ 1>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ 2>CRLF
<╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ N>CRLF CRLF
<╨Ф╨Р╨Э╨Э╨л╨Х>

  • <╨Ъ╨Ю╨Ф ╨б╨в╨Р╨в╨г╨б╨Р> рдпрд╣ рдСрдкрд░реЗрд╢рди рдХреЗ рдкрд░рд┐рдгрд╛рдо рдХреЛ рджрд░реНрд╢рд╛рдиреЗ рд╡рд╛рд▓реА рдПрдХ рд╕рдВрдЦреНрдпрд╛ рд╣реИред рд╣рдорд╛рд░рд╛ рд╕рд░реНрд╡рд░ рд╣рдореЗрд╢рд╛ рд╕реНрдерд┐рддрд┐ 200 (рд╕рдлрд▓ рдСрдкрд░реЗрд╢рди) рд▓реМрдЯрд╛рдПрдЧрд╛ред
  • <╨Ю╨Я╨Ш╨б╨Р╨Э╨Ш╨Х ╨б╨в╨Р╨в╨г╨б╨Р> - рд╕реНрдерд┐рддрд┐ рдХреЛрдб рдХрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ред рд╕реНрдЯреЗрдЯрд╕ рдХреЛрдб 200 рдХреЗ рд▓рд┐рдП рдпрд╣ рд╣реИ OK.
  • <╨Ч╨Р╨У╨Ю╨Ы╨Ю╨Т╨Ю╨Ъ N> - рдЕрдиреБрд░реЛрдз рдХреЗ рд╕рдорд╛рди рдкреНрд░рд╛рд░реВрдк рдХрд╛ рд╣реЗрдбрд░ред рд╣рдо рдЙрдкрд╛рдзрд┐рдпрд╛рдБ рд▓реМрдЯрд╛ рджреЗрдВрдЧреЗ Content-Length (рдлрд╝рд╛рдЗрд▓ рдЖрдХрд╛рд░) рдФрд░ Content-Type: text/html (рд╡рд╛рдкрд╕реА рдбреЗрдЯрд╛ рдкреНрд░рдХрд╛рд░)ред
  • <╨Ф╨Р╨Э╨Э╨л╨Х> - рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдЕрдиреБрд░реЛрдзрд┐рдд рдбреЗрдЯрд╛ред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдпрд╣ рдЫрд╡рд┐ рдХрд╛ рдкрде рд╣реИ рдПрдЪрдЯреАрдПрдордПрд▓.

рдлрд╝рд╛рдЗрд▓ http_server.c (рд╕рд┐рдВрдЧрд▓ рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░) рдореЗрдВ рдлрд╝рд╛рдЗрд▓ рд╢рд╛рдорд┐рд▓ рд╣реИ common.h, рдЬрд┐рд╕рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдлрд╝рдВрдХреНрд╢рди рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:

Common.h рдореЗрдВ рдлрд╝рдВрдХреНрд╢рди рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рджрд┐рдЦрд╛рдПрдВ

/*
 * ╨Ю╨▒╤А╨░╨▒╨╛╤В╤З╨╕╨║ ╤Б╨╛╨▒╤Л╤В╨╕╨╣, ╨║╨╛╤В╨╛╤А╤Л╨╣ ╨▓╤Л╨╖╨╛╨▓╨╡╤В╤Б╤П ╨┐╨╛╤Б╨╗╨╡ ╤В╨╛╨│╨╛, ╨║╨░╨║ ╤Б╨╛╨║╨╡╤В ╨▒╤Г╨┤╨╡╤В
 * ╨│╨╛╤В╨╛╨▓ ╨┐╤А╨╕╨╜╤П╤В╤М ╨╜╨╛╨▓╨╛╨╡ ╤Б╨╛╨╡╨┤╨╕╨╜╨╡╨╜╨╕╨╡.
 */
static void on_accept(void *arg, int fd, uint32_t events);

/*
 * ╨Ю╨▒╤А╨░╨▒╨╛╤В╤З╨╕╨║ ╤Б╨╛╨▒╤Л╤В╨╕╨╣, ╨║╨╛╤В╨╛╤А╤Л╨╣ ╨▓╤Л╨╖╨╛╨▓╨╡╤В╤Б╤П ╨┐╨╛╤Б╨╗╨╡ ╤В╨╛╨│╨╛, ╨║╨░╨║ ╤Б╨╛╨║╨╡╤В ╨▒╤Г╨┤╨╡╤В
 * ╨│╨╛╤В╨╛╨▓ ╨╛╤В╨┐╤А╨░╨▓╨╕╤В╤М HTTP ╨╛╤В╨▓╨╡╤В.
 */
static void on_send(void *arg, int fd, uint32_t events);

/*
 * ╨Ю╨▒╤А╨░╨▒╨╛╤В╤З╨╕╨║ ╤Б╨╛╨▒╤Л╤В╨╕╨╣, ╨║╨╛╤В╨╛╤А╤Л╨╣ ╨▓╤Л╨╖╨╛╨▓╨╡╤В╤Б╤П ╨┐╨╛╤Б╨╗╨╡ ╤В╨╛╨│╨╛, ╨║╨░╨║ ╤Б╨╛╨║╨╡╤В ╨▒╤Г╨┤╨╡╤В
 * ╨│╨╛╤В╨╛╨▓ ╨┐╤А╨╕╨╜╤П╤В╤М ╤З╨░╤Б╤В╤М HTTP ╨╖╨░╨┐╤А╨╛╤Б╨░.
 */
static void on_recv(void *arg, int fd, uint32_t events);

/*
 * ╨Я╨╡╤А╨╡╨▓╨╛╨┤╨╕╤В ╨▓╤Е╨╛╨┤╤П╤Й╨╡╨╡ ╤Б╨╛╨╡╨┤╨╕╨╜╨╡╨╜╨╕╨╡ ╨▓ ╨╜╨╡╨▒╨╗╨╛╨║╨╕╤А╤Г╤О╤Й╨╕╨╣ ╤А╨╡╨╢╨╕╨╝.
 */
static void set_nonblocking(int fd);

/*
 * ╨Я╨╡╤З╨░╤В╨░╨╡╤В ╨┐╨╡╤А╨╡╨┤╨░╨╜╨╜╤Л╨╡ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В╤Л ╨▓ stderr ╨╕ ╨▓╤Л╤Е╨╛╨┤╨╕╤В ╨╕╨╖ ╨┐╤А╨╛╤Ж╨╡╤Б╤Б╨░ ╤Б
 * ╨║╨╛╨┤╨╛╨╝ `EXIT_FAILURE`.
 */
static noreturn void fail(const char *format, ...);

/*
 * ╨Т╨╛╨╖╨▓╤А╨░╤Й╨░╨╡╤В ╤Д╨░╨╣╨╗╨╛╨▓╤Л╨╣ ╨┤╨╡╤Б╨║╤А╨╕╨┐╤В╨╛╤А ╤Б╨╛╨║╨╡╤В╨░, ╤Б╨┐╨╛╤Б╨╛╨▒╨╜╨╛╨│╨╛ ╨┐╤А╨╕╨╜╨╕╨╝╨░╤В╤М ╨╜╨╛╨▓╤Л╨╡
 * TCP ╤Б╨╛╨╡╨┤╨╕╨╜╨╡╨╜╨╕╤П.
 */
static int new_server(bool reuse_port);

рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдореИрдХреНрд░реЛ рдХрд╛ рднреА рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ SAFE_CALL() рдФрд░ рдлрд╝рдВрдХреНрд╢рди рдкрд░рд┐рднрд╛рд╖рд┐рдд рд╣реИ fail(). рдореИрдХреНрд░реЛ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐ рдХреЗ рдорд╛рди рдХреА рддреБрд▓рдирд╛ рддреНрд░реБрдЯрд┐ рд╕реЗ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдпрджрд┐ рд╕реНрдерд┐рддрд┐ рд╕рддреНрдп рд╣реИ, рддреЛ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ fail():

#define SAFE_CALL(call, error)                                                 
    do {                                                                       
        if ((call) == error) {                                                   
            fail("%s", #call);                                                 
        }                                                                      
    } while (false)

рд╕рдорд╛рд░реЛрд╣ fail() рдкрд╛рд░рд┐рдд рддрд░реНрдХреЛрдВ рдХреЛ рдЯрд░реНрдорд┐рдирд▓ рдкрд░ рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИ (рдЬреИрд╕реЗ printf()) рдФрд░ рдХреЛрдб рдХреЗ рд╕рд╛рде рдкреНрд░реЛрдЧреНрд░рд╛рдо рдХреЛ рд╕рдорд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ EXIT_FAILURE:

static noreturn void fail(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, ": %sn", strerror(errno));
    exit(EXIT_FAILURE);
}

рд╕рдорд╛рд░реЛрд╣ new_server() рд╕рд┐рд╕реНрдЯрдо рдХреЙрд▓ рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдП рдЧрдП "рд╕рд░реНрд╡рд░" рд╕реЙрдХреЗрдЯ рдХрд╛ рдлрд╝рд╛рдЗрд▓ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рд▓реМрдЯрд╛рддрд╛ рд╣реИ socket(), bind() ╨╕ listen() рдФрд░ рдЖрдиреЗ рд╡рд╛рд▓реЗ рдХрдиреЗрдХреНрд╢рдиреЛрдВ рдХреЛ рдиреЙрди-рдмреНрд▓реЙрдХрд┐рдВрдЧ рдореЛрдб рдореЗрдВ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИред

new_server() рдлрд╝рдВрдХреНрд╢рди рджрд┐рдЦрд╛рдПрдБ

static int new_server(bool reuse_port) {
    int fd;
    SAFE_CALL((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)),
              -1);

    if (reuse_port) {
        SAFE_CALL(
            setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)),
            -1);
    }

    struct sockaddr_in addr = {.sin_family = AF_INET,
                               .sin_port = htons(SERVER_PORT),
                               .sin_addr = {.s_addr = inet_addr(SERVER_IPV4)},
                               .sin_zero = {0}};

    SAFE_CALL(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), -1);
    SAFE_CALL(listen(fd, SERVER_BACKLOG), -1);
    return fd;
}

  • рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рд╕реЙрдХреЗрдЯ рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ рдзреНрд╡рдЬ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЧреИрд░-рдЕрд╡рд░реБрджреНрдз рдореЛрдб рдореЗрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ SOCK_NONBLOCKрддрд╛рдХрд┐ рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ on_accept() (рдФрд░ рдкрдврд╝реЗрдВ) рд╕рд┐рд╕реНрдЯрдо рдХреЙрд▓ accept() рдереНрд░реЗрдб рдирд┐рд╖реНрдкрд╛рджрди рдХреЛ рдирд╣реАрдВ рд░реЛрдХрд╛ред
  • рдЕрдЧрд░ reuse_port рдХреЗ рдмрд░рд╛рдмрд░ рд╣реИ true, рддреЛ рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рд╡рд┐рдХрд▓реНрдк рдХреЗ рд╕рд╛рде рд╕реЙрдХреЗрдЯ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдЧрд╛ SO_REUSEPORT рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ setsockopt()рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдПрдХ рд╣реА рдкреЛрд░реНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП (рдЕрдиреБрднрд╛рдЧ "рдорд▓реНрдЯреА-рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░" рджреЗрдЦреЗрдВ)ред

рдЖрдпреЛрдЬрди рдкреНрд░рдмрдВрдзрдХрд░реНрддрд╛ on_accept() OS рджреНрд╡рд╛рд░рд╛ рдХреЛрдИ рдИрд╡реЗрдВрдЯ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ EPOLLIN, рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдирдпрд╛ рдХрдиреЗрдХреНрд╢рди рд╕реНрд╡реАрдХрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред on_accept() рдПрдХ рдирдпрд╛ рдХрдиреЗрдХреНрд╢рди рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ рдиреЙрди-рдмреНрд▓реЙрдХрд┐рдВрдЧ рдореЛрдб рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреЗ рд╕рд╛рде рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░рддрд╛ рд╣реИ on_recv() I/O рд░рд┐рдПрдХреНрдЯрд░ рдореЗрдВ.

on_accept() рдлрд╝рдВрдХреНрд╢рди рджрд┐рдЦрд╛рдПрдВ

static void on_accept(void *arg, int fd, uint32_t events) {
    int incoming_conn;
    SAFE_CALL((incoming_conn = accept(fd, NULL, NULL)), -1);
    set_nonblocking(incoming_conn);
    SAFE_CALL(reactor_register(reactor, incoming_conn, EPOLLIN, on_recv,
                               request_buffer_new()),
              -1);
}

рдЖрдпреЛрдЬрди рдкреНрд░рдмрдВрдзрдХрд░реНрддрд╛ on_recv() OS рджреНрд╡рд╛рд░рд╛ рдХреЛрдИ рдИрд╡реЗрдВрдЯ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ EPOLLIN, рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдорддрд▓рдм рд╣реИ рдХрд┐ рдХрдиреЗрдХреНрд╢рди рдкрдВрдЬреАрдХреГрдд рд╣реИ on_accept(), рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ред

on_recv() HTTP рдЕрдиреБрд░реЛрдз рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдиреЗ рддрдХ рдХрдиреЗрдХреНрд╢рди рд╕реЗ рдбреЗрдЯрд╛ рдкрдврд╝рддрд╛ рд╣реИ, рдлрд┐рд░ рдпрд╣ рдПрдХ рд╣реИрдВрдбрд▓рд░ рдкрдВрдЬреАрдХреГрдд рдХрд░рддрд╛ рд╣реИ on_send() HTTP рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП. рдпрджрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдХрдиреЗрдХреНрд╢рди рддреЛрдбрд╝ рджреЗрддрд╛ рд╣реИ, рддреЛ рд╕реЙрдХреЗрдЯ рдХреЛ рдЕрдкрдВрдЬреАрдХреГрдд рдХрд░ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдмрдВрдж рдХрд░ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ close().

рдлрд╝рдВрдХреНрд╢рди on_recv рджрд┐рдЦрд╛рдПрдБ()

static void on_recv(void *arg, int fd, uint32_t events) {
    RequestBuffer *buffer = arg;

    // ╨Я╤А╨╕╨╜╨╕╨╝╨░╨╡╨╝ ╨▓╤Е╨╛╨┤╨╜╤Л╨╡ ╨┤╨░╨╜╨╜╤Л╨╡ ╨┤╨╛ ╤В╨╡╤Е ╨┐╨╛╤А, ╤З╤В╨╛ recv ╨▓╨╛╨╖╨▓╤А╨░╤В╨╕╤В 0 ╨╕╨╗╨╕ ╨╛╤И╨╕╨▒╨║╤Г
    ssize_t nread;
    while ((nread = recv(fd, buffer->data + buffer->size,
                         REQUEST_BUFFER_CAPACITY - buffer->size, 0)) > 0)
        buffer->size += nread;

    // ╨Ъ╨╗╨╕╨╡╨╜╤В ╨╛╨▒╨╛╤А╨▓╨░╨╗ ╤Б╨╛╨╡╨┤╨╕╨╜╨╡╨╜╨╕╨╡
    if (nread == 0) {
        SAFE_CALL(reactor_deregister(reactor, fd), -1);
        SAFE_CALL(close(fd), -1);
        request_buffer_destroy(buffer);
        return;
    }

    // read ╨▓╨╡╤А╨╜╤Г╨╗ ╨╛╤И╨╕╨▒╨║╤Г, ╨╛╤В╨╗╨╕╤З╨╜╤Г╤О ╨╛╤В ╨╛╤И╨╕╨▒╨║╨╕, ╨┐╤А╨╕ ╨║╨╛╤В╨╛╤А╨╛╨╣ ╨▓╤Л╨╖╨╛╨▓ ╨╖╨░╨▒╨╗╨╛╨║╨╕╤А╤Г╨╡╤В
    // ╨┐╨╛╤В╨╛╨║
    if (errno != EAGAIN && errno != EWOULDBLOCK) {
        request_buffer_destroy(buffer);
        fail("read");
    }

    // ╨Я╨╛╨╗╤Г╤З╨╡╨╜ ╨┐╨╛╨╗╨╜╤Л╨╣ HTTP ╨╖╨░╨┐╤А╨╛╤Б ╨╛╤В ╨║╨╗╨╕╨╡╨╜╤В╨░. ╨в╨╡╨┐╨╡╤А╤М ╤А╨╡╨│╨╕╤Б╤В╤А╨╕╤А╤Г╨╡╨╝ ╨╛╨▒╤А╨░╨▒╨╛╤В╤З╨╕╨║╨░
    // ╤Б╨╛╨▒╤Л╤В╨╕╨╣ ╨┤╨╗╤П ╨╛╤В╨┐╤А╨░╨▓╨║╨╕ ╨┤╨░╨╜╨╜╤Л╤Е
    if (request_buffer_is_complete(buffer)) {
        request_buffer_clear(buffer);
        SAFE_CALL(reactor_reregister(reactor, fd, EPOLLOUT, on_send, buffer),
                  -1);
    }
}

рдЖрдпреЛрдЬрди рдкреНрд░рдмрдВрдзрдХрд░реНрддрд╛ on_send() OS рджреНрд╡рд╛рд░рд╛ рдХреЛрдИ рдИрд╡реЗрдВрдЯ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ EPOLLOUT, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рдХрдиреЗрдХреНрд╢рди рдкрдВрдЬреАрдХреГрдд рд╣реИ on_recv(), рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ рд╣реИред рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдПрдХ рдЫрд╡рд┐ рдХреЗ рд╕рд╛рде HTML рдпреБрдХреНрдд HTTP рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рднреЗрдЬрддрд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдЗрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХреЛ рд╡рд╛рдкрд╕ рдмрджрд▓ рджреЗрддрд╛ рд╣реИ on_recv().

on_send() рдлрд╝рдВрдХреНрд╢рди рджрд┐рдЦрд╛рдПрдБ

static void on_send(void *arg, int fd, uint32_t events) {
    const char *content = "<img "
                          "src="https://habrastorage.org/webt/oh/wl/23/"
                          "ohwl23va3b-dioerobq_mbx4xaw.jpeg">";
    char response[1024];
    sprintf(response,
            "HTTP/1.1 200 OK" CRLF "Content-Length: %zd" CRLF "Content-Type: "
            "text/html" DOUBLE_CRLF "%s",
            strlen(content), content);

    SAFE_CALL(send(fd, response, strlen(response), 0), -1);
    SAFE_CALL(reactor_reregister(reactor, fd, EPOLLIN, on_recv, arg), -1);
}

рдФрд░ рдЕрдВрдд рдореЗрдВ, рдлрд╝рд╛рдЗрд▓ рдореЗрдВ http_server.c, рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ main() рд╣рдо рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ I/O рд░рд┐рдПрдХреНрдЯрд░ рдмрдирд╛рддреЗ рд╣реИрдВ reactor_new(), рдПрдХ рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯ рдмрдирд╛рдПрдВ рдФрд░ рдЗрд╕реЗ рдкрдВрдЬреАрдХреГрдд рдХрд░реЗрдВ, рд░рд┐рдПрдХреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╢реБрд░реВ рдХрд░реЗрдВ reactor_run() рдареАрдХ рдПрдХ рдорд┐рдирдЯ рдХреЗ рд▓рд┐рдП, рдФрд░ рдлрд┐рд░ рд╣рдо рд╕рдВрд╕рд╛рдзрди рдЬрд╛рд░реА рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдкреНрд░реЛрдЧреНрд░рд╛рдо рд╕реЗ рдмрд╛рд╣рд░ рдирд┐рдХрд▓ рдЬрд╛рддреЗ рд╣реИрдВред

http_server.c рджрд┐рдЦрд╛рдПрдБ

#include "reactor.h"

static Reactor *reactor;

#include "common.h"

int main(void) {
    SAFE_CALL((reactor = reactor_new()), NULL);
    SAFE_CALL(
        reactor_register(reactor, new_server(false), EPOLLIN, on_accept, NULL),
        -1);
    SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
    SAFE_CALL(reactor_destroy(reactor), -1);
}

рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рд╕рдм рдХреБрдЫ рдЙрдореНрдореАрдж рдХреЗ рдореБрддрд╛рдмрд┐рдХ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред рд╕рдВрдХрд▓рди (chmod a+x compile.sh && ./compile.sh рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд░реВрдЯ рдореЗрдВ) рдФрд░ рд╕реНрд╡-рд▓рд┐рдЦрд┐рдд рд╕рд░реНрд╡рд░ рд▓реЙрдиреНрдЪ рдХрд░реЗрдВ, рдЦреЛрд▓реЗрдВ http://127.0.0.1:18470 рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рджреЗрдЦреЗрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рд╣рдореЗрдВ рдХреНрдпрд╛ рдЙрдореНрдореАрдж рдереА:

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рдкрд░рдлреЙрд░рдореЗрдВрд╕ рдирд╛рдкрдирд╛

рдореЗрд░реА рдХрд╛рд░ рдХреА рд╡рд┐рд╢рд┐рд╖реНрдЯрддрд╛рдПрдБ рджрд┐рдЦрд╛рдПрдБ

$ screenfetch
 MMMMMMMMMMMMMMMMMMMMMMMMMmds+.        OS: Mint 19.1 tessa
 MMm----::-://////////////oymNMd+`     Kernel: x86_64 Linux 4.15.0-20-generic
 MMd      /++                -sNMd:    Uptime: 2h 34m
 MMNso/`  dMM    `.::-. .-::.` .hMN:   Packages: 2217
 ddddMMh  dMM   :hNMNMNhNMNMNh: `NMm   Shell: bash 4.4.20
     NMm  dMM  .NMN/-+MMM+-/NMN` dMM   Resolution: 1920x1080
     NMm  dMM  -MMm  `MMM   dMM. dMM   DE: Cinnamon 4.0.10
     NMm  dMM  -MMm  `MMM   dMM. dMM   WM: Muffin
     NMm  dMM  .mmd  `mmm   yMM. dMM   WM Theme: Mint-Y-Dark (Mint-Y)
     NMm  dMM`  ..`   ...   ydm. dMM   GTK Theme: Mint-Y [GTK2/3]
     hMM- +MMd/-------...-:sdds  dMM   Icon Theme: Mint-Y
     -NMm- :hNMNNNmdddddddddy/`  dMM   Font: Noto Sans 9
      -dMNs-``-::::-------.``    dMM   CPU: Intel Core i7-6700 @ 8x 4GHz [52.0┬░C]
       `/dMNmy+/:-------------:/yMMM   GPU: NV136
          ./ydNMMMMMMMMMMMMMMMMMMMMM   RAM: 2544MiB / 7926MiB
             .MMMMMMMMMMMMMMMMMMM

рдЖрдЗрдП рдПрдХрд▓-рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░ рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдорд╛рдкреЗрдВред рдЖрдЗрдП рджреЛ рдЯрд░реНрдорд┐рдирд▓ рдЦреЛрд▓реЗрдВ: рдПрдХ рдореЗрдВ рд╣рдо рдЪрд▓реЗрдВрдЧреЗ ./http_server, рдПрдХ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ - рдХреБрд▓реНрд╣рд╛рдбрд╝реА. рдПрдХ рдорд┐рдирдЯ рдХреЗ рдмрд╛рдж, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЖрдБрдХрдбрд╝реЗ рджреВрд╕рд░реЗ рдЯрд░реНрдорд┐рдирд▓ рдореЗрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рд╣реЛрдВрдЧреЗ:

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   493.52us   76.70us  17.31ms   89.57%
    Req/Sec    24.37k     1.81k   29.34k    68.13%
  11657769 requests in 1.00m, 1.60GB read
Requests/sec: 193974.70
Transfer/sec:     27.19MB

рд╣рдорд╛рд░рд╛ рдПрдХрд▓-рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░ 11 рдХрдиреЗрдХреНрд╢рдиреЛрдВ рд╕реЗ рдЙрддреНрдкрдиреНрди рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рддрд┐ рдорд┐рдирдЯ 100 рдорд┐рд▓рд┐рдпрди рд╕реЗ рдЕрдзрд┐рдХ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдерд╛ред рдмреБрд░рд╛ рдкрд░рд┐рдгрд╛рдо рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдХреНрдпрд╛ рдЗрд╕рдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ?

рдорд▓реНрдЯреАрдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░

рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, I/O рд░рд┐рдПрдХреНрдЯрд░ рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдереНрд░реЗрдб рдореЗрдВ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рд╕рднреА рд╕реАрдкреАрдпреВ рдХреЛрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдЖрдЗрдП рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЛ рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ рд▓рд╛рдПрдВ:

http_server_multithreaded.c рджрд┐рдЦрд╛рдПрдБ

#include "reactor.h"

static Reactor *reactor;
#pragma omp threadprivate(reactor)

#include "common.h"

int main(void) {
#pragma omp parallel
    {
        SAFE_CALL((reactor = reactor_new()), NULL);
        SAFE_CALL(reactor_register(reactor, new_server(true), EPOLLIN,
                                   on_accept, NULL),
                  -1);
        SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
        SAFE_CALL(reactor_destroy(reactor), -1);
    }
}

рдЕрдм рд╣рд░ рдзрд╛рдЧрд╛ рдЙрд╕рдХрд╛ рдЕрдкрдирд╛ рд╣реИ рд░рд┐рдПрдХреНрдЯрд░:

static Reactor *reactor;
#pragma omp threadprivate(reactor)

рдХреГрдкрдпрд╛ рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдлрд╝рдВрдХреНрд╢рди рддрд░реНрдХ new_server() рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИ true. рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╣рдо рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯ рдХреЛ рд╡рд┐рдХрд▓реНрдк рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддреЗ рд╣реИрдВ SO_REUSEPORTрдЗрд╕реЗ рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред рдЖрдк рдЕрдзрд┐рдХ рд╡рд┐рд╡рд░рдг рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╣рд╛рдВ.

рджреВрд╕рд░рд╛ рд░рди

рдЖрдЗрдП рдЕрдм рдорд▓реНрдЯреА-рдереНрд░реЗрдбреЗрдб рд╕рд░реНрд╡рд░ рдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдорд╛рдкреЗрдВ:

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.14ms    2.53ms  40.73ms   89.98%
    Req/Sec    79.98k    18.07k  154.64k    78.65%
  38208400 requests in 1.00m, 5.23GB read
Requests/sec: 635876.41
Transfer/sec:     89.14MB

1 рдорд┐рдирдЯ рдореЗрдВ рд╕рдВрд╕рд╛рдзрд┐рдд рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ ~3.28 рдЧреБрдирд╛ рдмрдврд╝ рдЧрдИ! рд▓реЗрдХрд┐рди рд╣рдо рд░рд╛рдЙрдВрдб рдирдВрдмрд░ рд╕реЗ рдХреЗрд╡рд▓ ~XNUMX рдорд┐рд▓рд┐рдпрди рдХрдо рдереЗ, рддреЛ рдЖрдЗрдП рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдЖрдЗрдП рдЙрддреНрдкрдиреНрди рдЖрдБрдХрдбрд╝реЛрдВ рдкрд░ рдирдЬрд╝рд░ рдбрд╛рд▓реЗрдВ рдкрд░реНрдлрд╝:

$ sudo perf stat -B -e task-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,branches,branch-misses,cache-misses ./http_server_multithreaded

 Performance counter stats for './http_server_multithreaded':

     242446,314933      task-clock (msec)         #    4,000 CPUs utilized          
         1тАп813тАп074      context-switches          #    0,007 M/sec                  
             4тАп689      cpu-migrations            #    0,019 K/sec                  
               254      page-faults               #    0,001 K/sec                  
   895тАп324тАп830тАп170      cycles                    #    3,693 GHz                    
   621тАп378тАп066тАп808      instructions              #    0,69  insn per cycle         
   119тАп926тАп709тАп370      branches                  #  494,653 M/sec                  
     3тАп227тАп095тАп669      branch-misses             #    2,69% of all branches        
           808тАп664      cache-misses                                                

      60,604330670 seconds time elapsed

рд╕реАрдкреАрдпреВ рдПрдлрд╝рд┐рдирд┐рдЯреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛, рдХреЗ рд╕рд╛рде рд╕рдВрдХрд▓рди -march=native, PGO, рд╣рд┐рдЯреНрд╕ рдХреА рд╕рдВрдЦреНрдпрд╛ рдореЗрдВ рд╡реГрджреНрдзрд┐ рдирдХрдж, рдмрдврд╝реЛрддрд░реА MAX_EVENTS рдФрд░ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ EPOLLET рдкреНрд░рджрд░реНрд╢рди рдореЗрдВ рдЙрд▓реНрд▓реЗрдЦрдиреАрдп рд╡реГрджреНрдзрд┐ рдирд╣реАрдВ рд╣реБрдИ. рд▓реЗрдХрд┐рди рдпрджрд┐ рдЖрдк рдПрдХ рд╕рд╛рде рдХрдиреЗрдХреНрд╢рди рдХреА рд╕рдВрдЦреНрдпрд╛ рдмрдврд╝рд╛ рджреЗрдВ рддреЛ рдХреНрдпрд╛ рд╣реЛрдЧрд╛?

352 рдПрдХ рд╕рд╛рде рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдЖрдБрдХрдбрд╝реЗ:

$ wrk -c352 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 352 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.12ms    3.79ms  68.23ms   87.49%
    Req/Sec    83.78k    12.69k  169.81k    83.59%
  40006142 requests in 1.00m, 5.48GB read
Requests/sec: 665789.26
Transfer/sec:     93.34MB

рд╡рд╛рдВрдЫрд┐рдд рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╛рдкреНрдд рд╣реБрдЖ, рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рдХрдиреЗрдХреНрд╢рди рдХреА рд╕рдВрдЦреНрдпрд╛ рдкрд░ 1 рдорд┐рдирдЯ рдореЗрдВ рд╕рдВрд╕рд╛рдзрд┐рдд рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреА рдирд┐рд░реНрднрд░рддрд╛ рджрд┐рдЦрд╛рдиреЗ рд╡рд╛рд▓рд╛ рдПрдХ рджрд┐рд▓рдЪрд╕реНрдк рдЧреНрд░рд╛рдл рднреА рдорд┐рд▓рд╛:

рдкреВрд░реНрдг рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рд╡рд╛рд▓рд╛ рдмреЗрдпрд░-рд╕реА I/O рд░рд┐рдПрдХреНрдЯрд░

рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдХреБрдЫ рд╕реМ рдХрдиреЗрдХреНрд╢рдиреЛрдВ рдХреЗ рдмрд╛рдж, рджреЛрдиреЛрдВ рд╕рд░реНрд╡рд░реЛрдВ рдХреЗ рд▓рд┐рдП рд╕рдВрд╕рд╛рдзрд┐рдд рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рддреЗрдЬреА рд╕реЗ рдХрдо рд╣реЛ рдЬрд╛рддреА рд╣реИ (рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдпрд╣ рдЕрдзрд┐рдХ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ)ред рдХреНрдпрд╛ рдпрд╣ рд▓рд┐рдирдХреНрд╕ рдЯреАрд╕реАрдкреА/рдЖрдИрдкреА рд╕реНрдЯреИрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реИ? рдЧреНрд░рд╛рдлрд╝ рдХреЗ рдЗрд╕ рд╡реНрдпрд╡рд╣рд╛рд░ рдФрд░ рдорд▓реНрдЯреА-рдереНрд░реЗрдбреЗрдб рдФрд░ рд╕рд┐рдВрдЧрд▓-рдереНрд░реЗрдбреЗрдб рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреЗ рдЕрдиреБрдХреВрд▓рди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдкрдиреА рдзрд╛рд░рдгрд╛рдПрдБ рдмреЗрдЭрд┐рдЭрдХ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ рд▓рд┐рдЦреЗрдВред

рдЬреИрд╕рд╛ рд╡рд┐рдЦреНрдпрд╛рдд рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ, рдпрд╣ рдкреНрд░рджрд░реНрд╢рди рдкрд░реАрдХреНрд╖рдг рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд▓реЛрдб рдХреЗ рддрд╣рдд I/O рд░рд┐рдПрдХреНрдЯрд░ рдХреЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рдирд╣реАрдВ рджрд┐рдЦрд╛рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд▓рдЧрднрдЧ рд╣рдореЗрд╢рд╛ рд╕рд░реНрд╡рд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░реИрдХреНрдЯ рдХрд░рддрд╛ рд╣реИ, рд▓реЙрдЧ рдЖрдЙрдЯрдкреБрдЯ рдХрд░рддрд╛ рд╣реИ, рдХреНрд░рд┐рдкреНрдЯреЛрдЧреНрд░рд╛рдлреА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ рдЯреАрдПрд▓рдПрд╕ рдЖрджрд┐, рдЬрд┐рд╕рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рднрд╛рд░ рдЧреИрд░-рд╕рдорд╛рди (рдЧрддрд┐рд╢реАрд▓) рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред I/O рдкреНрд░реЙрдПрдХреНрдЯрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд▓реЗрдЦ рдореЗрдВ рддреАрд╕рд░реЗ рдкрдХреНрд╖ рдХреЗ рдШрдЯрдХреЛрдВ рдХреЗ рд╕рд╛рде рдорд┐рд▓рдХрд░ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред

I/O рд░рд┐рдПрдХреНрдЯрд░ рдХреЗ рдиреБрдХрд╕рд╛рди

рдЖрдкрдХреЛ рдпрд╣ рд╕рдордЭрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ I/O рд░рд┐рдПрдХреНрдЯрд░ рдЕрдкрдиреА рдХрдорд┐рдпреЛрдВ рд╕реЗ рд░рд╣рд┐рдд рдирд╣реАрдВ рд╣реИ, рдЕрд░реНрдерд╛рддреН:

  • рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ I/O рд░рд┐рдПрдХреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдХреБрдЫ рдЕрдзрд┐рдХ рдХрдард┐рди рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЖрдкрдХреЛ рдкреНрд░рд╡рд╛рд╣ рдХреЛ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдкреНрд░рдмрдВрдзрд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛.
  • рдЕрднреНрдпрд╛рд╕ рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рд▓реЛрдб рдЕрд╕рдорд╛рди рд╣реЛрддрд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдПрдХ рдереНрд░реЗрдб рд▓реЙрдЧрд┐рдВрдЧ рд╣реЛ рд╕рдХрддреА рд╣реИ рдЬрдмрдХрд┐ рджреВрд╕рд░рд╛ рдХрд╛рдо рдореЗрдВ рд╡реНрдпрд╕реНрдд рд╣реЛрддрд╛ рд╣реИред
  • рдпрджрд┐ рдПрдХ рдИрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд░ рдХрд┐рд╕реА рдереНрд░реЗрдб рдХреЛ рдмреНрд▓реЙрдХ рдХрд░ рджреЗрддрд╛ рд╣реИ, рддреЛ рд╕рд┐рд╕реНрдЯрдо рдЪрдпрдирдХрд░реНрддрд╛ рд╕реНрд╡рдпрдВ рднреА рдмреНрд▓реЙрдХ рдХрд░ рджреЗрдЧрд╛, рдЬрд┐рд╕рд╕реЗ рдмрдЧ рдвреВрдВрдврдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред

рдЗрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╕рдорд╛рдзрд╛рди рдХрд░рддрд╛ рд╣реИ I/O рдкреНрд░реЙрдХреНрдЯрд░, рдЬрд┐рд╕рдореЗрдВ рдЕрдХреНрд╕рд░ рдПрдХ рд╢реЗрдбреНрдпреВрд▓рд░ рд╣реЛрддрд╛ рд╣реИ рдЬреЛ рдереНрд░реЗрдб рдХреЗ рдкреВрд▓ рдореЗрдВ рд▓реЛрдб рдХреЛ рд╕рдорд╛рди рд░реВрдк рд╕реЗ рд╡рд┐рддрд░рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдПрдХ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдПрдкреАрдЖрдИ рднреА рд╣реЛрддрд╛ рд╣реИред рд╣рдо рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдж рдореЗрдВ, рдЕрдкрдиреЗ рджреВрд╕рд░реЗ рд▓реЗрдЦ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗред

рдирд┐рд╖реНрдХрд░реНрд╖

рдпрд╣реАрдВ рдкрд░ рд╕рд┐рджреНрдзрд╛рдВрдд рд╕реЗ рд╕реАрдзреЗ рдкреНрд░реЛрдлрд╛рдЗрд▓рд░ рдПрдЧреНрдЬреЙрд╕реНрдЯ рддрдХ рдХреА рд╣рдорд╛рд░реА рдпрд╛рддреНрд░рд╛ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЧрдИ рд╣реИред

рдЖрдкрдХреЛ рдЗрд╕ рдкрд░ рдзреНрдпрд╛рди рдирд╣реАрдВ рджреЗрдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ рд╕реБрд╡рд┐рдзрд╛ рдФрд░ рдЧрддрд┐ рдХреЗ рд╡рд┐рднрд┐рдиреНрди рд╕реНрддрд░реЛрдВ рдХреЗ рд╕рд╛рде рдиреЗрдЯрд╡рд░реНрдХ рд╕реЙрдлрд╝реНрдЯрд╡реЗрдпрд░ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рдЕрдиреНрдп рд╕рдорд╛рди рд░реВрдк рд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣реИрдВред рджрд┐рд▓рдЪрд╕реНрдк рд╣реИ, рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рд▓рд┐рдВрдХ рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рд╣реИрдВред

рдЬрдм рддрдХ рд╣рдо рдлрд┐рд░ рдорд┐рд▓реЗрдВрдЧреЗ!

рджрд┐рд▓рдЪрд╕реНрдк рдкрд░рд┐рдпреЛрдЬрдирд╛рдПрдБ

рдореБрдЭреЗ рдФрд░ рдХреНрдпрд╛ рдкрдврд╝рдирд╛ рдЪрд╛рд╣рд┐рдП?

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╝реЗрдВ