├── CMakeLists.txt
├── LICENSE
├── README.md
├── img
├── 1.png
├── 2.png
└── 3.png
├── include
├── Acceptor.h
├── AsyncLogging.h
├── Buffer.h
├── Callbacks.h
├── Channel.h
├── CurrentThread.h
├── EPollPoller.h
├── EventLoop.h
├── EventLoopThread.h
├── EventLoopThreadPool.h
├── FileUtil.h
├── FixedBuffer.h
├── InetAddress.h
├── KICachePolicy.h
├── LFU.h
├── LogFile.h
├── LogStream.h
├── Logger.h
├── Poller.h
├── Socket.h
├── TcpConnection.h
├── TcpServer.h
├── Thread.h
├── Timer.h
├── TimerQueue.h
├── Timestamp.h
├── memoryPool.h
└── noncopyable.h
├── lib
├── liblog_lib.so
├── libmemory_lib.so
└── libsrc_lib.so
├── log
├── AsyncLogging.cc
├── CMakeLists.txt
├── CurrentThread.cc
├── FileUtil.cc
├── LogFile.cc
└── LogStream.cc
├── memory
├── CMakeLists.txt
└── memoryPool.cc
└── src
├── Acceptor.cc
├── Buffer.cc
├── CMakeLists.txt
├── Channel.cc
├── CurrentThread.cc
├── DefaultPoller.cc
├── EPollPoller.cc
├── EventLoop.cc
├── EventLoopThread.cc
├── EventLoopThreadPool.cc
├── InetAddress.cc
├── Logger.cc
├── Poller.cc
├── Socket.cc
├── TcpConnection.cc
├── TcpServer.cc
├── Thread.cc
├── Timer.cc
├── TimerQueue.cc
├── Timestamp.cc
└── main.cc
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #顶层Cmake,设置全局链接库以及添加子目录,和项目的头文件.h位置
2 | #设置最低版本和项目名称
3 | cmake_minimum_required(VERSION 3.0)
4 | project(kama-webserver)
5 |
6 | #设置全局的c++标准
7 | set (CMAKE_CXX_STANDARD 11)
8 | set (CMAKE_CXX_STANDARD_REQUIRED True)
9 |
10 | #设置头文件目录,供所有子项目使用
11 | include_directories(${CMAKE_SOURCE_DIR}/include)
12 |
13 | # 设置输出目录
14 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
15 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
16 |
17 | #设置全局链接库
18 | set(LIBS
19 | pthread
20 | )
21 |
22 | #添加子目录使用
23 | add_subdirectory(src)
24 | add_subdirectory(memory)
25 | add_subdirectory(log)
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kama-webserver
2 | 【代码随想录知识星球】项目分享-webserver
3 |
4 | ## 项目介绍
5 |
6 | 本项目是一个高性能的WEB服务器,使用C++实现,项目底层采用了muduo库核心的设计思想,多线程多Reactor的网络模型,并且在这基础上增加了内存池,高效的双缓冲异步日志系统,以及LFU的缓存。
7 |
8 | ## 开发环境
9 |
10 | * linux kernel version5.15.0-113-generic (ubuntu 22.04.6)
11 | * gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
12 | * cmake version 3.22
13 |
14 | ## 目录结构
15 |
16 | ```shell
17 | kama-webserver/
18 | ├── img/ #存放图片
19 | ├── include/ #所有头文件.h位置
20 | ├── lib/ #存放共享库
21 | |
22 | ├── log/ # 日志管理模块
23 | │ ├── log.cc # 日志实现
24 | ├── memory/ # 内存管理模块
25 | │ ├── memory.cc # 内存管理实现
26 | ├── src/ # 源代码目录
27 | │ ├── main.cpp # 主程序入口
28 | │ ├── ... # 其他源文件
29 | |
30 | ├── CMakeLists.txt # CMake 构建文件
31 | ├── LICENSE # 许可证文件
32 | └── README.md # 项目说明文件
33 | ```
34 |
35 | ## 前置工具准备
36 |
37 | 安装基本工具
38 |
39 | ```bash
40 | sudo apt-get update
41 | sudo apt-get install -y wget cmake build-essential unzip git
42 | ```
43 |
44 | ## 编译指令
45 | 1. 克隆项目:
46 | ```bash
47 | git clone https://github.com/youngyangyang04/kama-webserver.git
48 | cd kama-webserver
49 | ```
50 |
51 | 2. 创建构建目录并编译:
52 |
53 | ```bash
54 | mkdir build &&
55 | cd build &&
56 | cmake .. &&
57 | make -j ${nproc}
58 | ```
59 |
60 | 3. 在构建完成后,先进入到bin文件
61 |
62 | ```bash
63 | cd bin
64 | ```
65 |
66 | 4. 启动项目可执行程序main
67 |
68 | ```bash
69 | ./main
70 | ```
71 |
72 | **注意**:需要再另外开一个新窗口运行`nc 127.0.0.1 8080`启动我们的客户端,来链接main可执行程序启动的web服务器
73 |
74 | ## 运行结果
75 | 通过运行项目中bin文件下可执行程序main,会出现如下结果:
76 |
77 | 其中日志文件将存放bin文件下的 `logs` 目录中,每次运行程序时,都会生成新的日志文件,记录程序的运行状态和错误信息。
78 |
79 | - 服务器的,运行结果如图
80 |
81 | 
82 |
83 | - 客户端的,运行结果如图
84 |
85 | 
86 |
87 | **注意**:测试的结果还是采用回声服务器测试,注重架构的实现。
88 |
89 | ---
90 |
91 | ### 日志核心内容简单分析:
92 |
93 | 首先日志结果如图:
94 | 
95 |
96 | 1. 文件描述符统计
97 |
98 | ```bash
99 | 2025/01/24 17:40:240290 INFO fd total count:1 - EPollPoller.cc:32
100 | ```
101 |
102 | - 说明: EPoll 当前管理的文件描述符总数为 1(可能是一个连接的套接字)。
103 |
104 | 2. 事件触发
105 |
106 | ```bash
107 | 2025/01/24 17:40:454794 INFO %d events happend1 - EPollPoller.cc:40
108 | 2025/01/24 17:40:454852 INFO channel handleEvent revents:1 - Channel.cc:73
109 | ```
110 |
111 | - 一个事件发生(events happend1),可能是客户端套接字的关闭事件。
112 | - revents:1 表示触发的事件类型为 EPOLLIN,即对端关闭了连接或者发送了数据。
113 |
114 | 3. 连接关闭处理
115 |
116 | ```bash
117 | 2025/01/24 17:40:454890 INFO TcpConnection::handleClose fd=13state=2 - TcpConnection.cc:241
118 | 2025/01/24 17:40:454907 INFO func =>fd13events=0index=1 - EPollPoller.cc:66
119 | 2025/01/24 17:40:454929 INFO Connection DOWN :127.0.0.1:47376 - main.cc:44
120 | ```
121 | - TcpConnection::handleClose: 文件描述符 fd=13 的连接关闭,当前状态 state=2(可能表示“已建立连接”状态)。
122 | - Connection DOWN: 与客户端 127.0.0.1:47376 的连接断开。
123 | - events=0: 表示该文件描述符不再监听任何事件。
124 |
125 | 4. 从服务器中移除连接
126 |
127 | ```bash
128 | 2025/01/24 17:40:455009 INFO TcpServer::removeConnectionInLoop [EchoServer] - connection %sEchoServer-127.0.0.1:8080#1 - TcpServer.cc:114
129 | 2025/01/24 17:40:455138 INFO removeChannel fd=13 - EPollPoller.cc:102
130 | ```
131 | - TcpServer::removeConnectionInLoop: 服务器内部移除与连接 127.0.0.1:47376 的绑定。
132 | - removeChannel: 从 EPoll 的事件监听列表中移除了文件描述符 fd=13。
133 |
134 | 5. 资源清理
135 |
136 | ```bash
137 | 2025/01/24 17:40:455155 INFO TcpConnection::dtor[EchoServer-127.0.0.1:8080#1]at fd=13state=0 - TcpConnection.cc:58
138 | ```
139 | - 调用 TcpConnection 析构函数(dtor),释放连接的相关资源。
140 | - 状态 state=0 表示连接已完全关闭,文件描述符 fd=13 被销毁。
141 |
142 |
143 | ## 功能模块划分
144 |
145 | ### 网络模块
146 |
147 | - **事件轮询与分发模块**:`EventLoop.*`、`Channel.*`、`Poller.*`、`EPollPoller.*`负责事件轮询检测,并实现事件分发处理。`EventLoop`对`Poller`进行轮询,`Poller`底层由`EPollPoller`实现。
148 | - **线程与事件绑定模块**:`Thread.*`、`EventLoopThread.*`、`EventLoopThreadPool.*`绑定线程与事件循环,完成`one loop per thread`模型。
149 | - **网络连接模块**:`TcpServer.*`、`TcpConnection.*`、`Acceptor.*`、`Socket.*`实现`mainloop`对网络连接的响应,并分发到各`subloop`。
150 | - **缓冲区模块**:`Buffer.*`提供自动扩容缓冲区,保证数据有序到达。
151 |
152 | ### 日志模块
153 |
154 | - 日志模块负责记录服务器运行过程中的重要信息,帮助开发者进行调试和性能分析。日志文件存放位于 `bin/logs/` 目录下。
155 |
156 | ### 内存管理
157 |
158 | - 内存管理模块负责动态内存的分配和释放,确保服务器在高负载情况下的稳定性和性能。
159 |
160 | ### LFU缓存模块
161 | - 用于在缓存容量不足时决定删除哪些内容以释放空间。LFU 的核心思想是优先移除使用频率最低的缓存项。
162 |
163 | ## 贡献
164 |
165 | 欢迎任何形式的贡献!请提交问题、建议或代码请求。
166 |
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/img/3.png
--------------------------------------------------------------------------------
/include/Acceptor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "noncopyable.h"
6 | #include "Socket.h"
7 | #include "Channel.h"
8 |
9 | class EventLoop;
10 | class InetAddress;
11 |
12 | class Acceptor : noncopyable
13 | {
14 | public:
15 | using NewConnectionCallback = std::function;
16 |
17 | Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);
18 | ~Acceptor();
19 | //设置新连接的回调函数
20 | void setNewConnectionCallback(const NewConnectionCallback &cb) { NewConnectionCallback_ = cb; }
21 | // 判断是否在监听
22 | bool listenning() const { return listenning_; }
23 | // 监听本地端口
24 | void listen();
25 |
26 | private:
27 | void handleRead();//处理新用户的连接事件
28 |
29 | EventLoop *loop_; // Acceptor用的就是用户定义的那个baseLoop 也称作mainLoop
30 | Socket acceptSocket_;//专门用于接收新连接的socket
31 | Channel acceptChannel_;//专门用于监听新连接的channel
32 | NewConnectionCallback NewConnectionCallback_;//新连接的回调函数
33 | bool listenning_;//是否在监听
34 | };
--------------------------------------------------------------------------------
/include/AsyncLogging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "noncopyable.h"
4 | #include "Thread.h"
5 | #include "FixedBuffer.h"
6 | #include "LogStream.h"
7 | #include "LogFile.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | class AsyncLogging
15 | {
16 | public:
17 | AsyncLogging(const std::string &basename, off_t rollSize, int flushInterval=3);
18 | ~AsyncLogging()
19 | {
20 | if (running_)
21 | {
22 | stop();
23 | }
24 | }
25 | // 前端调用append写入日志
26 | void append(const char *logline, int len);
27 | void start()
28 | {
29 | running_ = true;
30 | thread_.start();
31 | }
32 | void stop()
33 | {
34 | running_ = false;
35 | cond_.notify_one();
36 | }
37 |
38 | private:
39 | using LargeBuffer = FixedBuffer;
40 | using BufferVector = std::vector>;
41 | // BufferVector::value_type 是 std::vector> 的元素类型,也就是 std::unique_ptr。
42 | using BufferPtr = BufferVector::value_type;
43 | void threadFunc();
44 | const int flushInterval_; // 日志刷新时间
45 | std::atomic running_;
46 | const std::string basename_;
47 | const off_t rollSize_;
48 | Thread thread_;
49 | std::mutex mutex_;
50 | std::condition_variable cond_;
51 |
52 | BufferPtr currentBuffer_;
53 | BufferPtr nextBuffer_;
54 | BufferVector buffers_;
55 | };
--------------------------------------------------------------------------------
/include/Buffer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | // 网络库底层的缓冲区类型定义
9 | class Buffer
10 | {
11 | public:
12 | static const size_t kCheapPrepend = 8;//初始预留的prependabel空间大小
13 | static const size_t kInitialSize = 1024;
14 |
15 | explicit Buffer(size_t initalSize = kInitialSize)
16 | : buffer_(kCheapPrepend + initalSize)
17 | , readerIndex_(kCheapPrepend)
18 | , writerIndex_(kCheapPrepend)
19 | {
20 | }
21 |
22 | size_t readableBytes() const { return writerIndex_ - readerIndex_; }
23 | size_t writableBytes() const { return buffer_.size() - writerIndex_; }
24 | size_t prependableBytes() const { return readerIndex_; }
25 |
26 | // 返回缓冲区中可读数据的起始地址
27 | const char *peek() const { return begin() + readerIndex_; }
28 | void retrieve(size_t len)
29 | {
30 | if (len < readableBytes())
31 | {
32 | readerIndex_ += len; // 说明应用只读取了可读缓冲区数据的一部分,就是len长度 还剩下readerIndex+=len到writerIndex_的数据未读
33 | }
34 | else // len == readableBytes()
35 | {
36 | retrieveAll();
37 | }
38 | }
39 | void retrieveAll()
40 | {
41 | readerIndex_ = kCheapPrepend;
42 | writerIndex_ = kCheapPrepend;
43 | }
44 |
45 | // 把onMessage函数上报的Buffer数据 转成string类型的数据返回
46 | std::string retrieveAllAsString() { return retrieveAsString(readableBytes()); }
47 | std::string retrieveAsString(size_t len)
48 | {
49 | std::string result(peek(), len);
50 | retrieve(len); // 上面一句把缓冲区中可读的数据已经读取出来 这里肯定要对缓冲区进行复位操作
51 | return result;
52 | }
53 |
54 | // buffer_.size - writerIndex_
55 | void ensureWritableBytes(size_t len)
56 | {
57 | if (writableBytes() < len)
58 | {
59 | makeSpace(len); // 扩容
60 | }
61 | }
62 |
63 | // 把[data, data+len]内存上的数据添加到writable缓冲区当中
64 | void append(const char *data, size_t len)
65 | {
66 | ensureWritableBytes(len);
67 | std::copy(data, data+len, beginWrite());
68 | writerIndex_ += len;
69 | }
70 | char *beginWrite() { return begin() + writerIndex_; }
71 | const char *beginWrite() const { return begin() + writerIndex_; }
72 |
73 | // 从fd上读取数据
74 | ssize_t readFd(int fd, int *saveErrno);
75 | // 通过fd发送数据
76 | ssize_t writeFd(int fd, int *saveErrno);
77 |
78 | private:
79 | // vector底层数组首元素的地址 也就是数组的起始地址
80 | char *begin() { return &*buffer_.begin(); }
81 | const char *begin() const { return &*buffer_.begin(); }
82 |
83 | void makeSpace(size_t len)
84 | {
85 | /**
86 | * | kCheapPrepend |xxx| reader | writer | // xxx标示reader中已读的部分
87 | * | kCheapPrepend | reader | len |
88 | **/
89 | if (writableBytes() + prependableBytes() < len + kCheapPrepend) // 也就是说 len > xxx前面剩余的空间 + writer的部分
90 | {
91 | buffer_.resize(writerIndex_ + len);
92 | }
93 | else // 这里说明 len <= xxx + writer 把reader搬到从xxx开始 使得xxx后面是一段连续空间
94 | {
95 | size_t readable = readableBytes(); // readable = reader的长度
96 | // 将当前缓冲区中从readerIndex_到writerIndex_的数据
97 | // 拷贝到缓冲区起始位置kCheapPrepend处,以便腾出更多的可写空间
98 | std::copy(begin() + readerIndex_,
99 | begin() + writerIndex_,
100 | begin() + kCheapPrepend);
101 | readerIndex_ = kCheapPrepend;
102 | writerIndex_ = readerIndex_ + readable;
103 | }
104 | }
105 |
106 | std::vector buffer_;
107 | size_t readerIndex_;
108 | size_t writerIndex_;
109 | };
110 |
--------------------------------------------------------------------------------
/include/Callbacks.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class Buffer;
7 | class TcpConnection;
8 | class Timestamp;
9 |
10 | using TcpConnectionPtr = std::shared_ptr;
11 | using ConnectionCallback = std::function;
12 | using CloseCallback = std::function;
13 | using WriteCompleteCallback = std::function;
14 | using HighWaterMarkCallback = std::function;
15 |
16 | using MessageCallback = std::function;
--------------------------------------------------------------------------------
/include/Channel.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "noncopyable.h"
7 | #include "Timestamp.h"
8 |
9 | class EventLoop;
10 |
11 | /**
12 | * 理清楚 EventLoop、Channel、Poller之间的关系 Reactor模型上对应多路事件分发器
13 | * Channel理解为通道 封装了sockfd和其感兴趣的event 如EPOLLIN、EPOLLOUT事件 还绑定了poller返回的具体事件
14 | **/
15 | class Channel : noncopyable
16 | {
17 | public:
18 | using EventCallback = std::function; // muduo仍使用typedef
19 | using ReadEventCallback = std::function;
20 |
21 | Channel(EventLoop *loop, int fd);
22 | ~Channel();
23 |
24 | // fd得到Poller通知以后 处理事件 handleEvent在EventLoop::loop()中调用
25 | void handleEvent(Timestamp receiveTime);
26 |
27 | // 设置回调函数对象
28 | void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }
29 | void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); }
30 | void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }
31 | void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }
32 |
33 | // 防止当channel被手动remove掉 channel还在执行回调操作
34 | void tie(const std::shared_ptr &);
35 |
36 | int fd() const { return fd_; }
37 | int events() const { return events_; }
38 | void set_revents(int revt) { revents_ = revt; }
39 |
40 | // 设置fd相应的事件状态 相当于epoll_ctl add delete
41 | void enableReading() { events_ |= kReadEvent; update(); }
42 | void disableReading() { events_ &= ~kReadEvent; update(); }
43 | void enableWriting() { events_ |= kWriteEvent; update(); }
44 | void disableWriting() { events_ &= ~kWriteEvent; update(); }
45 | void disableAll() { events_ = kNoneEvent; update(); }
46 |
47 | // 返回fd当前的事件状态
48 | bool isNoneEvent() const { return events_ == kNoneEvent; }
49 | bool isWriting() const { return events_ & kWriteEvent; }
50 | bool isReading() const { return events_ & kReadEvent; }
51 |
52 | int index() { return index_; }
53 | void set_index(int idx) { index_ = idx; }
54 |
55 | // one loop per thread
56 | EventLoop *ownerLoop() { return loop_; }
57 | void remove();
58 | private:
59 |
60 | void update();
61 | void handleEventWithGuard(Timestamp receiveTime);
62 |
63 | static const int kNoneEvent;
64 | static const int kReadEvent;
65 | static const int kWriteEvent;
66 |
67 | EventLoop *loop_; // 事件循环
68 | const int fd_; // fd,Poller监听的对象
69 | int events_; // 注册fd感兴趣的事件
70 | int revents_; // Poller返回的具体发生的事件
71 | int index_;
72 |
73 | std::weak_ptr tie_;
74 | bool tied_;
75 |
76 | // 因为channel通道里可获知fd最终发生的具体的事件events,所以它负责调用具体事件的回调操作
77 | ReadEventCallback readCallback_;
78 | EventCallback writeCallback_;
79 | EventCallback closeCallback_;
80 | EventCallback errorCallback_;
81 | };
--------------------------------------------------------------------------------
/include/CurrentThread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | namespace CurrentThread
6 | {
7 | extern thread_local int t_cachedTid; // 保存tid缓存 因为系统调用非常耗时 拿到tid后将其保存
8 |
9 | void cacheTid();
10 |
11 | inline int tid() // 内联函数只在当前文件中起作用
12 | {
13 | if (__builtin_expect(t_cachedTid == 0, 0)) // __builtin_expect 是一种底层优化 此语句意思是如果还未获取tid 进入if 通过cacheTid()系统调用获取tid
14 | {
15 | cacheTid();
16 | }
17 | return t_cachedTid;
18 | }
19 | }
--------------------------------------------------------------------------------
/include/EPollPoller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "Poller.h"
7 | #include "Timestamp.h"
8 |
9 | /**
10 | * epoll的使用:
11 | * 1. epoll_create
12 | * 2. epoll_ctl (add, mod, del)
13 | * 3. epoll_wait
14 | **/
15 |
16 | class Channel;
17 |
18 | class EPollPoller : public Poller
19 | {
20 | public:
21 | EPollPoller(EventLoop *loop);
22 | ~EPollPoller() override;
23 |
24 | // 重写基类Poller的抽象方法
25 | Timestamp poll(int timeoutMs, ChannelList *activeChannels) override;
26 | void updateChannel(Channel *channel) override;
27 | void removeChannel(Channel *channel) override;
28 |
29 | private:
30 | static const int kInitEventListSize = 16;
31 |
32 | // 填写活跃的连接
33 | void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
34 | // 更新channel通道 其实就是调用epoll_ctl
35 | void update(int operation, Channel *channel);
36 |
37 | using EventList = std::vector; // C++中可以省略struct 直接写epoll_event即可
38 |
39 | int epollfd_; // epoll_create创建返回的fd保存在epollfd_中
40 | EventList events_; // 用于存放epoll_wait返回的所有发生的事件的文件描述符事件集
41 | };
--------------------------------------------------------------------------------
/include/EventLoop.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "noncopyable.h"
10 | #include "Timestamp.h"
11 | #include "CurrentThread.h"
12 | #include "TimerQueue.h"
13 | class Channel;
14 | class Poller;
15 |
16 | // 事件循环类 主要包含了两个大模块 Channel Poller(epoll的抽象)
17 | class EventLoop : noncopyable
18 | {
19 | public:
20 | using Functor = std::function;
21 |
22 | EventLoop();
23 | ~EventLoop();
24 |
25 | // 开启事件循环
26 | void loop();
27 | // 退出事件循环
28 | void quit();
29 |
30 | Timestamp pollReturnTime() const { return pollRetureTime_; }
31 |
32 | // 在当前loop中执行
33 | void runInLoop(Functor cb);
34 | // 把上层注册的回调函数cb放入队列中 唤醒loop所在的线程执行cb
35 | void queueInLoop(Functor cb);
36 |
37 | // 通过eventfd唤醒loop所在的线程
38 | void wakeup();
39 |
40 | // EventLoop的方法 => Poller的方法
41 | void updateChannel(Channel *channel);
42 | void removeChannel(Channel *channel);
43 | bool hasChannel(Channel *channel);
44 |
45 | // 判断EventLoop对象是否在自己的线程里
46 | bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } // threadId_为EventLoop创建时的线程id CurrentThread::tid()为当前线程id
47 | /**
48 | * 定时任务相关函数
49 | */
50 | void runAt(Timestamp timestamp, Functor &&cb)
51 | {
52 | timerQueue_->addTimer(std::move(cb), timestamp, 0.0);
53 | }
54 |
55 | void runAfter(double waitTime, Functor &&cb)
56 | {
57 | Timestamp time(addTime(Timestamp::now(), waitTime));
58 | runAt(time, std::move(cb));
59 | }
60 |
61 | void runEvery(double interval, Functor &&cb)
62 | {
63 | Timestamp timestamp(addTime(Timestamp::now(), interval));
64 | timerQueue_->addTimer(std::move(cb), timestamp, interval);
65 | }
66 |
67 | private:
68 | void handleRead(); // 给eventfd返回的文件描述符wakeupFd_绑定的事件回调 当wakeup()时 即有事件发生时 调用handleRead()读wakeupFd_的8字节 同时唤醒阻塞的epoll_wait
69 | void doPendingFunctors(); // 执行上层回调
70 |
71 | using ChannelList = std::vector;
72 |
73 | std::atomic_bool looping_; // 原子操作 底层通过CAS实现
74 | std::atomic_bool quit_; // 标识退出loop循环
75 |
76 | const pid_t threadId_; // 记录当前EventLoop是被哪个线程id创建的 即标识了当前EventLoop的所属线程id
77 |
78 | Timestamp pollRetureTime_; // Poller返回发生事件的Channels的时间点
79 | std::unique_ptr poller_;
80 | std::unique_ptr timerQueue_;
81 | int wakeupFd_; // 作用:当mainLoop获取一个新用户的Channel 需通过轮询算法选择一个subLoop 通过该成员唤醒subLoop处理Channel
82 | std::unique_ptr wakeupChannel_;
83 |
84 | ChannelList activeChannels_; // 返回Poller检测到当前有事件发生的所有Channel列表
85 |
86 | std::atomic_bool callingPendingFunctors_; // 标识当前loop是否有需要执行的回调操作
87 | std::vector pendingFunctors_; // 存储loop需要执行的所有回调操作
88 | std::mutex mutex_; // 互斥锁 用来保护上面vector容器的线程安全操作
89 | };
--------------------------------------------------------------------------------
/include/EventLoopThread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "noncopyable.h"
9 | #include "Thread.h"
10 |
11 | class EventLoop;
12 |
13 | class EventLoopThread : noncopyable
14 | {
15 | public:
16 | using ThreadInitCallback = std::function;
17 |
18 | EventLoopThread(const ThreadInitCallback &cb = ThreadInitCallback(),
19 | const std::string &name = std::string());
20 | ~EventLoopThread();
21 |
22 | EventLoop *startLoop();
23 |
24 | private:
25 | void threadFunc();
26 |
27 | EventLoop *loop_;
28 | bool exiting_;
29 | Thread thread_;
30 | std::mutex mutex_; // 互斥锁
31 | std::condition_variable cond_; // 条件变量
32 | ThreadInitCallback callback_;
33 | };
--------------------------------------------------------------------------------
/include/EventLoopThreadPool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "noncopyable.h"
9 | class EventLoop;
10 | class EventLoopThread;
11 |
12 | class EventLoopThreadPool : noncopyable
13 | {
14 | public:
15 | using ThreadInitCallback = std::function;
16 |
17 | EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg);
18 | ~EventLoopThreadPool();
19 |
20 | void setThreadNum(int numThreads) { numThreads_ = numThreads; }
21 |
22 | void start(const ThreadInitCallback &cb = ThreadInitCallback());
23 |
24 | // 如果工作在多线程中,baseLoop_(mainLoop)会默认以轮询的方式分配Channel给subLoop
25 | EventLoop *getNextLoop();
26 |
27 | std::vector getAllLoops(); // 获取所有的EventLoop
28 |
29 | bool started() const { return started_; } // 是否已经启动
30 | const std::string name() const { return name_; } // 获取名字
31 |
32 | private:
33 | EventLoop *baseLoop_; // 用户使用muduo创建的loop 如果线程数为1 那直接使用用户创建的loop 否则创建多EventLoop
34 | std::string name_;//线程池名称,通常由用户指定,线程池中EventLoopThread名称依赖于线程池名称。
35 | bool started_;//是否已经启动标志
36 | int numThreads_;//线程池中线程的数量
37 | int next_; // 新连接到来,所选择EventLoop的索引
38 | std::vector> threads_;//IO线程的列表
39 | std::vector loops_;//线程池中EventLoop的列表,指向的是EVentLoopThread线程函数创建的EventLoop对象。
40 | };
--------------------------------------------------------------------------------
/include/FileUtil.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include//off_t
5 | /**
6 | * @brief 文件工具类,用于处理文件的写入操作
7 | * 该类封装了对文件的基本操作,包括写入数据和刷新缓冲区
8 | */
9 | class FileUtil
10 | {
11 | public:
12 | /**
13 | * @brief 构造函数
14 | * @param file_name 要打开的文件名
15 | */
16 | FileUtil(std::string& file_name);
17 |
18 | /**
19 | * @brief 析构函数
20 | * 负责关闭文件和清理资源
21 | */
22 | ~FileUtil();
23 |
24 | /**
25 | * @brief 向文件写入数据
26 | * @param data 要写入的数据的指针
27 | * @param len 要写入的数据长度
28 | */
29 | void append(const char* data, size_t len);
30 |
31 | /**
32 | * @brief 刷新文件缓冲区
33 | * 将缓冲区中的数据立即写入文件
34 | */
35 | void flush();
36 |
37 | /**
38 | * @brief 获取已写入的字节数
39 | * @return 返回已写入文件的总字节数
40 | */
41 | off_t writtenBytes() const { return writtenBytes_; }
42 |
43 | private:
44 | size_t write(const char* data, size_t len);
45 | FILE* file_; // 文件指针,用于操作文件
46 | char buffer_[64*1024]; // 文件操作的缓冲区,大小为64KB,用于提高写入效率
47 | off_t writtenBytes_; // 记录已写入文件的总字节数,off_t类型用于大文件支持
48 | };
--------------------------------------------------------------------------------
/include/FixedBuffer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | // 类的前置声明
5 | class AsyncLogging;
6 | constexpr int kSmallBufferSize = 4000;
7 | constexpr int kLargeBufferSize = 4000 * 1000;
8 |
9 | // 固定的缓冲区类,用于管理日志数据的存储
10 | // 该类提供了一个固定大小的缓冲区,允许将数据追加到缓冲区中,并提供相关的操作方法
11 | template
12 | class FixedBuffer : noncopyable
13 | {
14 | public:
15 | // 构造函数,初始化当前指针为缓冲区的起始位置
16 | FixedBuffer()
17 | : cur_(data_), size_(0)
18 | {
19 | }
20 |
21 | // 将指定长度的数据追加到缓冲区
22 | // 如果缓冲区有足够的可用空间,则将数据复制到当前指针位置,并更新当前指针
23 | void append(const char *buf, size_t len)
24 | {
25 | if (avail() > len)
26 | {
27 | memcpy(cur_, buf, len); // 复制数据到缓冲区
28 | add(len);
29 | }
30 | }
31 |
32 | // 返回缓冲区的起始地址
33 | const char *data() const { return data_; }
34 |
35 | // 返回缓冲区中当前有效数据的长度
36 | int length() const { return size_; }
37 |
38 | // 返回当前指针的位置
39 | char *current() { return cur_; }
40 |
41 | // 返回缓冲区中剩余可用空间的大小
42 | size_t avail() const { return static_cast(buffer_size - size_); }
43 |
44 | // 更新当前指针,增加指定长度
45 | void add(size_t len)
46 | {
47 | cur_ += len;
48 | size_ += len;
49 | }
50 | // 重置当前指针,回到缓冲区的起始位置
51 | void reset()
52 | {
53 | cur_ = data_;
54 | size_ = 0;
55 | }
56 |
57 | // 清空缓冲区的数据
58 | void bzero() { ::bzero(data_, sizeof(data_)); }
59 |
60 | // 将缓冲区中的数据转换为std::string类型并返回
61 | std::string toString() const { return std::string(data_, length()); }
62 |
63 | private:
64 | char data_[buffer_size]; // 定义固定大小的缓冲区
65 | char *cur_; // 当前指针,指向缓冲区中下一个可写入的位置
66 | int size_; // 缓冲区的大小
67 | };
68 |
--------------------------------------------------------------------------------
/include/InetAddress.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | // 封装socket地址类型
8 | class InetAddress
9 | {
10 | public:
11 | explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1");
12 | explicit InetAddress(const sockaddr_in &addr)
13 | : addr_(addr)
14 | {
15 | }
16 |
17 | std::string toIp() const;
18 | std::string toIpPort() const;
19 | uint16_t toPort() const;
20 |
21 | const sockaddr_in *getSockAddr() const { return &addr_; }
22 | void setSockAddr(const sockaddr_in &addr) { addr_ = addr; }
23 |
24 | private:
25 | sockaddr_in addr_;
26 | };
--------------------------------------------------------------------------------
/include/KICachePolicy.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace KamaCache
4 | {
5 |
6 | template
7 | class KICachePolicy
8 | {
9 | public:
10 | virtual ~KICachePolicy() {};
11 |
12 | // 添加缓存接口
13 | virtual void put(Key key, Value value) = 0;
14 |
15 | // key是传入参数 访问到的值以传出参数的形式返回 | 访问成功返回true
16 | virtual bool get(Key key, Value& value) = 0;
17 | // 如果缓存中能找到key,则直接返回value
18 | virtual Value get(Key key) = 0;
19 |
20 | };
21 |
22 | } // namespace KamaCache
--------------------------------------------------------------------------------
/include/LFU.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "KICachePolicy.h"
11 |
12 | namespace KamaCache
13 | {
14 |
15 | template class KLfuCache;
16 |
17 | template
18 | class FreqList
19 | {
20 | private:
21 | struct Node
22 | {
23 | int freq; // 访问频次
24 | Key key;
25 | Value value;
26 | std::shared_ptr pre; // 上一结点
27 | std::shared_ptr next;
28 |
29 | Node()
30 | : freq(1), pre(nullptr), next(nullptr) {}
31 | Node(Key key, Value value)
32 | : freq(1), key(key), value(value), pre(nullptr), next(nullptr) {}
33 | };
34 |
35 | using NodePtr = std::shared_ptr;
36 | int freq_; // 访问频率
37 | NodePtr head_; // 假头结点
38 | NodePtr tail_; // 假尾结点
39 |
40 | public:
41 | explicit FreqList(int n)
42 | : freq_(n)
43 | {
44 | head_ = std::make_shared();
45 | tail_ = std::make_shared();
46 | head_->next = tail_;
47 | tail_->pre = head_;
48 | }
49 |
50 | bool isEmpty() const
51 | {
52 | return head_->next == tail_;
53 | }
54 |
55 | // 提那家结点管理方法
56 | void addNode(NodePtr node)
57 | {
58 | if (!node || !head_ || !tail_)
59 | return;
60 |
61 | node->pre = tail_->pre;
62 | node->next = tail_;
63 | tail_->pre->next = node;
64 | tail_->pre = node;
65 | }
66 |
67 | void removeNode(NodePtr node)
68 | {
69 | if (!node || !head_ || !tail_)
70 | return;
71 | if (!node->pre || !node->next)
72 | return;
73 |
74 | node->pre->next = node->next;
75 | node->next->pre = node->pre;
76 | node->pre = nullptr;
77 | node->next = nullptr;
78 | }
79 |
80 | NodePtr getFirstNode() const { return head_->next; }
81 |
82 | friend class KLfuCache;
83 | //friend class KArcCache;
84 | };
85 |
86 | template
87 | class KLfuCache : public KICachePolicy
88 | {
89 | public:
90 | using Node = typename FreqList::Node;
91 | using NodePtr = std::shared_ptr;
92 | using NodeMap = std::unordered_map;
93 |
94 | KLfuCache(int capacity, int maxAverageNum = 10)
95 | : capacity_(capacity), minFreq_(INT8_MAX), maxAverageNum_(maxAverageNum),
96 | curAverageNum_(0), curTotalNum_(0)
97 | {}
98 |
99 | ~KLfuCache() override = default;
100 |
101 | void put(Key key, Value value) override
102 | {
103 | if (capacity_ == 0)
104 | return;
105 |
106 | std::lock_guard lock(mutex_);
107 | auto it = nodeMap_.find(key);
108 | if (it != nodeMap_.end())
109 | {
110 | // 重置其value值
111 | it->second->value = value;
112 | // 找到了直接调整就好了,不用再去get中再找一遍,但其实影响不大
113 | getInternal(it->second, value);
114 | return;
115 | }
116 |
117 | putInternal(key, value);
118 | }
119 |
120 | // value值为传出参数
121 | bool get(Key key, Value& value) override
122 | {
123 | std::lock_guard lock(mutex_);
124 | auto it = nodeMap_.find(key);
125 | if (it != nodeMap_.end())
126 | {
127 | getInternal(it->second, value);
128 | return true;
129 | }
130 |
131 | return false;
132 | }
133 |
134 | Value get(Key key) override
135 | {
136 | Value value;
137 | get(key, value);
138 | return value;
139 | }
140 |
141 | // 清空缓存,回收资源
142 | void purge()
143 | {
144 | nodeMap_.clear();
145 | freqToFreqList_.clear();
146 | }
147 |
148 | private:
149 | void putInternal(Key key, Value value); // 添加缓存
150 | void getInternal(NodePtr node, Value& value); // 获取缓存
151 |
152 | void kickOut(); // 移除缓存中的过期数据
153 |
154 | void removeFromFreqList(NodePtr node); // 从频率列表中移除节点
155 | void addToFreqList(NodePtr node); // 添加到频率列表
156 |
157 | void addFreqNum(); // 增加平均访问等频率
158 | void decreaseFreqNum(int num); // 减少平均访问等频率
159 | void handleOverMaxAverageNum(); // 处理当前平均访问频率超过上限的情况
160 | void updateMinFreq();
161 |
162 | private:
163 | int capacity_; // 缓存容量
164 | int minFreq_; // 最小访问频次(用于找到最小访问频次结点)
165 | int maxAverageNum_; // 最大平均访问频次
166 | int curAverageNum_; // 当前平均访问频次
167 | int curTotalNum_; // 当前访问所有缓存次数总数
168 | std::mutex mutex_; // 互斥锁
169 | NodeMap nodeMap_; // key 到 缓存节点的映射
170 | std::unordered_map*> freqToFreqList_;// 访问频次到该频次链表的映射
171 | };
172 |
173 | template
174 | void KLfuCache::getInternal(NodePtr node, Value& value)
175 | {
176 | // 找到之后需要将其从低访问频次的链表中删除,并且添加到+1的访问频次链表中,
177 | // 访问频次+1, 然后把value值返回
178 | value = node->value;
179 | // 从原有访问频次的链表中删除节点
180 | removeFromFreqList(node);
181 | node->freq++;
182 | addToFreqList(node);
183 | // 如果当前node的访问频次如果等于minFreq+1,并且其前驱链表为空,则说明
184 | // freqToFreqList_[node->freq - 1]链表因node的迁移已经空了,需要更新最小访问频次
185 | if (node->freq - 1 == minFreq_ && freqToFreqList_[node->freq - 1]->isEmpty())
186 | minFreq_++;
187 |
188 | // 总访问频次和当前平均访问频次都随之增加
189 | addFreqNum();
190 | }
191 |
192 | template
193 | void KLfuCache::putInternal(Key key, Value value)
194 | {
195 | // 如果不在缓存中,则需要判断缓存是否已满
196 | if (nodeMap_.size() == capacity_)
197 | {
198 | // 缓存已满,删除最不常访问的结点,更新当前平均访问频次和总访问频次
199 | kickOut();
200 | }
201 |
202 | // 创建新结点,将新结点添加进入,更新最小访问频次
203 | NodePtr node = std::make_shared(key, value);
204 | nodeMap_[key] = node;
205 | addToFreqList(node);
206 | addFreqNum();
207 | minFreq_ = std::min(minFreq_, 1);
208 | }
209 |
210 | template
211 | void KLfuCache::kickOut()
212 | {
213 | NodePtr node = freqToFreqList_[minFreq_]->getFirstNode();
214 | removeFromFreqList(node);
215 | nodeMap_.erase(node->key);
216 | decreaseFreqNum(node->freq);
217 | }
218 |
219 | template
220 | void KLfuCache::removeFromFreqList(NodePtr node)
221 | {
222 | // 检查结点是否为空
223 | if (!node)
224 | return;
225 |
226 | auto freq = node->freq;
227 | freqToFreqList_[freq]->removeNode(node);
228 | }
229 |
230 | template
231 | void KLfuCache::addToFreqList(NodePtr node)
232 | {
233 | // 检查结点是否为空
234 | if (!node)
235 | return;
236 |
237 | // 添加进入相应的频次链表前需要判断该频次链表是否存在
238 | auto freq = node->freq;
239 | if (freqToFreqList_.find(node->freq) == freqToFreqList_.end())
240 | {
241 | // 不存在则创建
242 | freqToFreqList_[node->freq] = new FreqList(node->freq);
243 | }
244 |
245 | freqToFreqList_[freq]->addNode(node);
246 | }
247 |
248 | template
249 | void KLfuCache::addFreqNum()
250 | {
251 | curTotalNum_++;
252 | if (nodeMap_.empty())
253 | curAverageNum_ = 0;
254 | else
255 | curAverageNum_ = curTotalNum_ / nodeMap_.size();
256 |
257 | if (curAverageNum_ > maxAverageNum_)
258 | {
259 | handleOverMaxAverageNum();
260 | }
261 | }
262 |
263 | template
264 | void KLfuCache::decreaseFreqNum(int num)
265 | {
266 | // 减少平均访问频次和总访问频次
267 | curTotalNum_ -= num;
268 | if (nodeMap_.empty())
269 | curAverageNum_ = 0;
270 | else
271 | curAverageNum_ = curTotalNum_ / nodeMap_.size();
272 | }
273 |
274 | template
275 | void KLfuCache::handleOverMaxAverageNum()
276 | {
277 | if (nodeMap_.empty())
278 | return;
279 |
280 | // 当前平均访问频次已经超过了最大平均访问频次,所有结点的访问频次- (maxAverageNum_ / 2)
281 | for (auto it = nodeMap_.begin(); it != nodeMap_.end(); ++it)
282 | {
283 | // 检查结点是否为空
284 | if (!it->second)
285 | continue;
286 |
287 | NodePtr node = it->second;
288 |
289 | // 先从当前频率列表中移除
290 | removeFromFreqList(node);
291 |
292 | // 减少频率
293 | node->freq -= maxAverageNum_ / 2;
294 | if (node->freq < 1) node->freq = 1;
295 |
296 | // 添加到新的频率列表
297 | addToFreqList(node);
298 | }
299 |
300 | // 更新最小频率
301 | updateMinFreq();
302 | }
303 |
304 | template
305 | void KLfuCache::updateMinFreq()
306 | {
307 | minFreq_ = INT8_MAX;
308 | for (const auto& pair : freqToFreqList_)
309 | {
310 | if (pair.second && !pair.second->isEmpty())
311 | {
312 | minFreq_ = std::min(minFreq_, pair.first);
313 | }
314 | }
315 | if (minFreq_ == INT8_MAX)
316 | minFreq_ = 1;
317 | }
318 |
319 | // 并没有牺牲空间换时间,他是把原有缓存大小进行了分片。
320 | template
321 | class KHashLfuCache
322 | {
323 | public:
324 | KHashLfuCache(size_t capacity, int sliceNum, int maxAverageNum = 10)
325 | : sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency())
326 | , capacity_(capacity)
327 | {
328 | size_t sliceSize = std::ceil(capacity_ / static_cast(sliceNum_)); // 每个lfu分片的容量
329 | for (int i = 0; i < sliceNum_; ++i)
330 | {
331 | lfuSliceCaches_.emplace_back(new KLfuCache(sliceSize, maxAverageNum));
332 | }
333 | }
334 |
335 | void put(Key key, Value value)
336 | {
337 | // 根据key找出对应的lfu分片
338 | size_t sliceIndex = Hash(key) % sliceNum_;
339 | return lfuSliceCaches_[sliceIndex]->put(key, value);
340 | }
341 |
342 | bool get(Key key, Value& value)
343 | {
344 | // 根据key找出对应的lfu分片
345 | size_t sliceIndex = Hash(key) % sliceNum_;
346 | return lfuSliceCaches_[sliceIndex]->get(key, value);
347 | }
348 |
349 | Value get(Key key)
350 | {
351 | Value value;
352 | get(key, value);
353 | return value;
354 | }
355 |
356 | // 清除缓存
357 | void purge()
358 | {
359 | for (auto& lfuSliceCache : lfuSliceCaches_)
360 | {
361 | lfuSliceCache->purge();
362 | }
363 | }
364 |
365 | private:
366 | // 将key计算成对应哈希值
367 | size_t Hash(Key key)
368 | {
369 | std::hash hashFunc;
370 | return hashFunc(key);
371 | }
372 |
373 | private:
374 | size_t capacity_; // 缓存总容量
375 | int sliceNum_; // 缓存分片数量
376 | std::vector>> lfuSliceCaches_; // 缓存lfu分片容器
377 | };
378 |
379 | } // namespace KamaCache
380 |
--------------------------------------------------------------------------------
/include/LogFile.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "FileUtil.h"
3 | #include
4 | #include
5 | #include
6 | /**
7 | * @brief 日志文件管理类
8 | * 负责日志文件的创建、写入、滚动和刷新等操作
9 | * 支持按大小和时间自动滚动日志文件
10 | */
11 | class LogFile
12 | {
13 | public:
14 | /**
15 | * @brief 构造函数
16 | * @param basename 日志文件基本名称
17 | * @param rollsize 日志文件大小达到多少字节时滚动,单位:字节
18 | * @param flushInterval 日志刷新间隔时间,默认3秒
19 | * @param checkEveryN_ 写入checkEveryN_次后检查是否需要滚动,默认1024次
20 | */
21 | LogFile(const std::string &basename,
22 | off_t rollsize,
23 | int flushInterval = 3,
24 | int checkEveryN_ = 1024);
25 | ~LogFile();
26 | /**
27 | * @brief 追加数据到日志文件
28 | * @param data 要写入的数据
29 | * @param len 数据长度
30 | */
31 | void append(const char *data,int len);
32 |
33 | /**
34 | * @brief 强制将缓冲区数据刷新到磁盘
35 | */
36 | void flush();
37 |
38 | /**
39 | * @brief 滚动日志文件
40 | * 当日志文件大小超过rollsize_或时间超过一天时,创建新的日志文件
41 | * @return 是否成功滚动日志文件
42 | */
43 | bool rollFile();
44 |
45 | private:
46 | /**
47 | * @brief 禁用析构函数,使用智能指针管理
48 | */
49 |
50 |
51 | /**
52 | * @brief 生成日志文件名
53 | * @param basename 日志文件基本名称
54 | * @param now 当前时间指针
55 | * @return 完整的日志文件名,格式为:basename.YYYYmmdd-HHMMSS.log
56 | */
57 | static std::string getLogFileName(const std::string &basename, time_t *now);
58 |
59 | /**
60 | * @brief 在已加锁的情况下追加数据
61 | * @param data 要写入的数据
62 | * @param len 数据长度
63 | */
64 | void appendInlock(const char *data, int len);
65 |
66 | const std::string basename_;
67 | const off_t rollsize_; //滚动文件大小
68 | const int flushInterval_; // 冲刷时间限值,默认3s
69 | const int checkEveryN_; // 写数据次数限制,默认1024
70 |
71 | int count_; // 写数据次数计数, 超过限值checkEveryN_时清除, 然后重新计数
72 |
73 | std::mutex mutex_;
74 | time_t startOfPeriod_;// 本次写log周期的起始时间(秒)
75 | time_t lastRoll_;// 上次roll日志文件时间(秒)
76 | time_t lastFlush_; // 上次flush日志文件时间(秒)
77 | std::unique_ptr file_;
78 | const static int kRollPerSeconds_ = 60*60*24;
79 | };
80 |
--------------------------------------------------------------------------------
/include/LogStream.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include "noncopyable.h"
5 | #include "FixedBuffer.h"
6 | class GeneralTemplate : noncopyable
7 | {
8 | public:
9 | GeneralTemplate()
10 | : data_(nullptr),
11 | len_(0)
12 | {}
13 |
14 | explicit GeneralTemplate(const char* data, int len)
15 | : data_(data),
16 | len_(len)
17 | {}
18 |
19 | const char* data_;
20 | int len_;
21 | };
22 | // LogStream类用于管理日志输出流,重载输出流运算符<<,将各种类型的值写入内部缓冲区
23 | class LogStream : noncopyable
24 | {
25 | public:
26 | // 定义一个Buffer类型,使用固定大小的缓冲区
27 | using Buffer = FixedBuffer;
28 |
29 | // 将指定长度的字符数据追加到缓冲区
30 | void append(const char *buffer, int len)
31 | {
32 | buffer_.append(buffer, len); // 调用Buffer的append方法
33 | }
34 |
35 | // 返回当前缓冲区的常量引用
36 | const Buffer &buffer() const
37 | {
38 | return buffer_; // 返回当前的缓冲区
39 | }
40 |
41 | // 重置缓冲区,将当前指针重置到缓冲区的起始位置
42 | void reset_buffer()
43 | {
44 | buffer_.reset(); // 调用Buffer的reset方法
45 | }
46 |
47 | // 重载输出流运算符<<,用于将布尔值写入缓冲区
48 | LogStream &operator<<(bool express);
49 |
50 | // 重载输出流运算符<<,用于将短整型写入缓冲区
51 | LogStream &operator<<(short number);
52 | // 重载输出流运算符<<,用于将无符号短整型写入缓冲区
53 | LogStream &operator<<(unsigned short);
54 | // 重载输出流运算符<<,用于将整型写入缓冲区
55 | LogStream &operator<<(int);
56 | // 重载输出流运算符<<,用于将无符号整型写入缓冲区
57 | LogStream &operator<<(unsigned int);
58 | // 重载输出流运算符<<,用于将长整型写入缓冲区
59 | LogStream &operator<<(long);
60 | // 重载输出流运算符<<,用于将无符号长整型写入缓冲区
61 | LogStream &operator<<(unsigned long);
62 | // 重载输出流运算符<<,用于将长长整型写入缓冲区
63 | LogStream &operator<<(long long);
64 | // 重载输出流运算符<<,用于将无符号长长整型写入缓冲区
65 | LogStream &operator<<(unsigned long long);
66 |
67 | // 重载输出流运算符<<,用于将浮点数写入缓冲区
68 | LogStream &operator<<(float number);
69 | // 重载输出流运算符<<,用于将双精度浮点数写入缓冲区
70 | LogStream &operator<<(double);
71 |
72 | // 重载输出流运算符<<,用于将字符写入缓冲区
73 | LogStream &operator<<(char str);
74 | // 重载输出流运算符<<,用于将C风格字符串写入缓冲区
75 | LogStream &operator<<(const char *);
76 | // 重载输出流运算符<<,用于将无符号字符指针写入缓冲区
77 | LogStream &operator<<(const unsigned char *);
78 | // 重载输出流运算符<<,用于将std::string对象写入缓冲区
79 | LogStream &operator<<(const std::string &);
80 | // (const char*, int)的重载
81 | LogStream& operator<<(const GeneralTemplate& g);
82 | private:
83 | // 定义最大数字大小常量
84 | static constexpr int kMaxNumberSize = 32;
85 |
86 | // 对于整型需要特殊的处理,模板函数用于格式化整型
87 | template
88 | void formatInteger(T num);
89 |
90 | // 内部缓冲区对象
91 | Buffer buffer_;
92 | };
93 |
--------------------------------------------------------------------------------
/include/Logger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "LogStream.h"
9 | #include
10 | #include "Timestamp.h"
11 |
12 | #define OPEN_LOGGING
13 |
14 | // SourceFile的作用是提取文件名
15 | class SourceFile
16 | {
17 | public:
18 | explicit SourceFile(const char* filename)
19 | : data_(filename)
20 | {
21 | /**
22 | * 找出data中出现/最后一次的位置,从而获取具体的文件名
23 | * 2022/10/26/test.log
24 | */
25 | const char* slash = strrchr(filename, '/');
26 | if (slash)
27 | {
28 | data_ = slash + 1;
29 | }
30 | size_ = static_cast(strlen(data_));
31 | }
32 |
33 | const char* data_;
34 | int size_;
35 | };
36 | class Logger
37 | {
38 | public:
39 | enum LogLevel
40 | {
41 | TRACE,
42 | DEBUG,
43 | INFO,
44 | WARN,
45 | ERROR,
46 | FATAL,
47 | LEVEL_COUNT,
48 | };
49 | Logger(const char *filename, int line, LogLevel level);
50 | ~Logger();
51 | // 流是会改变的
52 | LogStream& stream() { return impl_.stream_; }
53 |
54 |
55 | // 输出函数和刷新缓冲区函数
56 | using OutputFunc = std::function;
57 | using FlushFunc = std::function;
58 | static void setOutput(OutputFunc);
59 | static void setFlush(FlushFunc);
60 |
61 | private:
62 | class Impl
63 | {
64 | public:
65 | using LogLevel=Logger::LogLevel;
66 | Impl(LogLevel level,int savedErrno,const char *filename, int line);
67 | void formatTime();
68 | void finish(); // 添加一条log消息的后缀
69 |
70 | Timestamp time_;
71 | LogStream stream_;
72 | LogLevel level_;
73 | int line_;
74 | SourceFile basename_;
75 | };
76 |
77 | private:
78 | Impl impl_;
79 | };
80 |
81 | // 获取errno信息
82 | const char* getErrnoMsg(int savedErrno);
83 | /**
84 | * 当日志等级小于对应等级才会输出
85 | * 比如设置等级为FATAL,则logLevel等级大于DEBUG和INFO,DEBUG和INFO等级的日志就不会输出
86 | */
87 | #ifdef OPEN_LOGGING
88 | #define LOG_DEBUG Logger(__FILE__, __LINE__, Logger::DEBUG).stream()
89 | #define LOG_INFO Logger(__FILE__, __LINE__, Logger::INFO).stream()
90 | #define LOG_WARN Logger(__FILE__, __LINE__, Logger::WARN).stream()
91 | #define LOG_ERROR Logger(__FILE__, __LINE__, Logger::ERROR).stream()
92 | #define LOG_FATAL Logger(__FILE__, __LINE__, Logger::FATAL).stream()
93 | #else
94 | #define LOG(level) LogStream()
95 | #endif
--------------------------------------------------------------------------------
/include/Poller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "noncopyable.h"
7 | #include "Timestamp.h"
8 |
9 | class Channel;
10 | class EventLoop;
11 |
12 | // muduo库中多路事件分发器的核心IO复用模块
13 | class Poller
14 | {
15 | public:
16 | using ChannelList = std::vector;
17 |
18 | Poller(EventLoop *loop);
19 | virtual ~Poller() = default;
20 |
21 | // 给所有IO复用保留统一的接口
22 | virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;
23 | virtual void updateChannel(Channel *channel) = 0;
24 | virtual void removeChannel(Channel *channel) = 0;
25 |
26 | // 判断参数channel是否在当前的Poller当中
27 | bool hasChannel(Channel *channel) const;
28 |
29 | // EventLoop可以通过该接口获取默认的IO复用的具体实现
30 | static Poller *newDefaultPoller(EventLoop *loop);
31 |
32 | protected:
33 | // map的key:sockfd value:sockfd所属的channel通道类型
34 | using ChannelMap = std::unordered_map;
35 | ChannelMap channels_;
36 |
37 | private:
38 | EventLoop *ownerLoop_; // 定义Poller所属的事件循环EventLoop
39 | };
--------------------------------------------------------------------------------
/include/Socket.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "noncopyable.h"
4 |
5 | class InetAddress;
6 |
7 | // 封装socket fd
8 | class Socket : noncopyable
9 | {
10 | public:
11 | explicit Socket(int sockfd)
12 | : sockfd_(sockfd)
13 | {
14 | }
15 | ~Socket();
16 |
17 | int fd() const { return sockfd_; }
18 | void bindAddress(const InetAddress &localaddr);
19 | void listen();
20 | int accept(InetAddress *peeraddr);
21 |
22 | void shutdownWrite();
23 |
24 | void setTcpNoDelay(bool on);
25 | void setReuseAddr(bool on);
26 | void setReusePort(bool on);
27 | void setKeepAlive(bool on);
28 |
29 | private:
30 | const int sockfd_;
31 | };
32 |
--------------------------------------------------------------------------------
/include/TcpConnection.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "noncopyable.h"
8 | #include "InetAddress.h"
9 | #include "Callbacks.h"
10 | #include "Buffer.h"
11 | #include "Timestamp.h"
12 |
13 | class Channel;
14 | class EventLoop;
15 | class Socket;
16 |
17 | /**
18 | * TcpServer => Acceptor => 有一个新用户连接,通过accept函数拿到connfd
19 | * => TcpConnection设置回调 => 设置到Channel => Poller => Channel回调
20 | **/
21 |
22 | class TcpConnection : noncopyable, public std::enable_shared_from_this
23 | {
24 | public:
25 | TcpConnection(EventLoop *loop,
26 | const std::string &nameArg,
27 | int sockfd,
28 | const InetAddress &localAddr,
29 | const InetAddress &peerAddr);
30 | ~TcpConnection();
31 |
32 | EventLoop *getLoop() const { return loop_; }
33 | const std::string &name() const { return name_; }
34 | const InetAddress &localAddress() const { return localAddr_; }
35 | const InetAddress &peerAddress() const { return peerAddr_; }
36 |
37 | bool connected() const { return state_ == kConnected; }
38 |
39 | // 发送数据
40 | void send(const std::string &buf);
41 | void sendFile(int fileDescriptor, off_t offset, size_t count);
42 |
43 | // 关闭半连接
44 | void shutdown();
45 |
46 | void setConnectionCallback(const ConnectionCallback &cb)
47 | { connectionCallback_ = cb; }
48 | void setMessageCallback(const MessageCallback &cb)
49 | { messageCallback_ = cb; }
50 | void setWriteCompleteCallback(const WriteCompleteCallback &cb)
51 | { writeCompleteCallback_ = cb; }
52 | void setCloseCallback(const CloseCallback &cb)
53 | { closeCallback_ = cb; }
54 | void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark)
55 | { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
56 |
57 | // 连接建立
58 | void connectEstablished();
59 | // 连接销毁
60 | void connectDestroyed();
61 |
62 | private:
63 | enum StateE
64 | {
65 | kDisconnected, // 已经断开连接
66 | kConnecting, // 正在连接
67 | kConnected, // 已连接
68 | kDisconnecting // 正在断开连接
69 | };
70 | void setState(StateE state) { state_ = state; }
71 |
72 | void handleRead(Timestamp receiveTime);
73 | void handleWrite();//处理写事件
74 | void handleClose();
75 | void handleError();
76 |
77 | void sendInLoop(const void *data, size_t len);
78 | void shutdownInLoop();
79 | void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);
80 | EventLoop *loop_; // 这里是baseloop还是subloop由TcpServer中创建的线程数决定 若为多Reactor 该loop_指向subloop 若为单Reactor 该loop_指向baseloop
81 | const std::string name_;
82 | std::atomic_int state_;
83 | bool reading_;//连接是否在监听读事件
84 |
85 | // Socket Channel 这里和Acceptor类似 Acceptor => mainloop TcpConnection => subloop
86 | std::unique_ptr socket_;
87 | std::unique_ptr channel_;
88 |
89 | const InetAddress localAddr_;
90 | const InetAddress peerAddr_;
91 |
92 | // 这些回调TcpServer也有 用户通过写入TcpServer注册 TcpServer再将注册的回调传递给TcpConnection TcpConnection再将回调注册到Channel中
93 | ConnectionCallback connectionCallback_; // 有新连接时的回调
94 | MessageCallback messageCallback_; // 有读写消息时的回调
95 | WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调
96 | HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调
97 | CloseCallback closeCallback_; // 关闭连接的回调
98 | size_t highWaterMark_; // 高水位阈值
99 |
100 | // 数据缓冲区
101 | Buffer inputBuffer_; // 接收数据的缓冲区
102 | Buffer outputBuffer_; // 发送数据的缓冲区 用户send向outputBuffer_发
103 | };
104 |
--------------------------------------------------------------------------------
/include/TcpServer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /**
4 | * 用户使用muduo编写服务器程序
5 | **/
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "EventLoop.h"
14 | #include "Acceptor.h"
15 | #include "InetAddress.h"
16 | #include "noncopyable.h"
17 | #include "EventLoopThreadPool.h"
18 | #include "Callbacks.h"
19 | #include "TcpConnection.h"
20 | #include "Buffer.h"
21 |
22 | // 对外的服务器编程使用的类
23 | class TcpServer
24 | {
25 | public:
26 | using ThreadInitCallback = std::function;
27 |
28 | enum Option
29 | {
30 | kNoReusePort,//不允许重用本地端口
31 | kReusePort,//允许重用本地端口
32 | };
33 |
34 | TcpServer(EventLoop *loop,
35 | const InetAddress &listenAddr,
36 | const std::string &nameArg,
37 | Option option = kNoReusePort);
38 | ~TcpServer();
39 |
40 | void setThreadInitCallback(const ThreadInitCallback &cb) { threadInitCallback_ = cb; }
41 | void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }
42 | void setMessageCallback(const MessageCallback &cb) { messageCallback_ = cb; }
43 | void setWriteCompleteCallback(const WriteCompleteCallback &cb) { writeCompleteCallback_ = cb; }
44 |
45 | // 设置底层subloop的个数
46 | void setThreadNum(int numThreads);
47 | /**
48 | * 如果没有监听, 就启动服务器(监听).
49 | * 多次调用没有副作用.
50 | * 线程安全.
51 | */
52 | void start();
53 |
54 | private:
55 | void newConnection(int sockfd, const InetAddress &peerAddr);
56 | void removeConnection(const TcpConnectionPtr &conn);
57 | void removeConnectionInLoop(const TcpConnectionPtr &conn);
58 |
59 | using ConnectionMap = std::unordered_map;
60 |
61 | EventLoop *loop_; // baseloop 用户自定义的loop
62 |
63 | const std::string ipPort_;
64 | const std::string name_;
65 |
66 | std::unique_ptr acceptor_; // 运行在mainloop 任务就是监听新连接事件
67 |
68 | std::shared_ptr threadPool_; // one loop per thread
69 |
70 | ConnectionCallback connectionCallback_; //有新连接时的回调
71 | MessageCallback messageCallback_; // 有读写事件发生时的回调
72 | WriteCompleteCallback writeCompleteCallback_; // 消息发送完成后的回调
73 |
74 | ThreadInitCallback threadInitCallback_; // loop线程初始化的回调
75 | int numThreads_;//线程池中线程的数量。
76 | std::atomic_int started_;
77 | int nextConnId_;
78 | ConnectionMap connections_; // 保存所有的连接
79 | };
--------------------------------------------------------------------------------
/include/Thread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "noncopyable.h"
11 |
12 | class Thread : noncopyable
13 | {
14 | public:
15 | using ThreadFunc = std::function;
16 |
17 | explicit Thread(ThreadFunc, const std::string &name = std::string());
18 | ~Thread();
19 |
20 | void start();
21 | void join();
22 |
23 | bool started() { return started_; }
24 | pid_t tid() const { return tid_; }
25 | const std::string &name() const { return name_; }
26 |
27 | static int numCreated() { return numCreated_; }
28 |
29 | private:
30 | void setDefaultName();
31 |
32 | bool started_;
33 | bool joined_;
34 | std::shared_ptr thread_;
35 | pid_t tid_; // 在线程创建时再绑定
36 | ThreadFunc func_; // 线程回调函数
37 | std::string name_;
38 | static std::atomic_int numCreated_;
39 | };
--------------------------------------------------------------------------------
/include/Timer.h:
--------------------------------------------------------------------------------
1 | #ifndef TIMER_H
2 | #define TIMER_H
3 |
4 | #include "noncopyable.h"
5 | #include "Timestamp.h"
6 | #include
7 |
8 | /**
9 | * Timer用于描述一个定时器
10 | * 定时器回调函数,下一次超时时刻,重复定时器的时间间隔等
11 | */
12 | class Timer : noncopyable
13 | {
14 | public:
15 | using TimerCallback = std::function;
16 |
17 | Timer(TimerCallback cb, Timestamp when, double interval)
18 | : callback_(move(cb)),
19 | expiration_(when),
20 | interval_(interval),
21 | repeat_(interval > 0.0) // 一次性定时器设置为0
22 | {
23 | }
24 |
25 | void run() const
26 | {
27 | callback_();
28 | }
29 |
30 | Timestamp expiration() const { return expiration_; }
31 | bool repeat() const { return repeat_; }
32 |
33 | // 重启定时器(如果是非重复事件则到期时间置为0)
34 | void restart(Timestamp now);
35 |
36 | private:
37 | const TimerCallback callback_; // 定时器回调函数
38 | Timestamp expiration_; // 下一次的超时时刻
39 | const double interval_; // 超时时间间隔,如果是一次性定时器,该值为0
40 | const bool repeat_; // 是否重复(false 表示是一次性定时器)
41 | };
42 |
43 | #endif // TIMER_H
--------------------------------------------------------------------------------
/include/TimerQueue.h:
--------------------------------------------------------------------------------
1 | #ifndef TIMER_QUEUE_H
2 | #define TIMER_QUEUE_H
3 |
4 | #include "Timestamp.h"
5 | #include "Channel.h"
6 |
7 | #include
8 | #include
9 |
10 | class EventLoop;
11 | class Timer;
12 |
13 | class TimerQueue
14 | {
15 | public:
16 | using TimerCallback = std::function;
17 |
18 | explicit TimerQueue(EventLoop* loop);
19 | ~TimerQueue();
20 |
21 | // 插入定时器(回调函数,到期时间,是否重复)
22 | void addTimer(TimerCallback cb,
23 | Timestamp when,
24 | double interval);
25 |
26 | private:
27 | using Entry = std::pair; // 以时间戳作为键值获取定时器
28 | using TimerList = std::set; // 底层使用红黑树管理,自动按照时间戳进行排序
29 |
30 | // 在本loop中添加定时器
31 | // 线程安全
32 | void addTimerInLoop(Timer* timer);
33 |
34 | // 定时器读事件触发的函数
35 | void handleRead();
36 |
37 | // 重新设置timerfd_
38 | void resetTimerfd(int timerfd_, Timestamp expiration);
39 |
40 | // 移除所有已到期的定时器
41 | // 1.获取到期的定时器
42 | // 2.重置这些定时器(销毁或者重复定时任务)
43 | std::vector getExpired(Timestamp now);
44 | void reset(const std::vector& expired, Timestamp now);
45 |
46 | // 插入定时器的内部方法
47 | bool insert(Timer* timer);
48 |
49 | EventLoop* loop_; // 所属的EventLoop
50 | const int timerfd_; // timerfd是Linux提供的定时器接口
51 | Channel timerfdChannel_; // 封装timerfd_文件描述符
52 | // Timer list sorted by expiration
53 | TimerList timers_; // 定时器队列(内部实现是红黑树)
54 |
55 | bool callingExpiredTimers_; // 标明正在获取超时定时器
56 | };
57 |
58 | #endif // TIMER_QUEUE_H
--------------------------------------------------------------------------------
/include/Timestamp.h:
--------------------------------------------------------------------------------
1 | #ifndef TIME_STAMP_H
2 | #define TIME_STAMP_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class Timestamp
9 | {
10 | public:
11 | Timestamp()
12 | : microSecondsSinceEpoch_(0)
13 | {
14 | }
15 |
16 | explicit Timestamp(int64_t microSecondsSinceEpoch)
17 | : microSecondsSinceEpoch_(microSecondsSinceEpoch)
18 | {
19 | }
20 |
21 | // 获取当前时间戳
22 | static Timestamp now();
23 | std::string toString()const;
24 |
25 | //格式, "%4d年%02d月%02d日 星期%d %02d:%02d:%02d.%06d",时分秒.微秒
26 | std::string toFormattedString(bool showMicroseconds = false) const;
27 |
28 | //返回当前时间戳的微妙
29 | int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }
30 | //返回当前时间戳的秒数
31 | time_t secondsSinceEpoch() const
32 | {
33 | return static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);
34 | }
35 |
36 | // 失效的时间戳,返回一个值为0的Timestamp
37 | static Timestamp invalid()
38 | {
39 | return Timestamp();
40 | }
41 |
42 | // 1秒=1000*1000微妙
43 | static const int kMicroSecondsPerSecond = 1000 * 1000;
44 |
45 | private:
46 | // 表示时间戳的微秒数(自epoch开始经历的微妙数)
47 | int64_t microSecondsSinceEpoch_;
48 | };
49 |
50 | /**
51 | * 定时器需要比较时间戳,因此需要重载运算符
52 | */
53 | inline bool operator<(Timestamp lhs, Timestamp rhs)
54 | {
55 | return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();
56 | }
57 |
58 | inline bool operator==(Timestamp lhs, Timestamp rhs)
59 | {
60 | return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();
61 | }
62 |
63 | // 如果是重复定时任务就会对此时间戳进行增加。
64 | inline Timestamp addTime(Timestamp timestamp, double seconds)
65 | {
66 | // 将延时的秒数转换为微妙
67 | int64_t delta = static_cast(seconds * Timestamp::kMicroSecondsPerSecond);
68 | // 返回新增时后的时间戳
69 | return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
70 | }
71 |
72 | #endif // TIME_STAMP_H
--------------------------------------------------------------------------------
/include/memoryPool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace memoryPool
10 | {
11 | #define MEMORY_POOL_NUM 64
12 | #define SLOT_BASE_SIZE 8
13 | #define MAX_SLOT_SIZE 512
14 |
15 |
16 | /* 具体内存池的槽大小没法确定,因为每个内存池的槽大小不同(8的倍数)
17 | 所以这个槽结构体的sizeof 不是实际的槽大小 */
18 | struct Slot
19 | {
20 | Slot* next;
21 | };
22 |
23 | class MemoryPool
24 | {
25 | public:
26 | MemoryPool(size_t BlockSize = 4096);
27 | ~MemoryPool();
28 |
29 | void init(size_t);
30 |
31 | void* allocate();
32 | void deallocate(void*);
33 | private:
34 | void allocateNewBlock();
35 | size_t padPointer(char* p, size_t align);
36 |
37 | private:
38 | int BlockSize_; // 内存块大小
39 | int SlotSize_; // 槽大小
40 | Slot* firstBlock_; // 指向内存池管理的首个实际内存块
41 | Slot* curSlot_; // 指向当前未被使用过的槽
42 | Slot* freeList_; // 指向空闲的槽(被使用过后又被释放的槽)
43 | Slot* lastSlot_; // 作为当前内存块中最后能够存放元素的位置标识(超过该位置需申请新的内存块)
44 | std::mutex mutexForFreeList_; // 保证freeList_在多线程中操作的原子性
45 | std::mutex mutexForBlock_; // 保证多线程情况下避免不必要的重复开辟内存导致的浪费行为
46 | };
47 |
48 |
49 | class HashBucket
50 | {
51 | public:
52 | static void initMemoryPool();
53 | static MemoryPool& getMemoryPool(int index);
54 |
55 | static void* useMemory(size_t size)
56 | {
57 | if (size <= 0)
58 | return nullptr;
59 | if (size > MAX_SLOT_SIZE) // 大于512字节的内存,则使用new
60 | return operator new(size);
61 |
62 | // 相当于size / 8 向上取整(因为分配内存只能大不能小
63 | return getMemoryPool(((size + 7) / SLOT_BASE_SIZE) - 1).allocate();
64 | }
65 |
66 | static void freeMemory(void* ptr, size_t size)
67 | {
68 | if (!ptr)
69 | return;
70 | if (size > MAX_SLOT_SIZE)
71 | {
72 | operator delete(ptr);
73 | return;
74 | }
75 |
76 | getMemoryPool(((size + 7) / SLOT_BASE_SIZE) - 1).deallocate(ptr);
77 | }
78 |
79 | template
80 | friend T* newElement(Args&&... args);
81 |
82 | template
83 | friend void deleteElement(T* p);
84 | };
85 |
86 | template
87 | T* newElement(Args&&... args)
88 | {
89 | T* p = nullptr;
90 | // 根据元素大小选取合适的内存池分配内存
91 | if ((p = reinterpret_cast(HashBucket::useMemory(sizeof(T)))) != nullptr)
92 | // 在分配的内存上构造对象
93 | new(p) T(std::forward(args)...);
94 |
95 | return p;
96 | }
97 |
98 | template
99 | void deleteElement(T* p)
100 | {
101 | // 对象析构
102 | if (p)
103 | {
104 | p->~T();
105 | // 内存回收
106 | HashBucket::freeMemory(reinterpret_cast(p), sizeof(T));
107 | }
108 | }
109 |
110 | } // namespace memoryPool
--------------------------------------------------------------------------------
/include/noncopyable.h:
--------------------------------------------------------------------------------
1 | #pragma once // 防止头文件重复包含
2 |
3 | /**
4 | * noncopyable被继承后 派生类对象可正常构造和析构 但派生类对象无法进行拷贝构造和赋值构造
5 | **/
6 | class noncopyable
7 | {
8 | public:
9 | noncopyable(const noncopyable &) = delete;
10 | noncopyable &operator=(const noncopyable &) = delete;
11 | // void operator=(const noncopyable &) = delete; // muduo将返回值变为void 这其实无可厚非
12 | protected:
13 | noncopyable() = default;
14 | ~noncopyable() = default;
15 | };
--------------------------------------------------------------------------------
/lib/liblog_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/lib/liblog_lib.so
--------------------------------------------------------------------------------
/lib/libmemory_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/lib/libmemory_lib.so
--------------------------------------------------------------------------------
/lib/libsrc_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youngyangyang04/kama-webserver/a04ee9a777fed121ea289c339811065e0ce96188/lib/libsrc_lib.so
--------------------------------------------------------------------------------
/log/AsyncLogging.cc:
--------------------------------------------------------------------------------
1 | #include "AsyncLogging.h"
2 | #include
3 | AsyncLogging::AsyncLogging(const std::string &basename, off_t rollSize, int flushInterval)
4 | :
5 | flushInterval_(flushInterval),
6 | running_(false),
7 | basename_(basename),
8 | rollSize_(rollSize),
9 | thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"),
10 | mutex_(),
11 | cond_(),
12 | currentBuffer_(new LargeBuffer),
13 | nextBuffer_(new LargeBuffer),
14 | buffers_()
15 | {
16 | currentBuffer_->bzero();
17 | nextBuffer_->bzero();
18 | buffers_.reserve(16); // 只维持队列长度2~16.
19 | }
20 | // 调用此函数解决前端把LOG_XXX<<"..."传递给后端,后端再将日志消息写入日志文件
21 | void AsyncLogging::append(const char *logline, int len)
22 | {
23 | std::lock_guard lg(mutex_);
24 | // 缓冲区剩余的空间足够写入
25 | if (currentBuffer_->avail() > static_cast(len))
26 | {
27 | currentBuffer_->append(logline, len);
28 | }
29 | else
30 | {
31 | buffers_.push_back(std::move(currentBuffer_));
32 |
33 | if (nextBuffer_)
34 | {
35 | currentBuffer_ = std::move(nextBuffer_);
36 | }
37 | else
38 | {
39 | currentBuffer_.reset(new LargeBuffer);
40 | }
41 | currentBuffer_->append(logline, len);
42 | // 唤醒后端线程写入磁盘
43 | cond_.notify_one();
44 | }
45 | }
46 |
47 | void AsyncLogging::threadFunc()
48 | {
49 | // output写入磁盘接口
50 | LogFile output(basename_, rollSize_);
51 | BufferPtr newbuffer1(new LargeBuffer); // 生成新buffer替换currentbuffer_
52 | BufferPtr newbuffer2(new LargeBuffer); // 生成新buffer2替换newBuffer_,其目的是为了防止后端缓冲区全满前端无法写入
53 | newbuffer1->bzero();
54 | newbuffer2->bzero();
55 | // 缓冲区数组置为16个,用于和前端缓冲区数组进行交换
56 | BufferVector buffersToWrite;
57 | buffersToWrite.reserve(16);
58 | while (running_)
59 | {
60 | {
61 | // 互斥锁保护这样就保证了其他前端线程无法向前端buffer写入数据
62 | std::unique_lock lg(mutex_);
63 | if (buffers_.empty())
64 | {
65 | cond_.wait_for(lg, std::chrono::seconds(3));
66 | }
67 | buffers_.push_back(std::move(currentBuffer_));
68 | currentBuffer_ = std::move(newbuffer1);
69 | if (!nextBuffer_)
70 | {
71 | nextBuffer_ = std::move(newbuffer2);
72 | }
73 | buffersToWrite.swap(buffers_);
74 | }
75 | // 从待写缓冲区取出数据通过LogFile提供的接口写入到磁盘中
76 | for (auto &buffer : buffersToWrite)
77 | {
78 | output.append(buffer->data(), buffer->length());
79 | }
80 |
81 | if (buffersToWrite.size() > 2)
82 | {
83 | buffersToWrite.resize(2);
84 | }
85 |
86 | if (!newbuffer1)
87 | {
88 | newbuffer1 = std::move(buffersToWrite.back());
89 | buffersToWrite.pop_back();
90 | newbuffer1->reset();
91 | }
92 | if (!newbuffer2)
93 | {
94 | newbuffer2 = std::move(buffersToWrite.back());
95 | buffersToWrite.pop_back();
96 | newbuffer2->reset();
97 | }
98 | buffersToWrite.clear(); // 清空后端缓冲队列
99 | output.flush(); // 清空文件夹缓冲区
100 | }
101 | output.flush(); // 确保一定清空。
102 | }
--------------------------------------------------------------------------------
/log/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #获取当前目录下的所有源文件项目
2 | file(GLOB LOG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 |
4 | #创建静态库或共享库
5 | add_library(log_lib SHARED ${LOG_FILE})
--------------------------------------------------------------------------------
/log/CurrentThread.cc:
--------------------------------------------------------------------------------
1 | #include "CurrentThread.h"
2 |
3 | namespace CurrentThread
4 | {
5 | thread_local int t_cachedTid = 0; // 在源文件中定义线程局部变量
6 | void cacheTid()
7 | {
8 | if (t_cachedTid == 0)
9 | {
10 | t_cachedTid = static_cast(::syscall(SYS_gettid)); // Ensure syscall and SYS_gettid are defined
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/log/FileUtil.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include "FileUtil.h"
3 |
4 | FileUtil::FileUtil(std::string &file_name) : file_(::fopen(file_name.c_str(), "ae")),
5 | writtenBytes_(0)
6 | {
7 | // 将file_缓冲区设置为本地缓冲降低io次数。
8 | ::setbuffer(file_, buffer_, sizeof(buffer_));
9 | }
10 | FileUtil::~FileUtil()
11 | {
12 | if (file_)
13 | {
14 | ::fclose(file_);
15 | }
16 | }
17 | // 向文件写入数据
18 | void FileUtil::append(const char *data, size_t len)
19 | {
20 | size_t writen = 0;
21 | while (writen != len)
22 | {
23 | size_t remain = len - writen;
24 | size_t n = write(data + writen, remain);
25 | if (n != remain)
26 | {
27 | // 错误判断
28 | int err = ferror(file_);
29 | if (err)
30 | {
31 | fprintf(stderr, "AppendFile::append() failed %s\n", strerror(err));
32 | clearerr(file_); // 清除文件指针的错误标志
33 | break;
34 | }
35 | }
36 | writen += n;
37 | }
38 | writtenBytes_ += writen;
39 | }
40 |
41 | void FileUtil::flush()
42 | {
43 | ::fflush(file_);
44 | }
45 | // 真正向文件写入数据
46 | size_t FileUtil::write(const char *data, size_t len)
47 | {
48 | // 没用选择线程安全的fwrite()为性能考虑。
49 | return ::fwrite_unlocked(data, 1, len, file_);
50 | }
--------------------------------------------------------------------------------
/log/LogFile.cc:
--------------------------------------------------------------------------------
1 | #include "LogFile.h"
2 | LogFile::LogFile(const std::string &basename,
3 | off_t rollsize,
4 | int flushInterval,
5 | int checkEveryN ) : basename_(basename),
6 | rollsize_(rollsize),
7 | flushInterval_(flushInterval),
8 | checkEveryN_(checkEveryN),
9 | startOfPeriod_(0),
10 | lastRoll_(0),
11 | lastFlush_(0)
12 | {
13 | // 重新启动时,可能没有log文件,因此在构建logFile对象,直接调用rollfile()创建一个新的log文件
14 | rollFile();
15 | }
16 | LogFile::~LogFile() = default;
17 | void LogFile::append(const char *data, int len)
18 | {
19 | std::lock_guard lg(mutex_);
20 | appendInlock(data, len);
21 | }
22 | void LogFile::flush()
23 | {
24 | file_->flush();
25 | }
26 | // 滚动日志
27 | bool LogFile::rollFile()
28 | {
29 | time_t now = 0;
30 | std::string filename = getLogFileName(basename_, &now);
31 | time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;
32 | if (now > lastRoll_)
33 | {
34 | lastFlush_ = now;
35 | lastRoll_ = now;
36 | startOfPeriod_ = start;
37 | // 让file_指向一个名为filename的文件,相当于新建了一个文件,但是rollfile一次就会创建一共file对象去将数据写到日志文件中
38 | file_.reset(new FileUtil(filename));
39 | return true;
40 | }
41 | return false;
42 | }
43 | // 日志格式basename+now+".log"
44 | std::string LogFile::getLogFileName(const std::string &basename, time_t *now)
45 | {
46 | std::string filename;
47 | filename.reserve(basename.size() + 64);
48 | filename = basename;
49 |
50 | char timebuf[32];
51 | struct tm tm;
52 | *now = time(NULL); // 获取当前时间
53 | localtime_r(now, &tm);
54 | strftime(timebuf, sizeof(timebuf), ".%Y%m%d-%H%M%S", &tm);
55 |
56 | filename += timebuf;
57 | filename += ".log";
58 | return filename;
59 | }
60 | void LogFile::appendInlock(const char *data, int len)
61 | {
62 | file_->append(data, len);
63 |
64 | time_t now = time(NULL); // 当前时间
65 | ++count_;
66 |
67 | // 1. 判断是否需要滚动日志
68 | if (file_->writtenBytes() > rollsize_)
69 | {
70 | rollFile();
71 | }
72 | else if (count_ >= checkEveryN_) // 达到写入次数阈值后,进行检查
73 | {
74 | count_ = 0;
75 |
76 | // 基于时间周期滚动日志
77 | time_t thisPeriod = now / kRollPerSeconds_ * kRollPerSeconds_;
78 | if (thisPeriod != startOfPeriod_)
79 | {
80 | rollFile();
81 | }
82 | }
83 |
84 | // 2. 判断是否需要刷新日志(独立的刷新逻辑)
85 | if (now - lastFlush_ > flushInterval_)
86 | {
87 | lastFlush_ = now;
88 | file_->flush();
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/log/LogStream.cc:
--------------------------------------------------------------------------------
1 | #include "LogStream.h"
2 | #include
3 |
4 | static const char digits[] = "9876543210123456789";
5 |
6 | template
7 | void LogStream::formatInteger(T num)
8 | {
9 | if (buffer_.avail() >= kMaxNumberSize)
10 | {
11 | char *start = buffer_.current();
12 | char *cur = start;
13 | static const char *zero = digits + 9;
14 | bool negative = (num < 0); // 判断num是否为负数
15 | do
16 | {
17 | int remainder = static_cast(num % 10);
18 | (*cur++) = zero[remainder];
19 | num /= 10;
20 | } while (num != 0);
21 | if (negative)
22 | {
23 | *cur++ = '-';
24 | }
25 | *cur = '\0';
26 | std::reverse(start, cur);
27 | int length = static_cast(cur - start);
28 | buffer_.add(length);
29 | }
30 | }
31 | // 重载输出流运算符<<,用于将布尔值写入缓冲区
32 | LogStream &LogStream::operator<<(bool express) {
33 | buffer_.append(express ? "true" : "false", express ? 4 : 5);
34 | return *this;
35 | }
36 |
37 | // 重载输出流运算符<<,用于将短整型写入缓冲区
38 | LogStream &LogStream::operator<<(short number) {
39 | formatInteger(number);
40 | return *this;
41 | }
42 |
43 | // 重载输出流运算符<<,用于将无符号短整型写入缓冲区
44 | LogStream &LogStream::operator<<(unsigned short number) {
45 | formatInteger(number);
46 | return *this;
47 | }
48 |
49 | // 重载输出流运算符<<,用于将整型写入缓冲区
50 | LogStream &LogStream::operator<<(int number) {
51 | formatInteger(number);
52 | return *this;
53 | }
54 |
55 | // 重载输出流运算符<<,用于将无符号整型写入缓冲区
56 | LogStream &LogStream::operator<<(unsigned int number) {
57 | formatInteger(number);
58 | return *this;
59 | }
60 |
61 | // 重载输出流运算符<<,用于将长整型写入缓冲区
62 | LogStream &LogStream::operator<<(long number) {
63 | formatInteger(number);
64 | return *this;
65 | }
66 |
67 | // 重载输出流运算符<<,用于将无符号长整型写入缓冲区
68 | LogStream &LogStream::operator<<(unsigned long number) {
69 | formatInteger(number);
70 | return *this;
71 | }
72 |
73 | // 重载输出流运算符<<,用于将长长整型写入缓冲区
74 | LogStream &LogStream::operator<<(long long number) {
75 | formatInteger(number);
76 | return *this;
77 | }
78 |
79 | // 重载输出流运算符<<,用于将无符号长长整型写入缓冲区
80 | LogStream &LogStream::operator<<(unsigned long long number) {
81 | formatInteger(number);
82 | return *this;
83 | }
84 |
85 | // 重载输出流运算符<<,用于将浮点数写入缓冲区
86 | LogStream &LogStream::operator<<(float number) {
87 | *this<(number);
88 | return *this;
89 | }
90 |
91 | // 重载输出流运算符<<,用于将双精度浮点数写入缓冲区
92 | LogStream &LogStream::operator<<(double number) {
93 | char buffer[32];
94 | snprintf(buffer, sizeof(buffer), "%.12g", number);
95 | buffer_.append(buffer, strlen(buffer));
96 | return *this;
97 | }
98 |
99 | // 重载输出流运算符<<,用于将字符写入缓冲区
100 | LogStream &LogStream::operator<<(char str) {
101 | buffer_.append(&str, 1);
102 | return *this;
103 | }
104 |
105 | // 重载输出流运算符<<,用于将C风格字符串写入缓冲区
106 | LogStream &LogStream::operator<<(const char *str) {
107 | buffer_.append(str, strlen(str));
108 | return *this;
109 | }
110 |
111 | // 重载输出流运算符<<,用于将无符号字符指针写入缓冲区
112 | LogStream &LogStream::operator<<(const unsigned char *str) {
113 | buffer_.append(reinterpret_cast(str), strlen(reinterpret_cast(str)));
114 | return *this;
115 | }
116 |
117 | // 重载输出流运算符<<,用于将std::string对象写入缓冲区
118 | LogStream &LogStream::operator<<(const std::string &str) {
119 | buffer_.append(str.c_str(), str.size());
120 | return *this;
121 | }
122 |
123 | LogStream& LogStream::operator<<(const GeneralTemplate& g)
124 | {
125 | buffer_.append(g.data_, g.len_);
126 | return *this;
127 | }
--------------------------------------------------------------------------------
/memory/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #获取当前目录下的所有源文件项目
2 | file(GLOB MEMORY_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 |
4 | #创建静态库或共享库
5 | add_library(memory_lib SHARED ${MEMORY_FILE})
--------------------------------------------------------------------------------
/memory/memoryPool.cc:
--------------------------------------------------------------------------------
1 | #include "memoryPool.h"
2 |
3 | namespace memoryPool
4 | {
5 | MemoryPool::MemoryPool(size_t BlockSize)
6 | : BlockSize_ (BlockSize)
7 | {}
8 |
9 | MemoryPool::~MemoryPool()
10 | {
11 | // 把连续的block删除
12 | Slot* cur = firstBlock_;
13 | while (cur)
14 | {
15 | Slot* next = cur->next;
16 | // 等同于 free(reinterpret_cast(firstBlock_));
17 | // 转化为 void 指针,因为 void 类型不需要调用析构函数,只释放空间
18 | operator delete(reinterpret_cast(cur));
19 | cur = next;
20 | }
21 | }
22 |
23 | void MemoryPool::init(size_t size)
24 | {
25 | assert(size > 0);
26 | SlotSize_ = size;
27 | firstBlock_ = nullptr;
28 | curSlot_ = nullptr;
29 | freeList_ = nullptr;
30 | lastSlot_ = nullptr;
31 | }
32 |
33 | void* MemoryPool::allocate()
34 | {
35 | // 优先使用空闲链表中的内存槽
36 | if (freeList_ != nullptr)
37 | {
38 | {
39 | std::lock_guard lock(mutexForFreeList_);
40 | if (freeList_ != nullptr)
41 | {
42 | Slot* temp = freeList_;
43 | freeList_ = freeList_->next;
44 | return temp;
45 | }
46 | }
47 | }
48 |
49 | Slot* temp;
50 | {
51 | std::lock_guard lock(mutexForBlock_);
52 | if (curSlot_ >= lastSlot_)
53 | {
54 | // 当前内存块已无内存槽可用,开辟一块新的内存
55 | allocateNewBlock();
56 | }
57 |
58 | temp = curSlot_;
59 | // 这里不能直接 curSlot_ += SlotSize_ 因为curSlot_是Slot*类型,所以需要除以SlotSize_再加1
60 | curSlot_ += SlotSize_ / sizeof(Slot);
61 | }
62 |
63 | return temp;
64 | }
65 |
66 | void MemoryPool::deallocate(void* ptr)
67 | {
68 | if (ptr)
69 | {
70 | // 回收内存,将内存通过头插法插入到空闲链表中
71 | std::lock_guard lock(mutexForFreeList_);
72 | reinterpret_cast(ptr)->next = freeList_;
73 | freeList_ = reinterpret_cast(ptr);
74 | }
75 | }
76 |
77 | void MemoryPool::allocateNewBlock()
78 | {
79 | //std::cout << "申请一块内存块,SlotSize: " << SlotSize_ << std::endl;
80 | // 头插法插入新的内存块
81 | void* newBlock = operator new(BlockSize_);
82 | reinterpret_cast(newBlock)->next = firstBlock_;
83 | firstBlock_ = reinterpret_cast(newBlock);
84 |
85 | char* body = reinterpret_cast(newBlock) + sizeof(Slot*);
86 | size_t paddingSize = padPointer(body, SlotSize_); // 计算对齐需要填充内存的大小
87 | curSlot_ = reinterpret_cast(body + paddingSize);
88 |
89 | // 超过该标记位置,则说明该内存块已无内存槽可用,需向系统申请新的内存块
90 | lastSlot_ = reinterpret_cast(reinterpret_cast(newBlock) + BlockSize_ - SlotSize_ + 1);
91 |
92 | freeList_ = nullptr;
93 | }
94 |
95 | // 让指针对齐到槽大小的倍数位置
96 | size_t MemoryPool::padPointer(char* p, size_t align)
97 | {
98 | // align 是槽大小
99 | return (align - reinterpret_cast(p)) % align;
100 | }
101 |
102 | void HashBucket::initMemoryPool()
103 | {
104 | for (int i = 0; i < MEMORY_POOL_NUM; i++)
105 | {
106 | getMemoryPool(i).init((i + 1) * SLOT_BASE_SIZE);
107 | }
108 | }
109 |
110 | // 单例模式
111 | MemoryPool& HashBucket::getMemoryPool(int index)
112 | {
113 | static MemoryPool memoryPool[MEMORY_POOL_NUM];
114 | return memoryPool[index];
115 | }
116 |
117 | } // namespace memoryPool
118 |
--------------------------------------------------------------------------------
/src/Acceptor.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | static int createNonblocking()
10 | {
11 | int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
12 | if (sockfd < 0)
13 | {
14 | LOG_FATAL << "listen socket create err " << errno;
15 | }
16 | return sockfd;
17 | }
18 |
19 | Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
20 | : loop_(loop)
21 | , acceptSocket_(createNonblocking())
22 | , acceptChannel_(loop, acceptSocket_.fd())
23 | , listenning_(false)
24 | {
25 | acceptSocket_.setReuseAddr(true);
26 | acceptSocket_.setReusePort(true);
27 | acceptSocket_.bindAddress(listenAddr);
28 | // TcpServer::start() => Acceptor.listen() 如果有新用户连接 要执行一个回调(accept => connfd => 打包成Channel => 唤醒subloop)
29 | // baseloop监听到有事件发生 => acceptChannel_(listenfd) => 执行该回调函数
30 | acceptChannel_.setReadCallback(
31 | std::bind(&Acceptor::handleRead, this));
32 | }
33 |
34 | Acceptor::~Acceptor()
35 | {
36 | acceptChannel_.disableAll(); // 把从Poller中感兴趣的事件删除掉
37 | acceptChannel_.remove(); // 调用EventLoop->removeChannel => Poller->removeChannel 把Poller的ChannelMap对应的部分删除
38 | }
39 |
40 | void Acceptor::listen()
41 | {
42 | listenning_ = true;
43 | acceptSocket_.listen(); // listen
44 | acceptChannel_.enableReading(); // acceptChannel_注册至Poller !重要
45 | }
46 |
47 | // listenfd有事件发生了,就是有新用户连接了
48 | void Acceptor::handleRead()
49 | {
50 | InetAddress peerAddr;
51 | int connfd = acceptSocket_.accept(&peerAddr);
52 | if (connfd >= 0)
53 | {
54 | if (NewConnectionCallback_)
55 | {
56 | NewConnectionCallback_(connfd, peerAddr); // 轮询找到subLoop 唤醒并分发当前的新客户端的Channel
57 | }
58 | else
59 | {
60 | ::close(connfd);
61 | }
62 | }
63 | else
64 | {
65 | LOG_ERROR<<"accept Err";
66 | if (errno == EMFILE)
67 | {
68 | LOG_ERROR<<"sockfd reached limit";
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/Buffer.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | /**
8 | * 从fd上读取数据 Poller工作在LT模式
9 | * Buffer缓冲区是有大小的! 但是从fd上读取数据的时候 却不知道tcp数据的最终大小
10 | *
11 | * @description: 从socket读到缓冲区的方法是使用readv先读至buffer_,
12 | * Buffer_空间如果不够会读入到栈上65536个字节大小的空间,然后以append的
13 | * 方式追加入buffer_。既考虑了避免系统调用带来开销,又不影响数据的接收。
14 | **/
15 | ssize_t Buffer::readFd(int fd, int *saveErrno)
16 | {
17 | // 栈额外空间,用于从套接字往出读时,当buffer_暂时不够用时暂存数据,待buffer_重新分配足够空间后,在把数据交换给buffer_。
18 | char extrabuf[65536] = {0}; // 栈上内存空间 65536/1024 = 64KB
19 |
20 | /*
21 | struct iovec {
22 | ptr_t iov_base; // iov_base指向的缓冲区存放的是readv所接收的数据或是writev将要发送的数据
23 | size_t iov_len; // iov_len在各种情况下分别确定了接收的最大长度以及实际写入的长度
24 | };
25 | */
26 |
27 | // 使用iovec分配两个连续的缓冲区
28 | struct iovec vec[2];
29 | const size_t writable = writableBytes(); // 这是Buffer底层缓冲区剩余的可写空间大小 不一定能完全存储从fd读出的数据
30 |
31 | // 第一块缓冲区,指向可写空间
32 | vec[0].iov_base = begin() + writerIndex_;
33 | vec[0].iov_len = writable;
34 | // 第二块缓冲区,指向栈空间
35 | vec[1].iov_base = extrabuf;
36 | vec[1].iov_len = sizeof(extrabuf);
37 |
38 | // when there is enough space in this buffer, don't read into extrabuf.
39 | // when extrabuf is used, we read 128k-1 bytes at most.
40 | // 这里之所以说最多128k-1字节,是因为若writable为64k-1,那么需要两个缓冲区 第一个64k-1 第二个64k 所以做多128k-1
41 | // 如果第一个缓冲区>=64k 那就只采用一个缓冲区 而不使用栈空间extrabuf[65536]的内容
42 | const int iovcnt = (writable < sizeof(extrabuf)) ? 2 : 1;
43 | const ssize_t n = ::readv(fd, vec, iovcnt);
44 |
45 | if (n < 0)
46 | {
47 | *saveErrno = errno;
48 | }
49 | else if (n <= writable) // Buffer的可写缓冲区已经够存储读出来的数据了
50 | {
51 | writerIndex_ += n;
52 | }
53 | else // extrabuf里面也写入了n-writable长度的数据
54 | {
55 | writerIndex_ = buffer_.size();
56 | append(extrabuf, n - writable); // 对buffer_扩容 并将extrabuf存储的另一部分数据追加至buffer_
57 | }
58 | return n;
59 | }
60 |
61 | // inputBuffer_.readFd表示将对端数据读到inputBuffer_中,移动writerIndex_指针
62 | // outputBuffer_.writeFd标示将数据写入到outputBuffer_中,从readerIndex_开始,可以写readableBytes()个字节
63 | ssize_t Buffer::writeFd(int fd, int *saveErrno)
64 | {
65 | ssize_t n = ::write(fd, peek(), readableBytes());
66 | if (n < 0)
67 | {
68 | *saveErrno = errno;
69 | }
70 | return n;
71 | }
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #获取当前目录下的所有源文件项目
2 | file(GLOB SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 | list(REMOVE_ITEM SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)
4 |
5 |
6 | # 创建共享库
7 | add_library(src_lib SHARED ${SRC_FILE})
8 |
9 | #创建可执行文件
10 | add_executable(main main.cc)
11 |
12 | #链接必要的库
13 | target_link_libraries(main src_lib memory_lib log_lib ${LIBS})
14 |
15 |
--------------------------------------------------------------------------------
/src/Channel.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | const int Channel::kNoneEvent = 0; //空事件
8 | const int Channel::kReadEvent = EPOLLIN | EPOLLPRI; //读事件
9 | const int Channel::kWriteEvent = EPOLLOUT; //写事件
10 |
11 | // EventLoop: ChannelList Poller
12 | Channel::Channel(EventLoop *loop, int fd)
13 | : loop_(loop)
14 | , fd_(fd)
15 | , events_(0)
16 | , revents_(0)
17 | , index_(-1)
18 | , tied_(false)
19 | {
20 | }
21 |
22 | Channel::~Channel()
23 | {
24 | }
25 |
26 | // channel的tie方法什么时候调用过? TcpConnection => channel
27 | /**
28 | * TcpConnection中注册了Channel对应的回调函数,传入的回调函数均为TcpConnection
29 | * 对象的成员方法,因此可以说明一点就是:Channel的结束一定晚于TcpConnection对象!
30 | * 此处用tie去解决TcpConnection和Channel的生命周期时长问题,从而保证了Channel对象能够在
31 | * TcpConnection销毁前销毁。
32 | **/
33 | void Channel::tie(const std::shared_ptr &obj)
34 | {
35 | tie_ = obj;
36 | tied_ = true;
37 | }
38 | //update 和remove => EpollPoller 更新channel在poller中的状态
39 | /**
40 | * 当改变channel所表示的fd的events事件后,update负责再poller里面更改fd相应的事件epoll_ctl
41 | **/
42 | void Channel::update()
43 | {
44 | // 通过channel所属的eventloop,调用poller的相应方法,注册fd的events事件
45 | loop_->updateChannel(this);
46 | }
47 |
48 | // 在channel所属的EventLoop中把当前的channel删除掉
49 | void Channel::remove()
50 | {
51 | loop_->removeChannel(this);
52 | }
53 |
54 | void Channel::handleEvent(Timestamp receiveTime)
55 | {
56 | if (tied_)
57 | {
58 | std::shared_ptr guard = tie_.lock();
59 | if (guard)
60 | {
61 | handleEventWithGuard(receiveTime);
62 | }
63 | // 如果提升失败了 就不做任何处理 说明Channel的TcpConnection对象已经不存在了
64 | }
65 | else
66 | {
67 | handleEventWithGuard(receiveTime);
68 | }
69 | }
70 |
71 | void Channel::handleEventWithGuard(Timestamp receiveTime)
72 | {
73 | LOG_INFO<<"channel handleEvent revents:"<
2 |
3 | namespace CurrentThread
4 | {
5 | thread_local int t_cachedTid = 0; // 在源文件中定义线程局部变量
6 | void cacheTid()
7 | {
8 | if (t_cachedTid == 0)
9 | {
10 | t_cachedTid = static_cast(::syscall(SYS_gettid)); // Ensure syscall and SYS_gettid are defined
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/DefaultPoller.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 |
6 | Poller *Poller::newDefaultPoller(EventLoop *loop)
7 | {
8 | if (::getenv("MUDUO_USE_POLL"))
9 | {
10 | return nullptr; // 生成poll的实例
11 | }
12 | else
13 | {
14 | return new EPollPoller(loop); // 生成epoll的实例
15 | }
16 | }
--------------------------------------------------------------------------------
/src/EPollPoller.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | const int kNew = -1; // 某个channel还没添加至Poller // channel的成员index_初始化为-1
10 | const int kAdded = 1; // 某个channel已经添加至Poller
11 | const int kDeleted = 2; // 某个channel已经从Poller删除
12 |
13 | EPollPoller::EPollPoller(EventLoop *loop)
14 | : Poller(loop)
15 | , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
16 | , events_(kInitEventListSize) // vector(16)
17 | {
18 | if (epollfd_ < 0)
19 | {
20 | LOG_FATAL<<"epoll_create error:%d \n"<(events_.size()), timeoutMs);
35 | int saveErrno = errno;
36 | Timestamp now(Timestamp::now());
37 |
38 | if (numEvents > 0)
39 | {
40 | LOG_INFO<<"events happend"< EventLoop updateChannel removeChannel => Poller updateChannel removeChannel
63 | void EPollPoller::updateChannel(Channel *channel)
64 | {
65 | const int index = channel->index();
66 | LOG_INFO<<"func =>"<<"fd"<