-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
176 lines (84 loc) · 419 KB
/
search.xml
File metadata and controls
176 lines (84 loc) · 419 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>asio网络编程(一)</title>
<link href="/2024/05/13/asio%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B(%E4%B8%80)/"/>
<url>/2024/05/13/asio%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B(%E4%B8%80)/</url>
<content type="html"><![CDATA[<h2 id="asio之socket的创建和连接"><a href="#asio之socket的创建和连接" class="headerlink" title="asio之socket的创建和连接"></a>asio之socket的创建和连接</h2><h3 id="终端节点的创建"><a href="#终端节点的创建" class="headerlink" title="终端节点的创建"></a>终端节点的创建</h3><ul><li><p>所谓<strong>终端节点</strong>就是用来通信的端对端的节点,可以通过ip地址和端口构造,其的节点可以连接这个终端节点做通信。 </p></li><li><p>如果我们是客户端,我们可以通过对端的ip和端口构造一个endpoint,用这个endpoint和其通信。</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">client_end_point</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 步骤 1. 假设客户端应用程序已经获得了 IP 地址和协议端口号。</span></span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="comment">// 用于存储在解析原始 IP 地址时发生的错误信息。</span></span><br><span class="line"> boost::system::error_code ec;</span><br><span class="line"> <span class="comment">// 步骤 2. 使用 IP 协议版本无关的地址表示。</span></span><br><span class="line"> asio::ip::address ip_address =</span><br><span class="line"> asio::ip::address::<span class="built_in">from_string</span>(raw_ip_address, ec);</span><br><span class="line"> <span class="keyword">if</span> (ec.<span class="built_in">value</span>() != <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 提供的 IP 地址无效。中断执行。</span></span><br><span class="line"> std::cout</span><br><span class="line"> << <span class="string">"Failed to parse the IP address. Error code = "</span></span><br><span class="line"> << ec.<span class="built_in">value</span>() << <span class="string">". Message: "</span> << ec.<span class="built_in">message</span>();</span><br><span class="line"> <span class="keyword">return</span> ec.<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 步骤 3.</span></span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(ip_address, port_num)</span></span>;</span><br><span class="line"> <span class="comment">// 步骤 4. 端点已准备就绪,可以用来指定网络中客户端希望与之通信的特定服务器。</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>如果是服务端,则只需根据本地地址绑定就可以生成endpoint</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">server_end_point</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">// 步骤1. 这里我们假设服务器应用程序已经获取了协议端口号。</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 步骤2. 创建 asio::ip::address 类的特殊对象,</span></span><br><span class="line"> <span class="comment">// 该对象指定主机上可用的所有IP地址。注意</span></span><br><span class="line"> <span class="comment">// 这里我们假设服务器在IPv6协议上工作。</span></span><br><span class="line"> asio::ip::address ip_address = asio::ip::address_v6::<span class="built_in">any</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 步骤3. 创建 asio::ip::tcp::endpoint 实例。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(ip_address, port_num)</span></span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 步骤4. 端点已创建,可以用来</span></span><br><span class="line"> <span class="comment">// 指定服务器应用程序希望监听传入连接的</span></span><br><span class="line"> <span class="comment">// IP地址和端口号。</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="创建socket"><a href="#创建socket" class="headerlink" title="创建socket"></a>创建socket</h3><ul><li>socket进行通信必须要一个参数,这个参数就是上下文</li><li>上下文是boost的asio的一个核心服务,它的所有的服务都是通过上下文服务来通信的</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">create_tcp_socket</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 步骤1. 需要 'io_service' 类的实例作为套接字构造函数的参数。</span></span><br><span class="line"> asio::io_context ios;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 步骤2. 创建 'tcp' 类的对象,该对象表示使用 IPv4 作为底层协议的 TCP 协议。</span></span><br><span class="line"> asio::ip::tcp protocol = asio::ip::tcp::<span class="built_in">v4</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 步骤3. 实例化一个活动的 TCP 套接字对象。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用于存储打开套接字时发生的错误信息。</span></span><br><span class="line"> boost::system::error_code ec;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 步骤4. 打开套接字。</span></span><br><span class="line"> sock.<span class="built_in">open</span>(protocol, ec);</span><br><span class="line"> <span class="keyword">if</span> (ec.<span class="built_in">value</span>() != <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 打开套接字失败。</span></span><br><span class="line"> std::cout</span><br><span class="line"> << <span class="string">"Failed to open the socket! Error code = "</span></span><br><span class="line"> << ec.<span class="built_in">value</span>() << <span class="string">". Message: "</span> << ec.<span class="built_in">message</span>();</span><br><span class="line"> <span class="keyword">return</span> ec.<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上述socket只是通信的socket,如果是服务端,我们还需要生成一个acceptor的socket,用来接收新的连接。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">create_acceptor_socket</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> // 步骤1:需要一个'io_service'类的实例作为socket构造函数的参数。</span></span><br><span class="line"><span class="comment"> asio::io_context ios;</span></span><br><span class="line"><span class="comment"> // 步骤2:创建一个表示TCP协议的'tcp'类对象,使用IPv6作为底层协议。</span></span><br><span class="line"><span class="comment"> asio::ip::tcp protocol = asio::ip::tcp::v6();</span></span><br><span class="line"><span class="comment"> // 步骤3:实例化一个acceptor socket对象。</span></span><br><span class="line"><span class="comment"> asio::ip::tcp::acceptor acceptor(ios);</span></span><br><span class="line"><span class="comment"> // 用于存储在打开acceptor socket时发生的错误信息。</span></span><br><span class="line"><span class="comment"> boost::system::error_code ec;</span></span><br><span class="line"><span class="comment"> // 步骤4:打开acceptor socket。</span></span><br><span class="line"><span class="comment"> acceptor.open(protocol, ec);</span></span><br><span class="line"><span class="comment"> if (ec.value() != 0) {</span></span><br><span class="line"><span class="comment"> // 无法打开socket。</span></span><br><span class="line"><span class="comment"> std::cout</span></span><br><span class="line"><span class="comment"> << "无法打开acceptor socket!"</span></span><br><span class="line"><span class="comment"> << "错误代码 = "</span></span><br><span class="line"><span class="comment"> << ec.value() << "。消息:" << ec.message();</span></span><br><span class="line"><span class="comment"> return ec.value();</span></span><br><span class="line"><span class="comment"> }</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">// 新版本更简单的方法</span></span><br><span class="line">asio::ip::<span class="function">tcp::acceptor <span class="title">a</span><span class="params">(ios,asio::ip::tcp::endpoint(asio::ip::tcp::v4(),<span class="number">3333</span>))</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="绑定acceptor"><a href="#绑定acceptor" class="headerlink" title="绑定acceptor"></a>绑定acceptor</h3><p>对于acceptor类型的socket,服务器要将其绑定到指定的断点,所有连接这个端点的连接都可以被接收到。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">bind_acceptor_socket</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 步骤1:假设服务器应用程序已经获取了协议端口号。</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="comment">// 步骤2:创建端点。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(asio::ip::address_v4::any(), port_num)</span></span>;</span><br><span class="line"> <span class="comment">// 'acceptor'类构造函数使用。</span></span><br><span class="line"> asio::io_context ios;</span><br><span class="line"> <span class="comment">// 步骤3:创建并打开一个acceptor socket。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::acceptor <span class="title">acceptor</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> boost::system::error_code ec;</span><br><span class="line"> <span class="comment">// 步骤4:绑定acceptor socket。</span></span><br><span class="line"> acceptor.<span class="built_in">bind</span>(ep, ec);</span><br><span class="line"> <span class="comment">// 处理任何错误。</span></span><br><span class="line"> <span class="keyword">if</span> (ec.<span class="built_in">value</span>() != <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 无法绑定acceptor socket。中断执行。</span></span><br><span class="line"> std::cout << <span class="string">"无法绑定acceptor socket。"</span></span><br><span class="line"> << <span class="string">"错误代码 = "</span> << ec.<span class="built_in">value</span>() << <span class="string">"。消息:"</span></span><br><span class="line"> << ec.<span class="built_in">message</span>();</span><br><span class="line"> <span class="keyword">return</span> ec.<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="连接指定的端点"><a href="#连接指定的端点" class="headerlink" title="连接指定的端点"></a>连接指定的端点</h3><p>作为客户端可以连接服务器指定的端点进行连接</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">connect_to_end</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="comment">// 步骤1:假设客户端应用程序已经获取了目标服务器的IP地址和协议端口号。</span></span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 步骤2:创建一个指定目标服务器应用程序的端点。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address), port_num)</span></span>;</span><br><span class="line"> asio::io_context ios;</span><br><span class="line"> <span class="comment">// 步骤3:创建并打开一个socket。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> <span class="comment">// 步骤4:连接socket。</span></span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> <span class="comment">// 此时socket 'sock'已经连接到服务器应用程序,可以用于发送或接收数据。</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 这里使用了asio::ip::address::from_string()和asio::ip::tcp::socket::connect()</span></span><br><span class="line"> <span class="comment">// 的异常重载函数,以处理错误情况。</span></span><br><span class="line"> <span class="built_in">catch</span> (system::system_error &e) {</span><br><span class="line"> std::cout << <span class="string">"发生错误!错误代码 = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">"。消息:"</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><asio.hpp></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string></span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dns_connect_to_end</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">// 定义目标主机和端口</span></span><br><span class="line"> std::string host = <span class="string">"llfc.club"</span>;</span><br><span class="line"> std::string port_num = <span class="string">"3333"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建 ASIO 的 I/O 上下文对象,所有的异步操作都需要它来运行</span></span><br><span class="line"> asio::io_context ios;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建一个 DNS 查询,参数指定了主机名、端口号,以及服务类型(这里指定为数字服务,意味着 port_num 是一个服务的数字标识)</span></span><br><span class="line"> asio::ip::tcp::<span class="function">resolver::query <span class="title">resolver_query</span><span class="params">(host, port_num, asio::ip::tcp::resolver::query::numeric_service)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建解析器对象,用于 DNS 解析</span></span><br><span class="line"> asio::ip::<span class="function">tcp::resolver <span class="title">resolver</span><span class="params">(ios)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 执行 DNS 解析,返回一个迭代器,指向解析结果的开始</span></span><br><span class="line"> asio::ip::tcp::resolver::iterator it = resolver.<span class="built_in">resolve</span>(resolver_query);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建一个 TCP 套接字</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用解析出的端点信息来连接套接字</span></span><br><span class="line"> asio::<span class="built_in">connect</span>(sock, it);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (asio::system_error &e) { <span class="comment">// 捕获可能发生的异常,通常是网络错误或 DNS 解析失败</span></span><br><span class="line"> std::cout << <span class="string">"Error occurred! Error code = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="comment">// 返回错误代码</span></span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 若连接成功,返回 0 表示无错误</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="服务器接收连接"><a href="#服务器接收连接" class="headerlink" title="服务器接收连接"></a>服务器接收连接</h3><ul><li>当有客户端连接时,服务器需要接收连接<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">accept_new_connection</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">// 用于存储挂起连接请求的队列大小。</span></span><br><span class="line"> <span class="type">const</span> <span class="type">int</span> BACKLOG_SIZE = <span class="number">30</span>;</span><br><span class="line"> <span class="comment">// 步骤1:假设服务器应用程序已经获取了协议端口号。</span></span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="comment">// 步骤2:创建服务器端点。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(asio::ip::address_v4::any(), port_num)</span></span>;</span><br><span class="line"> asio::io_context ios;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 步骤3:实例化并打开一个acceptor socket。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::acceptor <span class="title">acceptor</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> <span class="comment">// 步骤4:将acceptor socket绑定到服务器端点。</span></span><br><span class="line"> acceptor.<span class="built_in">bind</span>(ep);</span><br><span class="line"> <span class="comment">// 步骤5:开始监听传入的连接请求。</span></span><br><span class="line"> acceptor.<span class="built_in">listen</span>(BACKLOG_SIZE);</span><br><span class="line"> <span class="comment">// 步骤6:创建一个活动socket。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios)</span></span>;</span><br><span class="line"> <span class="comment">// 步骤7:处理下一个连接请求并将活动socket连接到客户端。</span></span><br><span class="line"> acceptor.<span class="built_in">accept</span>(sock);</span><br><span class="line"> <span class="comment">// 此时'sock' socket已连接到客户端应用程序,可以用于发送或接收数据。</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"发生错误!错误代码 = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">"。消息:"</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h2 id="asio-socket同步读写"><a href="#asio-socket同步读写" class="headerlink" title="asio socket同步读写"></a>asio socket同步读写</h2><h3 id="关于buffer"><a href="#关于buffer" class="headerlink" title="关于buffer"></a>关于buffer</h3><ul><li>任何网络库都有提供buffer的数据结构,所谓buffer就是接收和发送数据时缓存数据的结构。 </li><li><code>boost::asio</code>提供了<code>asio::mutable_buffer</code>和<code>asio::const_buffer</code>这两个结构,他们是一段连续的空间,<strong>首字节存储了后续数据的长度</strong>。 </li><li><code>asio::mutable_buffer</code>用于写服务,<code>asio::const_buffer</code>用于读服务。但是这两个结构都没有被asio的api直接使用。 </li><li>对于api的buffer参数,asio提出了<code>MutableBufferSequence</code>和<code>ConstBufferSequence</code>概念,他们是由多个<code>asio::mutable_buffer</code>和<code>asio::const_buffer</code>组成的。也就是说<code>boost::asio</code>为了节省空间,将一部分连续的空间组合起来,作为参数交给api使用。 </li><li>我们可以理解为<code>MutableBufferSequence</code>的数据结构为<code>std::vector<asio::mutable_buffer></code><br>结构如下</li></ul><img src="https://cdn.llfc.club/1676257797218.jpg" alt="image.png" style="zoom:80%;" /><ul><li>每隔<code>vector</code>存储的都是<code>mutable_buffer</code>的地址,每个<code>mutable_buffer</code>的第一个字节表示数据的长度,后面跟着数据内容。 </li><li>这么复杂的结构交给用户使用并不合适,所以asio提出了<code>buffer()</code>函数,该函数接收多种形式的字节流,该函数返回<code>asio::mutable_buffers_1 o</code>或者<code>asio::const_buffers_1</code>结构的对象。 </li><li>如果传递给<code>buffer()</code>的参数是一个只读类型,则函数返回<code>asio::const_buffers_1</code> 类型对象。 </li><li>如果传递给<code>buffer()</code>的参数是一个可写类型,则返回<code>asio::mutable_buffers_1</code> 类型对象。 </li><li><code>asio::const_buffers_1</code>和<code>asio::mutable_buffers_1</code>是<code>asio::mutable_buffer</code>和<code>asio::const_buffer</code>的适配器,提供了符合<code>MutableBufferSequence</code>和<code>ConstBufferSequence</code>概念的接口,所以他们可以作为<code>boost::asio</code>的api函数的参数使用。 </li><li>简单概括一下,我们可以用<code>buffer()</code>函数生成我们要用的缓存存储数据。 </li><li>比如boost的发送接口send要求的参数为<code>ConstBufferSequence</code>类型</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span><<span class="keyword">typename</span> ConstBufferSequence></span></span><br><span class="line"><span class="function">std::<span class="type">size_t</span> <span class="title">send</span><span class="params">(<span class="type">const</span> ConstBufferSequence & buffers)</span></span>;</span><br></pre></td></tr></table></figure><p>我们需要将”Hello Word转化为该类型”</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">use_const_buffer</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string buf = <span class="string">"hello world!"</span>;</span><br><span class="line"> <span class="function">asio::const_buffer <span class="title">asio_buf</span><span class="params">(buf.c_str(), buf.length())</span></span>;</span><br><span class="line"> std::vector<asio::const_buffer> buffers_sequence;</span><br><span class="line"> buffers_sequence.<span class="built_in">push_back</span>(asio_buf);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>最终buffers_sequence就是可以传递给发送接口send的类型。但是这太复杂了,可以直接用buffer函数转化为send需要的参数类型</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">use_buffer_str</span><span class="params">()</span> </span>{</span><br><span class="line"> asio::const_buffers_1 output_buf = asio::<span class="built_in">buffer</span>(<span class="string">"hello world"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>output_buf可以直接传递给该send接口。我们也可以将数组转化为send接受的类型</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">use_buffer_array</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="type">const</span> <span class="type">size_t</span> BUF_SIZE_BYTES = <span class="number">20</span>;</span><br><span class="line"> <span class="function">std::unique_ptr<<span class="type">char</span>[] > <span class="title">buf</span><span class="params">(<span class="keyword">new</span> <span class="type">char</span>[BUF_SIZE_BYTES])</span></span>;</span><br><span class="line"> <span class="keyword">auto</span> input_buf = asio::<span class="built_in">buffer</span>(<span class="built_in">static_cast</span><<span class="type">void</span>*>(buf.<span class="built_in">get</span>()), BUF_SIZE_BYTES);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>对于流式操作,我们可以用streambuf,将输入输出流和streambuf绑定,可以实现流式输入和输出。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">use_stream_buffer</span><span class="params">()</span> </span>{</span><br><span class="line"> asio::streambuf buf;</span><br><span class="line"> <span class="function">std::ostream <span class="title">output</span><span class="params">(&buf)</span></span>;</span><br><span class="line"> <span class="comment">// 将消息写入基于流的缓冲区。</span></span><br><span class="line"> output << <span class="string">"Message1\nMessage2"</span>;</span><br><span class="line"> <span class="comment">// 现在我们想要从流缓冲区中读取所有数据,直到遇到'\n'分隔符。</span></span><br><span class="line"> <span class="comment">// 实例化一个输入流,它使用我们的流缓冲区。</span></span><br><span class="line"> <span class="function">std::istream <span class="title">input</span><span class="params">(&buf)</span></span>;</span><br><span class="line"> <span class="comment">// 我们将把数据读入这个字符串中。</span></span><br><span class="line"> std::string message1;</span><br><span class="line"> std::<span class="built_in">getline</span>(input, message1);</span><br><span class="line"> <span class="comment">// 现在message1字符串中包含'Message1'。</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="同步写write-some"><a href="#同步写write-some" class="headerlink" title="同步写write_some"></a>同步写write_some</h3><ul><li><code>boost::asio</code>提供了几种同步写的api,<code>write_som</code>e可以每次向指定的空间写入<strong>固定</strong>的字节数,如果写缓冲区满了,就<strong>只写一部分</strong>,返回写入的字节数。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_to_socket</span><span class="params">(asio::ip::tcp::socket& sock)</span> </span>{</span><br><span class="line"> std::string buf = <span class="string">"Hello World!"</span>;</span><br><span class="line"> std::<span class="type">size_t</span> total_bytes_written = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// 循环发送</span></span><br><span class="line"> <span class="comment">// write_some返回每次写入的字节数</span></span><br><span class="line"> <span class="comment">// total_bytes_written是已经发送的字节数。</span></span><br><span class="line"> <span class="comment">// 每次发送buf.length()- total_bytes_written)字节数据</span></span><br><span class="line"> <span class="keyword">while</span> (total_bytes_written != buf.<span class="built_in">length</span>()) {</span><br><span class="line"> total_bytes_written += sock.<span class="built_in">write_some</span>(asio::<span class="built_in">buffer</span>(buf.<span class="built_in">c_str</span>() + total_bytes_written, buf.<span class="built_in">length</span>() - total_bytes_written));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="同步写send"><a href="#同步写send" class="headerlink" title="同步写send"></a>同步写send</h3><ul><li><code>write_some</code>使用起来比较麻烦,需要多次调用,asio提供了send函数。send函数会一次性将buffer中的内容发送给对端,如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">send_data_by_send</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address), port_num)</span></span>;</span><br><span class="line"> asio::io_context ios;</span><br><span class="line"> <span class="comment">// 步骤1:分配并打开socket。</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> std::string buf = <span class="string">"Hello World!"</span>;</span><br><span class="line"> <span class="type">int</span> send_length = sock.<span class="built_in">send</span>(asio::<span class="built_in">buffer</span>(buf.<span class="built_in">c_str</span>(), buf.<span class="built_in">length</span>()));</span><br><span class="line"> <span class="keyword">if</span> (send_length <= <span class="number">0</span>) {</span><br><span class="line"> cout << <span class="string">"发送失败"</span> << endl;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"发生错误!错误代码 = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">"。消息:"</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="同步写write"><a href="#同步写write" class="headerlink" title="同步写write"></a>同步写write</h3><ul><li>类似send方法,asio还提供了一个write函数,可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">send_data_by_wirte</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint</span></span><br><span class="line"><span class="function"> <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address),</span></span></span><br><span class="line"><span class="params"><span class="function"> port_num)</span></span>;</span><br><span class="line"> asio::io_service ios;</span><br><span class="line"> <span class="comment">// Step 1. Allocating and opening the socket.</span></span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> std::string buf = <span class="string">"Hello World!"</span>;</span><br><span class="line"> <span class="type">int</span> send_length = asio::<span class="built_in">write</span>(sock, asio::<span class="built_in">buffer</span>(buf.<span class="built_in">c_str</span>(), buf.<span class="built_in">length</span>()));</span><br><span class="line"> <span class="keyword">if</span> (send_length <= <span class="number">0</span>) {</span><br><span class="line"> cout << <span class="string">"send failed"</span> << endl;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"Error occured! Error code = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li></ul><h3 id="同步读read-some"><a href="#同步读read-some" class="headerlink" title="同步读read_some"></a>同步读read_some</h3><ul><li>同步读和同步写类似,提供了读取指定字节数的接口read_some<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">std::string <span class="title">read_from_socket</span><span class="params">(asio::ip::tcp::socket& sock)</span> </span>{</span><br><span class="line"> <span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> MESSAGE_SIZE = <span class="number">7</span>;</span><br><span class="line"> <span class="type">char</span> buf[MESSAGE_SIZE];</span><br><span class="line"> std::<span class="type">size_t</span> total_bytes_read = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (total_bytes_read != MESSAGE_SIZE) {</span><br><span class="line"> total_bytes_read += sock.<span class="built_in">read_some</span>(</span><br><span class="line"> asio::<span class="built_in">buffer</span>(buf + total_bytes_read,</span><br><span class="line"> MESSAGE_SIZE - total_bytes_read));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> std::<span class="built_in">string</span>(buf, total_bytes_read);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">read_data_by_read_some</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint</span></span><br><span class="line"><span class="function"> <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address),</span></span></span><br><span class="line"><span class="params"><span class="function"> port_num)</span></span>;</span><br><span class="line"> asio::io_service ios;</span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> <span class="built_in">read_from_socket</span>(sock);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"Error occured! Error code = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="同步读receive"><a href="#同步读receive" class="headerlink" title="同步读receive"></a>同步读receive</h3><ul><li>可以一次性同步接收对方发送的数据<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">read_data_by_receive</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint</span></span><br><span class="line"><span class="function"> <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address),</span></span></span><br><span class="line"><span class="params"><span class="function"> port_num)</span></span>;</span><br><span class="line"> asio::io_service ios;</span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> <span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> BUFF_SIZE = <span class="number">7</span>;</span><br><span class="line"> <span class="type">char</span> buffer_receive[BUFF_SIZE];</span><br><span class="line"> <span class="type">int</span> receive_length = sock.<span class="built_in">receive</span>(asio::<span class="built_in">buffer</span>(buffer_receive, BUFF_SIZE));</span><br><span class="line"> <span class="keyword">if</span> (receive_length <= <span class="number">0</span>) {</span><br><span class="line"> cout << <span class="string">"receive failed"</span> << endl;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"Error occured! Error code = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="同步读read"><a href="#同步读read" class="headerlink" title="同步读read"></a>同步读read</h3><ul><li>可以一次性同步读取对方发送的数据</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">read_data_by_read</span><span class="params">()</span> </span>{</span><br><span class="line"> std::string raw_ip_address = <span class="string">"127.0.0.1"</span>;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">short</span> port_num = <span class="number">3333</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> asio::ip::<span class="function">tcp::endpoint</span></span><br><span class="line"><span class="function"> <span class="title">ep</span><span class="params">(asio::ip::address::from_string(raw_ip_address),</span></span></span><br><span class="line"><span class="params"><span class="function"> port_num)</span></span>;</span><br><span class="line"> asio::io_service ios;</span><br><span class="line"> asio::ip::<span class="function">tcp::socket <span class="title">sock</span><span class="params">(ios, ep.protocol())</span></span>;</span><br><span class="line"> sock.<span class="built_in">connect</span>(ep);</span><br><span class="line"> <span class="type">const</span> <span class="type">unsigned</span> <span class="type">char</span> BUFF_SIZE = <span class="number">7</span>;</span><br><span class="line"> <span class="type">char</span> buffer_receive[BUFF_SIZE];</span><br><span class="line"> <span class="type">int</span> receive_length = asio::<span class="built_in">read</span>(sock, asio::<span class="built_in">buffer</span>(buffer_receive, BUFF_SIZE));</span><br><span class="line"> <span class="keyword">if</span> (receive_length <= <span class="number">0</span>) {</span><br><span class="line"> cout << <span class="string">"receive failed"</span> << endl;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">catch</span> (system::system_error& e) {</span><br><span class="line"> std::cout << <span class="string">"Error occured! Error code = "</span> << e.<span class="built_in">code</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << e.<span class="built_in">what</span>();</span><br><span class="line"> <span class="keyword">return</span> e.<span class="built_in">code</span>().<span class="built_in">value</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="读取直到指定字符"><a href="#读取直到指定字符" class="headerlink" title="读取直到指定字符"></a>读取直到指定字符</h3><ul><li>我们可以一直读取,直到读取指定字符结束</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">std::string <span class="title">read_data_by_until</span><span class="params">(asio::ip::tcp::socket& sock)</span> </span>{</span><br><span class="line"> asio::streambuf buf;</span><br><span class="line"> <span class="comment">// 同步从套接字读取数据,直到遇到'\n'符号。</span></span><br><span class="line"> asio::<span class="built_in">read_until</span>(sock, buf, <span class="string">'\n'</span>);</span><br><span class="line"> std::string message;</span><br><span class="line"> <span class="comment">// 因为缓冲区'buf'可能包含'\n'符号后的其他数据,所以我们需要解析缓冲区,并提取分隔符前的数据。</span></span><br><span class="line"> <span class="function">std::istream <span class="title">input_stream</span><span class="params">(&buf)</span></span>;</span><br><span class="line"> std::<span class="built_in">getline</span>(input_stream, message);</span><br><span class="line"> <span class="keyword">return</span> message;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="asio异步读写操作"><a href="#asio异步读写操作" class="headerlink" title="asio异步读写操作"></a>asio异步读写操作</h2><h3 id="异步写操作"><a href="#异步写操作" class="headerlink" title="异步写操作"></a>异步写操作</h3><p>在写操作前,我们先封装一个Node结构,用来管理要发送和接收的数据,该结构包含数据域首地址,数据的总长度,以及已经处理的长度(已读的长度或者已写的长度)</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//最大报文接收大小</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> RECVSIZE = <span class="number">1024</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MsgNode</span> {</span><br><span class="line"><span class="keyword">public</span> :</span><br><span class="line"> <span class="built_in">MsgNode</span>(<span class="type">const</span> <span class="type">char</span>* msg, <span class="type">int</span> total_len): _total_len(total_len), _cur_len(<span class="number">0</span>){</span><br><span class="line"> _msg = <span class="keyword">new</span> <span class="type">char</span>[total_len];</span><br><span class="line"> <span class="built_in">memcpy</span>(_msg, msg, total_len);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">MsgNode</span>(<span class="type">int</span> total_len) :_total_len(total_len), _cur_len(<span class="number">0</span>) {</span><br><span class="line"> _msg = <span class="keyword">new</span> <span class="type">char</span>[total_len];</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">MsgNode</span>(){</span><br><span class="line"> <span class="keyword">delete</span>[]_msg;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//消息首地址</span></span><br><span class="line"> <span class="type">char</span>* _msg;</span><br><span class="line"> <span class="comment">//总长度</span></span><br><span class="line"> <span class="type">int</span> _total_len;</span><br><span class="line"> <span class="comment">//当前长度</span></span><br><span class="line"> <span class="type">int</span> _cur_len;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>写了两个构造函数,两个参数的负责构造写节点,一个参数的负责构造读节点。<br>接下来为Session添加异步写操作和负责发送写数据的节点</p><ul><li>需要一个回调函数<code>WriteCallBackErr</code>,其中<code>bytes_transferred</code>代表我们写了多少字符</li><li><code>WriteToSocketErr</code>是封装的异步写函数</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Session</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">WriteCallBackErr</span><span class="params">(<span class="type">const</span> boost::system::error_code & ec, std::<span class="type">size_t</span> bytes_transferred,std::shared_ptr<MsgNode>)</span></span>; <span class="comment">//</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">WriteToSocketErr</span><span class="params">(<span class="type">const</span> std::string& buf)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> std::shared_ptr<MsgNode> _send_node;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>WriteToSocketErr函数为我们封装的写操作,WriteCallBackErr为异步写操作回调的函数,为什么会有三个参数呢,我们可以看一下asio源码</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">BOOST_ASIO_COMPLETION_TOKEN_FOR</span>(<span class="built_in">void</span> (boost::system::error_code,</span><br><span class="line"> std::<span class="type">size_t</span>)) <span class="function">WriteToken</span></span><br><span class="line"><span class="function"> <span class="title">BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE</span><span class="params">(executor_type)</span>></span></span><br><span class="line"><span class="function"> <span class="title">BOOST_ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX</span><span class="params">(WriteToken,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">void</span> (boost::system::error_code, std::<span class="type">size_t</span>))</span></span></span><br><span class="line"><span class="function"> <span class="title">async_write_some</span><span class="params">(<span class="type">const</span> ConstBufferSequence& buffers,</span></span></span><br><span class="line"><span class="params"><span class="function"> BOOST_ASIO_MOVE_ARG(WriteToken)token</span></span></span><br><span class="line"><span class="params"><span class="function"> BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))</span></span></span><br></pre></td></tr></table></figure><p><code>async_write_some</code>是异步写的函数,这个异步写函数有两个参数,第一个参数为<code>ConstBufferSequence</code>常引用类型的buffers,第二个参数为<code>WriteToken</code>类型,而<code>WriteToken</code>在上面定义了,是一个函数对象类型,返回值为void,参数为<code>error_code</code>和size_t,所以我们为了调用<code>async_write_some</code>函数也要传入一个符合WriteToken定义的函数,就是我们声明的<code>WriteCallBackErr</code>函数,前两个参数为<code>WriteToken</code>规定的参数,第三个参数为MsgNode的智能指针,这样通过智能指针保证我们发送的Node生命周期延长。我们看一下<code>WriteToSocketErr</code>函数的具体实现</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteToSocketErr</span><span class="params">(<span class="type">const</span> std::string& buf)</span> </span>{</span><br><span class="line"> _send_node = <span class="built_in">make_shared</span><MsgNode>(buf.<span class="built_in">c_str</span>(), buf.<span class="built_in">length</span>());</span><br><span class="line"> <span class="comment">//异步发送数据,因为异步所以不会一下发送完</span></span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_write_some</span>(asio::<span class="built_in">buffer</span>(_send_node->_msg, </span><br><span class="line"> _send_node->_total_len),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteCallBackErr,</span><br><span class="line"> <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2, _send_node));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>因为<code>WriteCallBackErr</code>函数为三个参数且为成员函数,而<code>async_write_some</code>需要的回调函数为两个参数,所以我们通过bind将三个参数转换为两个参数的普通函数。<br>我们看看回调函数的实现</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteCallBackErr</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, </span></span></span><br><span class="line"><span class="params"><span class="function"> std::<span class="type">size_t</span> bytes_transferred, std::shared_ptr<MsgNode> msg_node)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (bytes_transferred + msg_node->_cur_len </span><br><span class="line"> < msg_node->_total_len) {</span><br><span class="line"> _send_node->_cur_len += bytes_transferred;</span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_write_some</span>(asio::<span class="built_in">buffer</span>(_send_node->_msg+_send_node->_cur_len,</span><br><span class="line"> _send_node->_total_len-_send_node->_cur_len),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteCallBackErr,</span><br><span class="line"> <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2, _send_node));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在<code>WriteCallBackErr</code>函数里判断如果已经发送的字节数没有达到要发送的总字节数,那么久更新节点已经发送的长度,然后计算剩余要发送的长度,如果有数据未发送完,再次调用async_write_some函数异步发送。<br>但是这个函数并不能投入实际应用,因为<code>async_write_some</code>回调函数返回已发送的字节数可能并不是全部长度。比如TCP发送缓存区总大小为8字节,但是有3字节未发送(上一次未发送完),这样剩余空间为5字节</p><img src="https://cdn.llfc.club/1680692232796.jpg" alt="image.png" style="zoom:60%;" />此时我们调用`async_write_some`发送hello world!实际发送的长度就是为5,也就是只发送了hello,剩余world!通过我们的回调继续发送。 而实际开发的场景用户是不清楚底层tcp的多路复用调用情况的,用户想发送数据的时候就调用WriteToSocketErr,或者循环调用WriteToSocketErr,很可能在一次没发送完数据还未调用回调函数时再次调用`WriteToSocketErr`,因为boost::asio封装的时epoll和iocp等多路复用模型,当写事件就绪后就发数据,发送的数据按照async_write_some调用的顺序发送,所以回调函数内调用的async_write_some可能并没有被及时调用。 比如我们如下代码<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//用户发送数据</span></span><br><span class="line"><span class="built_in">WriteToSocketErr</span>(<span class="string">"Hello World!"</span>);</span><br><span class="line"><span class="comment">//用户无感知下层调用情况又一次发送了数据</span></span><br><span class="line"><span class="built_in">WriteToSocketErr</span>(<span class="string">"Hello World!"</span>);</span><br></pre></td></tr></table></figure>那么很可能第一次只发送了Hello,后面的数据没发完,第二次发送了Hello World!之后又发送了World!所以对端收到的数据很可能是”HelloHello World! World!”那怎么解决这个问题呢,我们可以通过队列保证应用层的发送顺序。我们在Session中定义一个发送队列,然后重新定义正确的异步发送函数和回调处理<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Session</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">WriteCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, std::<span class="type">size_t</span> bytes_transferred)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">WriteToSocket</span><span class="params">(<span class="type">const</span> std::string &buf)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> std::queue<std::shared_ptr<MsgNode>> _send_queue;</span><br><span class="line"> std::shared_ptr<asio::ip::tcp::socket> _socket;</span><br><span class="line"> <span class="type">bool</span> _send_pending;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>定义了bool变量<code>_send_pending</code>,该变量为true表示一个节点还未发送完。<br><code>_send_queue</code>用来缓存要发送的消息节点,是一个队列。<br>我们实现异步发送功能</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteToSocket</span><span class="params">(<span class="type">const</span> std::string& buf)</span></span>{</span><br><span class="line"> <span class="comment">//插入发送队列</span></span><br><span class="line"> _send_queue.<span class="built_in">emplace</span>(<span class="keyword">new</span> <span class="built_in">MsgNode</span>(buf.<span class="built_in">c_str</span>(), buf.<span class="built_in">length</span>()));</span><br><span class="line"> <span class="comment">//pending状态说明上一次有未发送完的数据</span></span><br><span class="line"> <span class="keyword">if</span> (_send_pending) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//异步发送数据,因为异步所以不会一下发送完</span></span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_write_some</span>(asio::<span class="built_in">buffer</span>(buf), std::<span class="built_in">bind</span>(&Session::WriteCallBack, <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> _send_pending = <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code & ec, std::<span class="type">size_t</span> bytes_transferred)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (ec.<span class="built_in">value</span>() != <span class="number">0</span>) {</span><br><span class="line"> std::cout << <span class="string">"Error , code is "</span> << ec.<span class="built_in">value</span>() << <span class="string">" . Message is "</span> << ec.<span class="built_in">message</span>();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//取出队首元素即当前未发送完数据</span></span><br><span class="line"> <span class="keyword">auto</span> & send_data = _send_queue.<span class="built_in">front</span>();</span><br><span class="line"> send_data->_cur_len += bytes_transferred;</span><br><span class="line"> <span class="comment">//数据未发送完, 则继续发送</span></span><br><span class="line"> <span class="keyword">if</span> (send_data->_cur_len < send_data->_total_len) {</span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_write_some</span>(asio::<span class="built_in">buffer</span>(send_data->_msg + send_data->_cur_len, send_data->_total_len-send_data->_cur_len),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteCallBack,</span><br><span class="line"> <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果发送完,则pop出队首元素</span></span><br><span class="line"> _send_queue.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="comment">//如果队列为空,则说明所有数据都发送完,将pending设置为false</span></span><br><span class="line"> <span class="keyword">if</span> (_send_queue.<span class="built_in">empty</span>()) {</span><br><span class="line"> _send_pending = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果队列不是空,则继续将队首元素发送</span></span><br><span class="line"> <span class="keyword">if</span> (!_send_queue.<span class="built_in">empty</span>()) {</span><br><span class="line"> <span class="keyword">auto</span>& send_data = _send_queue.<span class="built_in">front</span>();</span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_write_some</span>(asio::<span class="built_in">buffer</span>(send_data->_msg + send_data->_cur_len, send_data->_total_len - send_data->_cur_len),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteCallBack,</span><br><span class="line"> <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>async_write_some</code>函数不能保证每次回调函数触发时发送的长度为要总长度,这样我们每次都要在回调函数判断发送数据是否完成,asio提供了一个更简单的发送函数<code>async_send</code>,这个函数在发送的长度未达到我们要求的长度时就不会触发回调,所以触发回调函数时要么时发送出错了要么是发送完成了,其内部的实现原理就是帮我们不断的调用<code>async_write_some</code>直到完成发送,所以<code>async_send</code>不能和<code>async_write_some</code>混合使用,我们基于<code>async_send</code>封装另外一个发送函数</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//不能与async_write_some混合使用</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteAllToSocket</span><span class="params">(<span class="type">const</span> std::string& buf)</span> </span>{</span><br><span class="line"> <span class="comment">//插入发送队列</span></span><br><span class="line"> _send_queue.<span class="built_in">emplace</span>(<span class="keyword">new</span> <span class="built_in">MsgNode</span>(buf.<span class="built_in">c_str</span>(), buf.<span class="built_in">length</span>()));</span><br><span class="line"> <span class="comment">//pending状态说明上一次有未发送完的数据</span></span><br><span class="line"> <span class="keyword">if</span> (_send_pending) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//异步发送数据,因为异步所以不会一下发送完</span></span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_send</span>(asio::<span class="built_in">buffer</span>(buf), </span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteAllCallBack, <span class="keyword">this</span>,</span><br><span class="line"> std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> _send_pending = <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::WriteAllCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, std::<span class="type">size_t</span> bytes_transferred)</span></span>{</span><br><span class="line"> <span class="keyword">if</span> (ec.<span class="built_in">value</span>() != <span class="number">0</span>) {</span><br><span class="line"> std::cout << <span class="string">"Error occured! Error code = "</span></span><br><span class="line"> << ec.<span class="built_in">value</span>()</span><br><span class="line"> << <span class="string">". Message: "</span> << ec.<span class="built_in">message</span>();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果发送完,则pop出队首元素</span></span><br><span class="line"> _send_queue.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="comment">//如果队列为空,则说明所有数据都发送完,将pending设置为false</span></span><br><span class="line"> <span class="keyword">if</span> (_send_queue.<span class="built_in">empty</span>()) {</span><br><span class="line"> _send_pending = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果队列不是空,则继续将队首元素发送</span></span><br><span class="line"> <span class="keyword">if</span> (!_send_queue.<span class="built_in">empty</span>()) {</span><br><span class="line"> <span class="keyword">auto</span>& send_data = _send_queue.<span class="built_in">front</span>();</span><br><span class="line"> <span class="keyword">this</span>->_socket-><span class="built_in">async_send</span>(asio::<span class="built_in">buffer</span>(send_data->_msg + send_data->_cur_len, send_data->_total_len - send_data->_cur_len),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::WriteAllCallBack,</span><br><span class="line"> <span class="keyword">this</span>, std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="异步读操作"><a href="#异步读操作" class="headerlink" title="异步读操作"></a>异步读操作</h3><p>接下来介绍异步读操作,异步读操作和异步的写操作类似同样又async_read_some和async_receive函数,前者触发的回调函数获取的读数据的长度可能会小于要求读取的总长度,后者触发的回调函数读取的数据长度等于读取的总长度。<br>先基于<code>async_read_some</code>封装一个读取的函数<code>ReadFromSocket</code>,同样在Session类的声明中添加一些变量</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Session</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">ReadFromSocket</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">ReadCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, std::<span class="type">size_t</span> bytes_transferred)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> std::shared_ptr<asio::ip::tcp::socket> _socket;</span><br><span class="line"> std::shared_ptr<MsgNode> _recv_node;</span><br><span class="line"> <span class="type">bool</span> _recv_pending;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><code>_recv_node</code>用来缓存接收的数据,<code>_recv_pending</code>为true表示节点正在接收数据,还未接受完。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//不考虑粘包情况, 先用固定的字节接收</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::ReadFromSocket</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (_recv_pending) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//可以调用构造函数直接构造,但不可用已经构造好的智能指针赋值</span></span><br><span class="line"> <span class="comment">/*auto _recv_nodez = std::make_unique<MsgNode>(RECVSIZE);</span></span><br><span class="line"><span class="comment"> _recv_node = _recv_nodez;*/</span></span><br><span class="line"> _recv_node = std::<span class="built_in">make_shared</span><MsgNode>(RECVSIZE);</span><br><span class="line"> _socket-><span class="built_in">async_read_some</span>(asio::<span class="built_in">buffer</span>(_recv_node->_msg, _recv_node->_total_len), std::<span class="built_in">bind</span>(&Session::ReadCallBack, <span class="keyword">this</span>,</span><br><span class="line"> std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> _recv_pending = <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::ReadCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, std::<span class="type">size_t</span> bytes_transferred)</span></span>{</span><br><span class="line"> _recv_node->_cur_len += bytes_transferred;</span><br><span class="line"> <span class="comment">//没读完继续读</span></span><br><span class="line"> <span class="keyword">if</span> (_recv_node->_cur_len < _recv_node->_total_len) {</span><br><span class="line"> _socket-><span class="built_in">async_read_some</span>(asio::<span class="built_in">buffer</span>(_recv_node->_msg+_recv_node->_cur_len,</span><br><span class="line"> _recv_node->_total_len - _recv_node->_cur_len), std::<span class="built_in">bind</span>(&Session::ReadCallBack, <span class="keyword">this</span>,</span><br><span class="line"> std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//将数据投递到队列里交给逻辑线程处理,此处略去</span></span><br><span class="line"> <span class="comment">//如果读完了则将标记置为false</span></span><br><span class="line"> _recv_pending = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">//指针置空</span></span><br><span class="line"> _recv_node = <span class="literal">nullptr</span>; </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们基于<code>async_receive</code>再封装一个接收数据的函数</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::ReadAllFromSocket</span><span class="params">(<span class="type">const</span> std::string& buf)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (_recv_pending) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//可以调用构造函数直接构造,但不可用已经构造好的智能指针赋值</span></span><br><span class="line"> <span class="comment">/*auto _recv_nodez = std::make_unique<MsgNode>(RECVSIZE);</span></span><br><span class="line"><span class="comment"> _recv_node = _recv_nodez;*/</span></span><br><span class="line"> _recv_node = std::<span class="built_in">make_shared</span><MsgNode>(RECVSIZE);</span><br><span class="line"> _socket-><span class="built_in">async_receive</span>(asio::<span class="built_in">buffer</span>(_recv_node->_msg, _recv_node->_total_len), std::<span class="built_in">bind</span>(&Session::ReadAllCallBack, <span class="keyword">this</span>,</span><br><span class="line"> std::placeholders::_1, std::placeholders::_2));</span><br><span class="line"> _recv_pending = <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::ReadAllCallBack</span><span class="params">(<span class="type">const</span> boost::system::error_code& ec, std::<span class="type">size_t</span> bytes_transferred)</span> </span>{</span><br><span class="line"> _recv_node->_cur_len += bytes_transferred;</span><br><span class="line"> <span class="comment">//将数据投递到队列里交给逻辑线程处理,此处略去</span></span><br><span class="line"> <span class="comment">//如果读完了则将标记置为false</span></span><br><span class="line"> _recv_pending = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">//指针置空</span></span><br><span class="line"> _recv_node = <span class="literal">nullptr</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同样<code>async_read_some</code>和<code>async_receive</code>不能混合使用,否则会出现逻辑问题。</p><h2 id="异步读写的服务器示例"><a href="#异步读写的服务器示例" class="headerlink" title="异步读写的服务器示例"></a>异步读写的服务器示例</h2><h3 id="Session类"><a href="#Session类" class="headerlink" title="Session类"></a>Session类</h3><p>Session类主要是处理客户端消息收发的会话类,为了简单起见,我们不考虑粘包问题,也不考虑支持手动调用发送的接口,只以应答的方式发送和接收固定长度(1024字节长度)的数据。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Session</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Session</span>(boost::asio::io_context& ioc):_socket(ioc){</span><br><span class="line"> }</span><br><span class="line"> <span class="function">tcp::socket& <span class="title">Socket</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> _socket;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Start</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">handle_read</span><span class="params">(<span class="type">const</span> boost::system::error_code & error, <span class="type">size_t</span> bytes_transfered)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">handle_write</span><span class="params">(<span class="type">const</span> boost::system::error_code& error)</span></span>;</span><br><span class="line"> tcp::socket _socket;</span><br><span class="line"> <span class="keyword">enum</span> {max_length = <span class="number">1024</span>};</span><br><span class="line"> <span class="type">char</span> _data[max_length];</span><br><span class="line">};</span><br></pre></td></tr></table></figure><ol><li><code>_data</code>用来接收客户端传递的数据</li><li><code>_socket</code>为单独处理客户端读写的socket。</li><li><code>handle_read</code>和<code>handle_write</code>分别为读回调函数和写回调函数。<br>接下来我们实现Session类<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::Start</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="built_in">memset</span>(_data, <span class="number">0</span>, max_length);</span><br><span class="line"> _socket.<span class="built_in">async_read_some</span>(boost::asio::<span class="built_in">buffer</span>(_data, max_length),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::handle_read, <span class="keyword">this</span>, placeholders::_1,</span><br><span class="line"> placeholders::_2)</span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><p>在Start方法中我们调用异步读操作,监听对端发送的消息。当对端发送数据后,触发<code>handle_read</code>函数</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::handle_read</span><span class="params">(<span class="type">const</span> boost::system::error_code& error, <span class="type">size_t</span> bytes_transfered)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!error) {</span><br><span class="line"> cout << <span class="string">"server receive data is "</span> << _data << endl;</span><br><span class="line"> boost::asio::<span class="built_in">async_write</span>(_socket, boost::asio::<span class="built_in">buffer</span>(_data, bytes_transfered), </span><br><span class="line"> std::<span class="built_in">bind</span>(&Session::handle_write, <span class="keyword">this</span>, placeholders::_1));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">delete</span> <span class="keyword">this</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>handle_read</code>函数内将收到的数据发送给对端,当发送完成后触发<code>handle_write</code>回调函数。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Session::handle_write</span><span class="params">(<span class="type">const</span> boost::system::error_code& error)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!error) {</span><br><span class="line"> <span class="built_in">memset</span>(_data, <span class="number">0</span>, max_length);</span><br><span class="line"> _socket.<span class="built_in">async_read_some</span>(boost::asio::<span class="built_in">buffer</span>(_data, max_length), std::<span class="built_in">bind</span>(&Session::handle_read,</span><br><span class="line"> <span class="keyword">this</span>, placeholders::_1, placeholders::_2));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">delete</span> <span class="keyword">this</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>handle_write</code>函数内又一次监听了读事件,如果对端有数据发送过来则触发handle_read,我们再将收到的数据发回去。从而达到应答式服务的效果。</p><h3 id="Server类"><a href="#Server类" class="headerlink" title="Server类"></a>Server类</h3><p>Server类为服务器接收连接的管理类</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Server</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Server</span>(boost::asio::io_context& ioc, <span class="type">short</span> port);</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">start_accept</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">handle_accept</span><span class="params">(Session* new_session, <span class="type">const</span> boost::system::error_code& error)</span></span>;</span><br><span class="line"> boost::asio::io_context& _ioc;</span><br><span class="line"> tcp::acceptor _acceptor;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><code>start_accept</code>将要接收连接的<code>acceptor</code>绑定到服务上,其内部就是将<code>accpeptor</code>对应的socket描述符绑定到<code>epoll</code>或<code>iocp</code>模型上,实现事件驱动。<br><code>handle_accept</code>为新连接到来后触发的回调函数。<br>下面是具体实现</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Server::<span class="built_in">Server</span>(boost::asio::io_context& ioc, <span class="type">short</span> port) :_ioc(ioc),</span><br><span class="line">_acceptor(ioc, tcp::<span class="built_in">endpoint</span>(tcp::<span class="built_in">v4</span>(), port)) {</span><br><span class="line"> <span class="built_in">start_accept</span>();</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Server::start_accept</span><span class="params">()</span> </span>{</span><br><span class="line"> Session* new_session = <span class="keyword">new</span> <span class="built_in">Session</span>(_ioc);</span><br><span class="line"> _acceptor.<span class="built_in">async_accept</span>(new_session-><span class="built_in">Socket</span>(),</span><br><span class="line"> std::<span class="built_in">bind</span>(&Server::handle_accept, <span class="keyword">this</span>, new_session, placeholders::_1));</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Server::handle_accept</span><span class="params">(Session* new_session, <span class="type">const</span> boost::system::error_code& error)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!error) {</span><br><span class="line"> new_session-><span class="built_in">Start</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">delete</span> new_session;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">start_accept</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 网络编程 </category>
</categories>
<tags>
<tag> 网络编程 </tag>
</tags>
</entry>
<entry>
<title>一起写webserver 项目(五)</title>
<link href="/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%BA%94)/"/>
<url>/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%BA%94)/</url>
<content type="html"><![CDATA[<h2 id="webserver类的封装"><a href="#webserver类的封装" class="headerlink" title="webserver类的封装"></a>webserver类的封装</h2><h3 id="参数和方法"><a href="#参数和方法" class="headerlink" title="参数和方法"></a>参数和方法</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基础</span></span><br><span class="line"><span class="type">int</span> m_close_log; <span class="comment">// 是否关闭日志</span></span><br><span class="line"><span class="type">int</span> m_is_async; <span class="comment">// 日志是否异步</span></span><br><span class="line"><span class="type">char</span> *m_root; <span class="comment">// 我们之前服务器上用于存放网页文件的根目录的路径</span></span><br><span class="line">http_conn *users; <span class="comment">// 对应所有的客户端</span></span><br><span class="line"><span class="type">int</span> m_listen_fd;</span><br><span class="line"><span class="type">int</span> m_TRIGMode;</span><br><span class="line"><span class="type">int</span> m_LISTENTrigmode;</span><br><span class="line"><span class="type">int</span> m_CONNTrigmode;</span><br><span class="line"><span class="type">int</span> m_opt_linger;</span><br><span class="line"><span class="type">int</span> m_epoll_fd;</span><br><span class="line"><span class="type">int</span> m_pipefd[<span class="number">2</span>];</span><br><span class="line">epoll_event events[MAX_EVENT_NUMBER];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据库相关</span></span><br><span class="line">connection_pool *m_sql_pool;</span><br><span class="line"><span class="type">int</span> m_port;</span><br><span class="line">string m_user;</span><br><span class="line">string m_password;</span><br><span class="line">string m_dbName;</span><br><span class="line"><span class="type">int</span> m_sql_num;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程池相关</span></span><br><span class="line">thread_pool<http_conn> *m_pool;</span><br><span class="line"><span class="type">int</span> m_actormodel;</span><br><span class="line"><span class="type">int</span> m_thread_num;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定时相关</span></span><br><span class="line">client_data *user_timer;</span><br><span class="line">Utils utils;</span><br></pre></td></tr></table></figure><h3 id="初始化各模块"><a href="#初始化各模块" class="headerlink" title="初始化各模块"></a>初始化各模块</h3><p>WebServer()</p><ul><li>这里有初始化uers对象</li><li>得到服务项目路径用getcwd方法,初始化m_root,结合服务路径得到m_root</li><li>初始化定时器</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Webserver::<span class="built_in">Webserver</span>() {</span><br><span class="line"> <span class="comment">//http_conn类对象</span></span><br><span class="line"> users=<span class="keyword">new</span> http_conn[MAX_FD];</span><br><span class="line"></span><br><span class="line"> <span class="comment">//root文件夹路径</span></span><br><span class="line"> <span class="type">char</span> server_path[<span class="number">200</span>];</span><br><span class="line"> <span class="built_in">getcwd</span>(server_path,<span class="number">200</span>);</span><br><span class="line"> <span class="type">char</span> root[<span class="number">6</span>]=<span class="string">"/root"</span>;</span><br><span class="line"> m_root=(<span class="type">char</span> *) <span class="built_in">malloc</span>(<span class="built_in">strlen</span>(root)+ <span class="built_in">strlen</span>(server_path)+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_root,server_path);</span><br><span class="line"> <span class="built_in">strcat</span>(m_root,root);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定时器</span></span><br><span class="line"> user_timer=<span class="keyword">new</span> client_data[MAX_FD];</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>~WebServer</p><ul><li>关闭用到的各种文件描述符</li><li>删除创建在堆上的内存</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Webserver::~<span class="built_in">Webserver</span>() {</span><br><span class="line"> <span class="built_in">close</span>(m_epoll_fd);</span><br><span class="line"> <span class="built_in">close</span>(m_pipefd[<span class="number">0</span>]);</span><br><span class="line"> <span class="built_in">close</span>(m_pipefd[<span class="number">1</span>]);</span><br><span class="line"> <span class="built_in">close</span>(m_listen_fd);</span><br><span class="line"> <span class="keyword">delete</span>[] users;</span><br><span class="line"> <span class="keyword">delete</span>[] user_timer;</span><br><span class="line"> <span class="keyword">delete</span> m_pool;</span><br><span class="line"> <span class="built_in">free</span>(m_root);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>init</p><ul><li>初始化各参数</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::init</span><span class="params">(<span class="type">int</span> port, string user, string passwd, string databaseName, <span class="type">int</span> log_write, <span class="type">int</span> opt_linger, <span class="type">int</span> trigmode, <span class="type">int</span> sql_num, <span class="type">int</span> thread_num, <span class="type">int</span> close_log, <span class="type">int</span> actor_model)</span> </span>{</span><br><span class="line"> m_port=port;</span><br><span class="line"> m_user=user;</span><br><span class="line"> m_password=passwd;</span><br><span class="line"> m_dbName=databaseName;</span><br><span class="line"> m_is_async=log_write;</span><br><span class="line"> m_opt_linger=opt_linger;</span><br><span class="line"> m_TRIGMode=trigmode;</span><br><span class="line"> m_sql_num=sql_num;</span><br><span class="line"> m_thread_num=thread_num;</span><br><span class="line"> m_close_log=close_log;</span><br><span class="line"> m_actormodel=actor_model;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="初始化日志"><a href="#初始化日志" class="headerlink" title="初始化日志"></a>初始化日志</h3><ul><li>根据是否和是否异步日志用单例创建日志</li><li>file_name=”./ServerLog”log_buf_size=2000,split_line=800000,异步的max_queue_size=800</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::log_write</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(m_close_log==<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">if</span>(m_is_async==<span class="number">0</span>){</span><br><span class="line"> Log::<span class="built_in">get_instance</span>()-><span class="built_in">init</span>(<span class="string">"./ServerLog"</span>,m_close_log,<span class="number">2000</span>,<span class="number">800000</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(m_is_async==<span class="number">1</span>){</span><br><span class="line"> Log::<span class="built_in">get_instance</span>()-><span class="built_in">init</span>(<span class="string">"./ServerLog"</span>,m_close_log,<span class="number">2000</span>,<span class="number">800000</span>,<span class="number">800</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="初始化数据库连接池"><a href="#初始化数据库连接池" class="headerlink" title="初始化数据库连接池"></a>初始化数据库连接池</h3><ul><li>单例创建连接池对象</li><li>初始化连接池对象,url为localhost</li><li>调用http的initmysql_result初始化数据库读取表</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::sql_pool</span><span class="params">()</span> </span>{</span><br><span class="line"> m_sql_pool = connection_pool::<span class="built_in">GetInstance</span>();</span><br><span class="line"> m_sql_pool-><span class="built_in">init</span>(<span class="string">"localhost"</span>,<span class="number">3306</span>,m_user,m_password,m_dbName,m_close_log,m_sql_num);</span><br><span class="line"> users-><span class="built_in">initmysql_result</span>(m_sql_pool);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="初始化线程池"><a href="#初始化线程池" class="headerlink" title="初始化线程池"></a>初始化线程池</h3><ul><li>初始化线程池</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::threadPool</span><span class="params">()</span> </span>{</span><br><span class="line"> m_pool=<span class="keyword">new</span> <span class="built_in">thread_pool</span><http_conn>(m_actormodel,m_sql_pool,m_thread_num);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="模式选择-trig-mode"><a href="#模式选择-trig-mode" class="headerlink" title="模式选择 trig_mode()"></a>模式选择 trig_mode()</h3><p>有4中模式分别是 第一个是监听的模式,第二个连接</p><ol><li>LT+LT m_TRIGMode=0</li><li>LT+ET m_TRIGMode=1</li><li>ET+LT m_TRIGMode=2</li><li>ET+ET m_TRIGMode=3</li></ol><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::trig_mode</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_TRIGMode==<span class="number">0</span>){ <span class="comment">// LT+LT</span></span><br><span class="line"> m_LISTENTrigmode=<span class="number">0</span>;</span><br><span class="line"> m_CONNTrigmode=<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_TRIGMode==<span class="number">1</span>){ <span class="comment">// LT+ET</span></span><br><span class="line"> m_LISTENTrigmode=<span class="number">0</span>;</span><br><span class="line"> m_CONNTrigmode=<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_TRIGMode==<span class="number">2</span>){ <span class="comment">// ET+LT</span></span><br><span class="line"> m_LISTENTrigmode=<span class="number">1</span>;</span><br><span class="line"> m_CONNTrigmode=<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_TRIGMode==<span class="number">3</span>){ <span class="comment">// ET+ET</span></span><br><span class="line"> m_LISTENTrigmode=<span class="number">1</span>;</span><br><span class="line"> m_CONNTrigmode=<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="eventListen"><a href="#eventListen" class="headerlink" title="eventListen"></a>eventListen</h3><ul><li>这是一个正常的正常的创建listenfd、绑定、监听流程</li><li>我们得到监听的套字节之后要设置是否是优雅关闭,用setsockopt方法,optname=SO_LINGER,optval创一个结构linger 分别是{0,1}和{1,1}</li><li>绑定地址后还要设置reuseraddr</li><li>创建m_epollfd,这个是http类和抽象工具类Utils的变量,初始化一下</li><li>调用抽象工具类Utils的添加信号方法,分别是SIGPIPE、SIGALRM、SIGTEAM,调用alarm</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::eventListen</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//网络编程基础步骤</span></span><br><span class="line"> m_listen_fd = <span class="built_in">socket</span>(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">assert</span>(m_listen_fd>=<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//优雅关闭连接</span></span><br><span class="line"> <span class="keyword">if</span>(m_opt_linger==<span class="number">0</span>){ <span class="comment">// 优雅关闭连接</span></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">linger</span> tmp{<span class="number">0</span>,<span class="number">1</span>};</span><br><span class="line"> <span class="built_in">setsockopt</span>(m_listen_fd,SOL_SOCKET,SO_LINGER,&tmp, <span class="built_in">sizeof</span>(tmp));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(m_opt_linger==<span class="number">1</span>){ <span class="comment">// 不优雅</span></span><br><span class="line"> <span class="keyword">struct</span> linger tmp{<span class="number">1</span>,<span class="number">1</span>};</span><br><span class="line"> <span class="built_in">setsockopt</span>(m_listen_fd,SOL_SOCKET,SO_LINGER,&tmp, <span class="built_in">sizeof</span>(tmp));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> ret=<span class="number">0</span>;</span><br><span class="line"> sockaddr_in address{};</span><br><span class="line"> <span class="built_in">bzero</span>(&address, <span class="built_in">sizeof</span>(address));</span><br><span class="line"> address.sin_port= <span class="built_in">htons</span>(m_port);</span><br><span class="line"> address.sin_family=AF_INET;</span><br><span class="line"> address.sin_addr.s_addr= <span class="built_in">htonl</span>(INADDR_ANY);</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> flag=<span class="number">1</span>;</span><br><span class="line"> <span class="built_in">setsockopt</span>(m_listen_fd,SOL_SOCKET,SO_REUSEADDR,&flag, <span class="built_in">sizeof</span>(flag));</span><br><span class="line"> ret= <span class="built_in">bind</span>(m_listen_fd, <span class="built_in">reinterpret_cast</span><<span class="type">const</span> sockaddr *>(&address), <span class="built_in">sizeof</span>(address));</span><br><span class="line"> <span class="built_in">assert</span>(ret>=<span class="number">0</span>);</span><br><span class="line"> ret= <span class="built_in">listen</span>(m_listen_fd,<span class="number">5</span>);</span><br><span class="line"> <span class="built_in">assert</span>(ret>=<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> utils.<span class="built_in">init</span>(TIMESLOT);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//epoll创建内核事件表</span></span><br><span class="line"> <span class="comment">// epoll_event events[MAX_EVENT_NUMBER];</span></span><br><span class="line"> m_epoll_fd=<span class="built_in">epoll_create</span>(<span class="number">5</span>);</span><br><span class="line"> <span class="built_in">assert</span>(m_epoll_fd!=<span class="number">-1</span>);</span><br><span class="line"></span><br><span class="line"> utils.<span class="built_in">addfd</span>(m_epoll_fd,m_listen_fd, <span class="literal">false</span>,m_LISTENTrigmode);</span><br><span class="line"></span><br><span class="line"> ret= <span class="built_in">socketpair</span>(PF_UNIX,SOCK_STREAM,<span class="number">0</span>,m_pipefd);</span><br><span class="line"> <span class="built_in">assert</span>(ret!=<span class="number">-1</span>);</span><br><span class="line"> utils.<span class="built_in">setnonblocking</span>(m_pipefd[<span class="number">1</span>]);</span><br><span class="line"> utils.<span class="built_in">addfd</span>(m_epoll_fd,m_pipefd[<span class="number">0</span>], <span class="literal">false</span>,<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> utils.<span class="built_in">addsig</span>(SIGPIPE,SIG_IGN);</span><br><span class="line"> utils.<span class="built_in">addsig</span>(SIGALRM,Utils::sig_handler, <span class="literal">false</span>);</span><br><span class="line"> utils.<span class="built_in">addsig</span>(SIGTERM,Utils::sig_handler, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">alarm</span>(TIMESLOT);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//工具类,信号和描述符基础操作</span></span><br><span class="line"> http_conn::m_epoll_fd=m_epoll_fd;</span><br><span class="line"> Utils::u_epollfd=m_epoll_fd;</span><br><span class="line"> Utils::u_pipefd=m_pipefd;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="eventLoop"><a href="#eventLoop" class="headerlink" title="eventLoop()"></a>eventLoop()</h3><ul><li>这是一个正常的事件循环方式在while调用epoll_wait,关注顺序分别是监听、异常、信号、读写事件</li><li>循环我们也要检测是否超时,超时了触发抽象工具类的定时处理任务,超时通过timeout这个函数</li><li>这里有dealclientdata()、deal_timer、dealwithsignal、dealwithread、dealwithwrite分别对应监听、异常、信号、读写事件所要处理的逻辑</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::eventLoop</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">bool</span> stop_server=<span class="literal">false</span>;</span><br><span class="line"> <span class="type">bool</span> timeout=<span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">while</span>(!stop_server){</span><br><span class="line"> <span class="type">int</span> num= <span class="built_in">epoll_wait</span>(m_epoll_fd,events,MAX_EVENT_NUMBER,<span class="number">-1</span>);</span><br><span class="line"> <span class="keyword">if</span>(num<<span class="number">0</span>&&errno!=EINTR){</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s"</span>,<span class="string">"epoll failure"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<num;i++){</span><br><span class="line"> <span class="type">int</span> sock_fd= events[i].data.fd;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//处理新到的客户连接</span></span><br><span class="line"> <span class="keyword">if</span>(sock_fd==m_listen_fd){</span><br><span class="line"> <span class="type">bool</span> flag=<span class="built_in">dealclientdata</span>();</span><br><span class="line"> <span class="keyword">if</span>(!flag) <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(events[i].events&(EPOLLRDHUP|EPOLLHUP|EPOLLERR)){</span><br><span class="line"> <span class="comment">//服务器端关闭连接,移除对应的定时器</span></span><br><span class="line"> util_timer *timer= user_timer[sock_fd].timer;</span><br><span class="line"> <span class="built_in">deal_timer</span>(timer,sock_fd);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>((sock_fd==m_pipefd[<span class="number">0</span>])&&(events[i].events&EPOLLIN)){ <span class="comment">//处理信号</span></span><br><span class="line"> <span class="type">bool</span> flag= <span class="built_in">dealwithsignal</span>(timeout,stop_server);</span><br><span class="line"> <span class="keyword">if</span>(!flag)</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s"</span>, <span class="string">"dealclientdata failure"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(events[i].events&EPOLLIN){ <span class="comment">//处理客户连接上接收到的数据</span></span><br><span class="line"> <span class="built_in">dealwithread</span>(sock_fd);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(events[i].events&EPOLLOUT){</span><br><span class="line"> <span class="built_in">dealwithwrite</span>(sock_fd);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(timeout){</span><br><span class="line"> utils.<span class="built_in">time_handler</span>();</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"%s"</span>, <span class="string">"timer tick"</span>);</span><br><span class="line"> timeout= <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="dealclientdata"><a href="#dealclientdata" class="headerlink" title="dealclientdata()"></a>dealclientdata()</h3><p>处理新连接</p><ul><li>这就是一个处理的连接的代码,核心就是调用accept方法</li><li>LT模式我们调用一次accept,异常有两种,一种就是accpet返回了错误负值,因为我们的监听的描述符是用的非阻塞方式,另外一种就是连接数量超过了设置的最大限制</li><li>ET模式我们就是循环调用accept</li><li>成功我们都会调用timer方法,因为这个时候我们已经得到新连接的客户端的基本信息,我们用来初始化http_conn类,定时器类client_data,特别初始化这个util_timer类,连接的超时是当前时间+3倍的timeslot,这个时候也要添加到升序链表的定时器。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Webserver::dealclientdata</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> sockaddr_in client_address{};</span><br><span class="line"> <span class="type">socklen_t</span> client_len= <span class="built_in">sizeof</span>(client_address);</span><br><span class="line"> <span class="keyword">if</span>(m_LISTENTrigmode==<span class="number">0</span>){ <span class="comment">// LT</span></span><br><span class="line"> <span class="type">int</span> conn_fd=<span class="built_in">accept</span>(m_listen_fd, <span class="built_in">reinterpret_cast</span><sockaddr *>(&client_address),&client_len);</span><br><span class="line"> <span class="keyword">if</span>(conn_fd<<span class="number">0</span>){</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s:errno is:%d"</span>, <span class="string">"accept error"</span>, errno);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(http_conn::m_user_count>=MAX_FD){</span><br><span class="line"> utils.<span class="built_in">show_errno</span>(conn_fd, <span class="string">"Internal server busy"</span>);</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s"</span>, <span class="string">"Internal server busy"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">timer</span>(conn_fd,client_address);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">//ET</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>){</span><br><span class="line"> <span class="type">int</span> conn_fd=<span class="built_in">accept</span>(m_listen_fd, <span class="built_in">reinterpret_cast</span><sockaddr *>(&client_address),&client_len);</span><br><span class="line"> <span class="keyword">if</span>(conn_fd<<span class="number">0</span>){</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s:errno is:%d"</span>, <span class="string">"accept error"</span>, errno);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(http_conn::m_user_count>=MAX_FD){</span><br><span class="line"> utils.<span class="built_in">show_errno</span>(conn_fd, <span class="string">"Internal server busy"</span>);</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"%s"</span>, <span class="string">"Internal server busy"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">timer</span>(conn_fd,client_address);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::timer</span><span class="params">(<span class="type">int</span> conn_fd, <span class="keyword">struct</span> sockaddr_in client_address)</span> </span>{</span><br><span class="line"> users[conn_fd].<span class="built_in">init</span>(conn_fd,client_address,m_root,m_CONNTrigmode,m_close_log,m_user,m_password,m_dbName);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//初始化client_data数据</span></span><br><span class="line"> <span class="comment">//创建定时器,设置回调函数和超时时间,绑定用户数据,将定时器添加到链表中</span></span><br><span class="line"> user_timer[conn_fd].address=client_address;</span><br><span class="line"> user_timer[conn_fd].sockfd=conn_fd;</span><br><span class="line"> <span class="keyword">auto</span> *timer=<span class="keyword">new</span> util_timer;</span><br><span class="line"> timer->user_data=&user_timer[conn_fd];</span><br><span class="line"> timer->cb_func=cb_func;</span><br><span class="line"> <span class="type">time_t</span> cur= <span class="built_in">time</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> timer->expire=cur+<span class="number">3</span>*TIMESLOT;</span><br><span class="line"> user_timer[conn_fd].timer=timer;</span><br><span class="line"> utils.m_time_lst.<span class="built_in">add_timer</span>(timer);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="deal-timer"><a href="#deal-timer" class="headerlink" title="deal_timer"></a>deal_timer</h3><p>就是删除这个链接不再监视它,并且从升序链表的定时器中删除,这里的删除我们用之前定义的回调函数cb_func</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::deal_timer</span><span class="params">(util_timer *timer, <span class="type">int</span> sockfd)</span> </span>{</span><br><span class="line"> timer-><span class="built_in">cb_func</span>(&user_timer[sockfd]);</span><br><span class="line"> <span class="keyword">if</span>(timer){</span><br><span class="line"> utils.m_time_lst.<span class="built_in">del_timer</span>(timer);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"close fd %d"</span>,user_timer[sockfd].sockfd);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="dealwithsignal"><a href="#dealwithsignal" class="headerlink" title="dealwithsignal"></a>dealwithsignal</h3><ul><li>主要就是调用recv函数,然后判断是那种信号,SIGALRM超时标志改为true,SIGTERM停止服务标志改为false</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Webserver::dealwithsignal</span><span class="params">(<span class="type">bool</span> &timeout, <span class="type">bool</span> &stop_server)</span> </span>{</span><br><span class="line"> <span class="type">char</span> signals[<span class="number">1024</span>];</span><br><span class="line"> <span class="type">int</span> ret=<span class="built_in">recv</span>(m_pipefd[<span class="number">0</span>],signals, <span class="built_in">sizeof</span>(signals),<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(ret<=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<ret;++i){</span><br><span class="line"> <span class="keyword">switch</span> (signals[i]) {</span><br><span class="line"> <span class="keyword">case</span> SIGALRM:{</span><br><span class="line"> timeout= <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> SIGTERM:</span><br><span class="line"> {</span><br><span class="line"> stop_server= <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="dealwithread"><a href="#dealwithread" class="headerlink" title="dealwithread"></a>dealwithread</h3><p>读事件设计了reactor和proctor两种模式,不过这里的reactor等待子线程IO完成的设计好像有点问题,主线程似乎会跟着罚站,不知道有没有什么优化方法</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::dealwithread</span><span class="params">(<span class="type">int</span> sockfd)</span> </span>{</span><br><span class="line"> util_timer *timer=user_timer[sockfd].timer;</span><br><span class="line"> <span class="keyword">if</span>(m_actormodel==<span class="number">1</span>){ <span class="comment">//reactor</span></span><br><span class="line"> <span class="keyword">if</span>(timer){</span><br><span class="line"> <span class="built_in">adjust_timer</span>(timer);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//若监测到读事件,将该事件放入请求队列</span></span><br><span class="line"> m_pool-><span class="built_in">append</span>(users + sockfd, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span> == users[sockfd].improv)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span> == users[sockfd].timer_flag)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">deal_timer</span>(timer, sockfd);</span><br><span class="line"> users[sockfd].timer_flag = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> users[sockfd].improv = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">// proactor</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(users[sockfd].<span class="built_in">read_once</span>()){</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"deal with the client(%s)"</span>, <span class="built_in">inet_ntoa</span>(users[sockfd].<span class="built_in">get_address</span>()->sin_addr));</span><br><span class="line"></span><br><span class="line"> <span class="comment">//若监测到读事件,将该事件放入请求队列</span></span><br><span class="line"> m_pool-><span class="built_in">append_p</span>(users + sockfd);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (timer)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">adjust_timer</span>(timer);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">deal_timer</span>(timer, sockfd);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="dealwithwrite"><a href="#dealwithwrite" class="headerlink" title="dealwithwrite"></a>dealwithwrite</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Webserver::dealwithwrite</span><span class="params">(<span class="type">int</span> sockfd)</span> </span>{</span><br><span class="line"> util_timer *timer = user_timer[sockfd].timer;</span><br><span class="line"> <span class="comment">//reactor</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span> == m_actormodel)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (timer)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">adjust_timer</span>(timer);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m_pool-><span class="built_in">append</span>(users + sockfd, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span> == users[sockfd].improv)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="number">1</span> == users[sockfd].timer_flag)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">deal_timer</span>(timer, sockfd);</span><br><span class="line"> users[sockfd].timer_flag = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> users[sockfd].improv = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//proactor</span></span><br><span class="line"> <span class="keyword">if</span> (users[sockfd].<span class="built_in">write</span>())</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"send data to the client(%s)"</span>, <span class="built_in">inet_ntoa</span>(users[sockfd].<span class="built_in">get_address</span>()->sin_addr));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (timer)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">adjust_timer</span>(timer);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">deal_timer</span>(timer, sockfd);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="配置文件,服务器,启动!"><a href="#配置文件,服务器,启动!" class="headerlink" title="配置文件,服务器,启动!"></a>配置文件,服务器,启动!</h2><h3 id="配置文件和main文件"><a href="#配置文件和main文件" class="headerlink" title="配置文件和main文件"></a>配置文件和main文件</h3><p>配置文件和main文件就不讲了,直接贴代码,感兴趣的同学自己看看</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> CONFIG_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_H</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"webserver.h"</span></span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Config</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Config</span>();</span><br><span class="line"> ~<span class="built_in">Config</span>(){};</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">parse_arg</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>*argv[])</span></span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//端口号</span></span><br><span class="line"> <span class="type">int</span> PORT;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//日志写入方式</span></span><br><span class="line"> <span class="type">int</span> LOGWrite;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//触发组合模式</span></span><br><span class="line"> <span class="type">int</span> TRIGMode;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//listenfd触发模式</span></span><br><span class="line"> <span class="type">int</span> LISTENTrigmode;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//connfd触发模式</span></span><br><span class="line"> <span class="type">int</span> CONNTrigmode;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//优雅关闭链接</span></span><br><span class="line"> <span class="type">int</span> OPT_LINGER;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//数据库连接池数量</span></span><br><span class="line"> <span class="type">int</span> sql_num;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//线程池内的线程数量</span></span><br><span class="line"> <span class="type">int</span> thread_num;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//是否关闭日志</span></span><br><span class="line"> <span class="type">int</span> close_log;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//并发模型选择</span></span><br><span class="line"> <span class="type">int</span> actor_model;</span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"config.h"</span></span></span><br><span class="line"> </span><br><span class="line">Config::<span class="built_in">Config</span>(){</span><br><span class="line"> <span class="comment">//端口号,默认9006</span></span><br><span class="line"> PORT = <span class="number">9006</span>;</span><br><span class="line"> <span class="comment">//PORT = 9000;</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">//日志写入方式,默认同步</span></span><br><span class="line"> LOGWrite = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//触发组合模式,默认listenfd LT + connfd LT</span></span><br><span class="line"> TRIGMode = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//listenfd触发模式,默认LT</span></span><br><span class="line"> LISTENTrigmode = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//connfd触发模式,默认LT</span></span><br><span class="line"> CONNTrigmode = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//优雅关闭链接,默认不使用</span></span><br><span class="line"> OPT_LINGER = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//数据库连接池数量,默认8</span></span><br><span class="line"> sql_num = <span class="number">8</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//线程池内的线程数量,默认8</span></span><br><span class="line"> thread_num = <span class="number">8</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//关闭日志,默认不关闭</span></span><br><span class="line"> close_log = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//并发模型,默认是proactor</span></span><br><span class="line"> actor_model = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Config::parse_arg</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>*argv[])</span></span>{</span><br><span class="line"> <span class="type">int</span> opt;</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *str = <span class="string">"p:l:m:o:s:t:c:a:"</span>;</span><br><span class="line"> <span class="keyword">while</span> ((opt = <span class="built_in">getopt</span>(argc, argv, str)) != <span class="number">-1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> (opt)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'p'</span>:</span><br><span class="line"> {</span><br><span class="line"> PORT = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'l'</span>:</span><br><span class="line"> {</span><br><span class="line"> LOGWrite = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'m'</span>:</span><br><span class="line"> {</span><br><span class="line"> TRIGMode = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'o'</span>:</span><br><span class="line"> {</span><br><span class="line"> OPT_LINGER = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line"> {</span><br><span class="line"> sql_num = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'t'</span>:</span><br><span class="line"> {</span><br><span class="line"> thread_num = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'c'</span>:</span><br><span class="line"> {</span><br><span class="line"> close_log = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'a'</span>:</span><br><span class="line"> {</span><br><span class="line"> actor_model = <span class="built_in">atoi</span>(optarg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"config.h"</span></span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">//需要修改的数据库信息,登录名,密码,库名</span></span><br><span class="line"> string user = <span class="string">"root"</span>;</span><br><span class="line"> string passwd = <span class="string">"123"</span>;</span><br><span class="line"> string databasename = <span class="string">"testDB"</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//命令行解析</span></span><br><span class="line"> Config config;</span><br><span class="line"> config.<span class="built_in">parse_arg</span>(argc, argv);</span><br><span class="line"> </span><br><span class="line"> WebServer server;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//初始化</span></span><br><span class="line"> server.<span class="built_in">init</span>(config.PORT, user, passwd, databasename, config.LOGWrite, </span><br><span class="line"> config.OPT_LINGER, config.TRIGMode, config.sql_num, config.thread_num, </span><br><span class="line"> config.close_log, config.actor_model);</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">//日志</span></span><br><span class="line"> server.<span class="built_in">log_write</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//数据库</span></span><br><span class="line"> server.<span class="built_in">sql_pool</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//线程池</span></span><br><span class="line"> server.<span class="built_in">thread_pool</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//触发模式</span></span><br><span class="line"> server.<span class="built_in">trig_mode</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//监听</span></span><br><span class="line"> server.<span class="built_in">eventListen</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//运行</span></span><br><span class="line"> server.<span class="built_in">eventLoop</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>个性化运⾏(使⽤⾃⼰的参数运⾏)</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">./server [-p port] [-l LOGWrite] [-m TRIGMode] [-o OPT_LINGER] [-s sql_num] [-t</span><br><span class="line">thread_num] [-c close_log] [-a actor_model]</span><br></pre></td></tr></table></figure><ul><li>-p,⾃定义端⼝号<ul><li>默认9006</li></ul></li><li>-l,选择⽇志写⼊⽅式,默认同步写⼊<ul><li>0,同步写⼊</li><li>1,异步写⼊</li></ul></li><li>-m,listenfd和connfd的模式组合,默认使⽤LT + LT<ul><li>0,表示使⽤LT + LT</li><li>1,表示使⽤LT + ET</li><li>2,表示使⽤ET + LT</li><li>3,表示使⽤ET + ET</li></ul></li><li>-o,优雅关闭连接,默认不使⽤<ul><li>0,不使⽤</li><li>1,使⽤</li></ul></li><li>-s,数据库连接数量<ul><li>默认为8</li><li>-t,线程数量</li><li>默认为8</li></ul></li><li>-c,关闭⽇志,默认打开<ul><li>0,打开⽇志</li><li>1,关闭⽇志</li></ul></li><li>-a,选择反应堆模型,默认Proactor<ul><li>0,Proactor模型</li><li>1,Reactor模型</li></ul></li></ul><h3 id="压力测试"><a href="#压力测试" class="headerlink" title="压力测试"></a>压力测试</h3><h4 id="webbench原理"><a href="#webbench原理" class="headerlink" title="webbench原理"></a>webbench原理</h4><blockquote><p>webbench是一个开源的用来测试服务器性能的小软件,其基本原理是创建多个子进程进行服务器访问,子进程将自己得到的数据通过管道发给主进程,主进程将结果统计打印输出在屏幕上</p></blockquote><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430212039.png" alt="image.png" style="zoom:60%;" /><h4 id="测试结果"><a href="#测试结果" class="headerlink" title="测试结果"></a>测试结果</h4><ul><li><p>Proactor LT+LT 40274.4QPS</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">yesho@:~/Code/C++/CLiondemo/Webserver/test_pressure/webbench-1.5$ ./webbench -c 10500 -t 5 http://127.0.0.1:9006/</span><br><span class="line">Webbench - Simple Web Benchmark 1.5</span><br><span class="line">Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.</span><br><span class="line"></span><br><span class="line">Benchmarking: GET http://127.0.0.1:9006/</span><br><span class="line">10500 clients, running 5 sec.</span><br><span class="line"></span><br><span class="line">Speed=2416464 pages/min, 4510733 bytes/sec.</span><br><span class="line">Requests: 201372 susceed, 0 failed.</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>Proactor LT+ET</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">yesho@:~/Code/C++/CLiondemo/Webserver/test_pressure/webbench-1.5$ ./webbench -c 10500 -t 5 http://127.0.0.1:9006/</span><br><span class="line">Webbench - Simple Web Benchmark 1.5</span><br><span class="line">Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.</span><br><span class="line"></span><br><span class="line">Benchmarking: GET http://127.0.0.1:9006/</span><br><span class="line">10500 clients, running 5 sec.</span><br><span class="line"></span><br><span class="line">Speed=1728564 pages/min, 3226652 bytes/sec.</span><br><span class="line">Requests: 144047 susceed, 0 failed.</span><br></pre></td></tr></table></figure></li><li><p>Proactor ET+LT</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">yesho@:~/Code/C++/CLiondemo/Webserver/test_pressure/webbench-1.5$ ./webbench -c 10500 -t 5 http://127.0.0.1:9006/</span><br><span class="line">Webbench - Simple Web Benchmark 1.5</span><br><span class="line">Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.</span><br><span class="line"></span><br><span class="line">Benchmarking: GET http://127.0.0.1:9006/</span><br><span class="line">10500 clients, running 5 sec.</span><br><span class="line"></span><br><span class="line">Speed=1585944 pages/min, 2960406 bytes/sec.</span><br><span class="line">Requests: 132162 susceed, 0 failed.</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>Proactor ET+ET</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">yesho@:~/Code/C++/CLiondemo/Webserver/test_pressure/webbench-1.5$ ./webbench -c 10500 -t 5 http://127.0.0.1:9006/</span><br><span class="line">Webbench - Simple Web Benchmark 1.5</span><br><span class="line">Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.</span><br><span class="line"></span><br><span class="line">Benchmarking: GET http://127.0.0.1:9006/</span><br><span class="line">10500 clients, running 5 sec.</span><br><span class="line"></span><br><span class="line">Speed=1749768 pages/min, 3266233 bytes/sec.</span><br><span class="line">Requests: 145814 susceed, 0 failed.</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>Reactor LT+LT</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">yesho@:~/Code/C++/CLiondemo/Webserver/test_pressure/webbench-1.5$ ./webbench -c 10500 -t 5 http://127.0.0.1:9006/</span><br><span class="line">Webbench - Simple Web Benchmark 1.5</span><br><span class="line">Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.</span><br><span class="line"></span><br><span class="line">Benchmarking: GET http://127.0.0.1:9006/</span><br><span class="line">10500 clients, running 5 sec.</span><br><span class="line"></span><br><span class="line">Speed=1150344 pages/min, 2147913 bytes/sec.</span><br><span class="line">Requests: 95862 susceed, 0 failed.</span><br></pre></td></tr></table></figure></li></ul><p>项目地址: <a href="https://github.com/YeSho-cpp/My-webserver">https://github.com/YeSho-cpp/My-webserver</a></p>]]></content>
<categories>
<category> 一起做项目 </category>
</categories>
<tags>
<tag> 项目 </tag>
</tags>
</entry>
<entry>
<title>一起写webserver 项目(四)</title>
<link href="/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E5%9B%9B)/"/>
<url>/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E5%9B%9B)/</url>
<content type="html"><![CDATA[<h2 id="http连接的封装"><a href="#http连接的封装" class="headerlink" title="http连接的封装"></a>http连接的封装</h2><p>先前我们写的线程,数据库池或者底层的小工具如locker锁等东西是地基,是骨架,那么http解析部分就是主体,是血肉。这是这个项目最关键的部分。</p><blockquote><p>下面是一张简易的框架图,首先说明一下这个WebServer的本质,WebServer的本质上是⼀个<strong>⾼性能⽹络框架</strong>,它提供了⼀个单服务端(当然也可以扩展为多服务端)与多客户端的⾼效连接框架,但是客户端与服务端连接上以后具体应该做些什么(也就是有哪些业务),这就可以由我们⾃由发挥了,这就是 WebServer 的功能扩展。⽬前⼤多数的WebServer都将从服务端获取<strong>MIME</strong>作为主要功能。</p></blockquote><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430204114.png " alt="image.png" style="zoom:80%;" /><p>由于变量和参数过多,先不介绍初始化了,先从设计讲起。</p><h3 id="http-conn的设计"><a href="#http-conn的设计" class="headerlink" title="http_conn的设计"></a>http_conn的设计</h3><p>其中大部分设计都是参考了《Linux高性能服务器编程》</p><h4 id="固定的一些方法和变量"><a href="#固定的一些方法和变量" class="headerlink" title="固定的一些方法和变量"></a>固定的一些方法和变量</h4><p>定义http响应的一些状态信息</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">char</span> *ok_200_title = <span class="string">"OK"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_400_title = <span class="string">"Bad Request"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_400_form = <span class="string">"Your request has bad syntax or is inherently impossible to staisfy.\n"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_403_title = <span class="string">"Forbidden"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_403_form = <span class="string">"You do not have permission to get file form this server.\n"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_404_title = <span class="string">"Not Found"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_404_form = <span class="string">"The requested file was not found on this server.\n"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_500_title = <span class="string">"Internal Error"</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *error_500_form = <span class="string">"There was an unusual problem serving the request file.\n"</span>;</span><br></pre></td></tr></table></figure><p>类内的方法枚举,</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">METHOD</span></span><br><span class="line">{</span><br><span class="line">GET = <span class="number">0</span>,</span><br><span class="line">POST,</span><br><span class="line">HEAD,</span><br><span class="line">PUT,</span><br><span class="line">DELETE,</span><br><span class="line">TRACE,</span><br><span class="line">OPTIONS,</span><br><span class="line">CONNECT,</span><br><span class="line">PATCH</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>分析状态,这些状态是解析请求的不同阶段,所有的完成后我们才能回应响应</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">CHECK_STATE</span></span><br><span class="line">{</span><br><span class="line"> CHECK_STATE_REQUESTLINE = <span class="number">0</span>, <span class="comment">// 当前正在分析请求行</span></span><br><span class="line"> CHECK_STATE_HEADER, <span class="comment">// 当前正在分析请求头</span></span><br><span class="line"> CHECK_STATE_CONTENT <span class="comment">// 当前正在分析请求体</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>请求结果枚举</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">HTTP_CODE</span></span><br><span class="line">{</span><br><span class="line"> NO_REQUEST, <span class="comment">// 表示请求不完整,需要继续读取客户数据 这是解析请求的不同阶段的默认返回值</span></span><br><span class="line"> GET_REQUEST, <span class="comment">// 表示获得了一个完整的客户请求 只有这个完成后我们才能进行</span></span><br><span class="line"> BAD_REQUEST, <span class="comment">// 表示客户请求有语法错误 </span></span><br><span class="line"> NO_RESOURCE, <span class="comment">// 请求的资源不存在</span></span><br><span class="line"> FORBIDDEN_REQUEST, <span class="comment">// 表示客户对资源没有足够的访问权限</span></span><br><span class="line"> FILE_REQUEST, <span class="comment">// 这是一个文件请求</span></span><br><span class="line"> INTERNAL_ERROR, <span class="comment">// 表示服务器内部错误;</span></span><br><span class="line"> CLOSED_CONNECTION <span class="comment">// 表示客户端已经关闭连接了</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>状态行</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">LINE_STATUS</span></span><br><span class="line">{</span><br><span class="line"> LINE_OK = <span class="number">0</span>,</span><br><span class="line"> LINE_BAD,</span><br><span class="line"> LINE_OPEN</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>一些固定常用方法,具体的实现就不展示了,也可以在《Linux高性能服务器编程》找到</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span> <span class="comment">// 对文件描述符设置非阻塞</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">addfd</span><span class="params">(<span class="type">int</span> epollfd, <span class="type">int</span> fd, <span class="type">bool</span> one_shot, <span class="type">int</span> TRIGMode)</span> <span class="comment">//将内核事件表注册读事件,ET模式,选择开启EPOLLONESHOT</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">removefd</span><span class="params">(<span class="type">int</span> epollfd, <span class="type">int</span> fd)</span> <span class="comment">//从内核时间表删除描述符</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">modfd</span><span class="params">(<span class="type">int</span> epollfd, <span class="type">int</span> fd, <span class="type">int</span> ev, <span class="type">int</span> TRIGMode)</span> <span class="comment">//将事件重置为EPOLLONESHOT</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">http_conn::unmap</span><span class="params">()</span></span>; <span class="comment">// 解除内存映射,并将资源文件到内存的m_file_address重置</span></span><br></pre></td></tr></table></figure><h3 id="read-once"><a href="#read-once" class="headerlink" title="read_once()"></a>read_once()</h3><p>设置都有读写缓存区,分别是<code>m_read_buf</code>和<code>m_write_buf</code></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> m_read_buf[READ_BUFFER_SIZE]{};</span><br><span class="line"><span class="type">int</span> m_read_idx{};</span><br><span class="line"><span class="type">int</span> m_check_idx{};</span><br><span class="line"><span class="type">char</span> m_write_buf[WRITE_BUFFER_SIZE]{};</span><br><span class="line"><span class="type">int</span> m_write_idx{};</span><br><span class="line"><span class="type">int</span> m_start_line{};</span><br></pre></td></tr></table></figure><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430171402.png" alt="image.png" style="zoom:60%;" /><ul><li><code>m_read_idx</code>:代表从客户端接受的数据的末尾位置。</li><li><code>m_check_idx</code>:用来标记当前正在检查的字符的位置。</li><li><code>m_start_line</code>: 用来标记缓冲区中当前处理的行的起始位置。</li></ul><p><code>read_once</code>这个函数主要完成将数据读到缓冲区,定义了一块缓冲区<code>m_read_buf</code>专门用来存放从浏览器发送来的请求报文,并用一个指针<code>m_read_idx</code>记录,这里分LT、ET模式,在LT模式下,<code>epoll_wait</code>会无数次地通知应用程序读事件的发生,直到应用程序去取。这里的应用程序是什么呢?很显然就是下面这个<code>read_once()</code>代码,在<code>if(m_TRIGMode == 0)</code>程序块里,应用程序用<code>recv</code>去将sockfd内的内容取到<code>m_readbuf</code>里面,如果没有取完,程序是无所谓的,它会继续往下执行,直到下一次epoll_wait再次通知,它便再进行recv操作。</p><p>而在ET模式下,epoll_wait只会通知应用程序一次,应用程序被要求在这一次就把sockfd中全部的数据取出,即read_once,可以看到在ET模式下,代码执行一个永不结束的循环while(true),唯有当数据全部取完(即recv返回-1并设置errno为EAGAIN或EWOULDBLOCK)时,程序才会退出,不然程序就一直循环下去。(其实当对方关闭了sock连接也会退出,但这就属于异常处理的流程,而非常规流程了)</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::read_once</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(m_read_idx>=READ_BUFFER_SIZE){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> read_ret=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span>(m_TRIGMode==<span class="number">0</span>){ <span class="comment">// LT模式</span></span><br><span class="line"> read_ret= <span class="built_in">recv</span>(m_sock_fd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(read_ret<=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> m_read_idx+=read_ret;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">// ET 模式</span></span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>){</span><br><span class="line"> read_ret= <span class="built_in">recv</span>(m_sock_fd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(read_ret==<span class="number">-1</span>){ <span class="comment">// // 这两个错误表明在非阻塞模式下没有数据可读。这是正常的</span></span><br><span class="line"> <span class="keyword">if</span>(errno==EAGAIN||errno==EWOULDBLOCK){</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(read_ret==<span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> m_read_idx+=read_ret;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="process"><a href="#process" class="headerlink" title="process"></a>process</h3><p>这是http最为核心的函数了,这是线程池工作线程不断循环执行的逻辑了</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430165359.png" alt="image.png" style="zoom:60%;" /><p>主要执行process_read和process_write。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">http_conn::process</span><span class="params">()</span> </span>{</span><br><span class="line"> HTTP_CODE read_ret = <span class="built_in">process_read</span>();</span><br><span class="line"> <span class="keyword">if</span>(read_ret==NO_REQUEST){</span><br><span class="line"> <span class="built_in">modfd</span>(m_epoll_fd,m_sock_fd,EPOLLIN,m_TRIGMode);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">bool</span> write_ret= <span class="built_in">process_write</span>(read_ret);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(!write_ret){</span><br><span class="line"> <span class="built_in">close_conn</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">modfd</span>(m_epoll_fd,m_sock_fd,EPOLLOUT,m_TRIGMode);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="process-read"><a href="#process-read" class="headerlink" title="process_read"></a>process_read</h4><p>process_read用来处理连接,我们设计了两个状态机<strong>主状态机/从状态机</strong>来进行报文解析。下图是处理连接的拓扑图: </p><ul><li>从状态机负责读取一行的数据</li><li>当从状态机成功读完一行后,就将这行数据交给主状态机</li><li>主状态机会根据报文格式以及自身状态对该请求行进行解析</li><li>解析完后主状态机的状态改变,切换到下一个状态再等待从状态机的数据循环、</li></ul><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430184115.png" alt="image.png" style="zoom:60%;" /><p>从上面的<code>read_once()</code>函数中我们获得了一个字符数组<code>m_read_buf</code>,和一个指针<code>m_read_idx</code>。我们开始读取m_read_buf至今为止保存的内容。这时我们需要创建一个新的指针<code>m_checked_idx</code>来记录每一行报文的结束地址。</p><p>下面是process_read流程图,</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430183621.png" alt="image.png" style="zoom:100%;" /><h5 id="parse-line"><a href="#parse-line" class="headerlink" title="parse_line"></a>parse_line</h5><p>因为http报文是<strong>按行来分开</strong>不同的信息的,而发送过来的数据要按行处理分出一行的内容,因为报文的行都是以<code><CR><LF></code>结束,我们可以通过这个条件让指针<code>m_checked_idx</code>来记录每一行报文的结束地址。当<code>m_checked_idx</code>在一行报文的末尾时,那<code>m_start_line</code>到<code>m_checked_idx</code>就是一行的首尾了。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//从状态机,用于分析出一行内容</span></span><br><span class="line"><span class="comment">//返回值为行的读取状态,有LINE_OK,LINE_BAD,LINE_OPEN</span></span><br><span class="line"><span class="function">http_conn::LINE_STATUS <span class="title">http_conn::parse_line</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">char</span> temp;</span><br><span class="line"> <span class="keyword">for</span>(;m_check_idx<m_read_idx;++m_check_idx){</span><br><span class="line"> temp=m_read_buf[m_check_idx];</span><br><span class="line"> <span class="keyword">if</span>(temp==<span class="string">'\r'</span>){</span><br><span class="line"> <span class="keyword">if</span>(m_check_idx+<span class="number">1</span>==m_read_idx)</span><br><span class="line"> <span class="keyword">return</span> LINE_OPEN;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(m_read_buf[m_check_idx+<span class="number">1</span>]==<span class="string">'\n'</span>){</span><br><span class="line"> m_read_buf[m_check_idx++]=<span class="string">'\0'</span>;</span><br><span class="line"> m_read_buf[m_check_idx++]=<span class="string">'\0'</span>;</span><br><span class="line"> <span class="keyword">return</span> LINE_OK;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> LINE_BAD;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(temp==<span class="string">'\n'</span>){</span><br><span class="line"> <span class="keyword">if</span>(m_check_idx><span class="number">1</span>&&m_read_buf[m_check_idx<span class="number">-1</span>]==<span class="string">'\r'</span>){</span><br><span class="line"> m_read_buf[m_check_idx<span class="number">-1</span>]=<span class="string">'\0'</span>;</span><br><span class="line"> m_read_buf[m_check_idx++]=<span class="string">'\0'</span>;</span><br><span class="line"> <span class="keyword">return</span> LINE_OK;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> LINE_BAD;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> LINE_OPEN;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>而<code>get_line</code>就是一行内容了,因为我们在末尾都赋值了<code>\0</code></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">text = <span class="built_in">get_line</span>();</span><br><span class="line"><span class="function"><span class="type">char</span> *<span class="title">get_line</span><span class="params">()</span> </span>{<span class="keyword">return</span> m_read_buf+m_start_line;};</span><br><span class="line">ret = <span class="built_in">parse_request_line</span>(text);</span><br></pre></td></tr></table></figure><h5 id="parse-request-line"><a href="#parse-request-line" class="headerlink" title="parse_request_line"></a>parse_request_line</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">METHOD m_method; <span class="comment">// 请求方法</span></span><br><span class="line"><span class="type">char</span> *m_url{}; <span class="comment">// url 比如 http://www.baidu.com/index.html</span></span><br><span class="line"><span class="type">char</span> *m_version{}; <span class="comment">// http 版本</span></span><br><span class="line"><span class="type">int</span> cgi{}; <span class="comment">//是否启用的POST</span></span><br></pre></td></tr></table></figure><p>这个函数主要用来解析请求行 比如<code>GET http://www.baidu.com/index.html HTTP/1.0</code></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http_conn::HTTP_CODE <span class="title">http_conn::parse_request_line</span><span class="params">(<span class="type">char</span> *text)</span> </span>{</span><br><span class="line"></span><br><span class="line"> m_url = <span class="built_in">strpbrk</span>(text, <span class="string">" \t"</span>);</span><br><span class="line"> <span class="keyword">if</span> (!m_url)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> }</span><br><span class="line"> *m_url++ = <span class="string">'\0'</span>;</span><br><span class="line"> <span class="type">char</span> *method = text;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcasecmp</span>(method, <span class="string">"GET"</span>) == <span class="number">0</span>)</span><br><span class="line"> m_method = GET;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strcasecmp</span>(method, <span class="string">"POST"</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> m_method = POST;</span><br><span class="line"> cgi = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> m_url += <span class="built_in">strspn</span>(m_url, <span class="string">" \t"</span>); <span class="comment">// 这里主要用于去除m_url多余的空格</span></span><br><span class="line"> m_version = <span class="built_in">strpbrk</span>(m_url, <span class="string">" \t"</span>);</span><br><span class="line"> <span class="keyword">if</span> (!m_version)</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> *m_version++ = <span class="string">'\0'</span>;</span><br><span class="line"> m_version += <span class="built_in">strspn</span>(m_version, <span class="string">" \t"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcasecmp</span>(m_version, <span class="string">"HTTP/1.1"</span>) != <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strncasecmp</span>(m_url, <span class="string">"http://"</span>, <span class="number">7</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> m_url += <span class="number">7</span>;</span><br><span class="line"> m_url = <span class="built_in">strchr</span>(m_url, <span class="string">'/'</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strncasecmp</span>(m_url, <span class="string">"https://"</span>, <span class="number">8</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> m_url += <span class="number">8</span>;</span><br><span class="line"> m_url = <span class="built_in">strchr</span>(m_url, <span class="string">'/'</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!m_url || m_url[<span class="number">0</span>] != <span class="string">'/'</span>)</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> <span class="comment">//当url为/时,显示判断界面</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strlen</span>(m_url) == <span class="number">1</span>)</span><br><span class="line"> <span class="built_in">strcat</span>(m_url, <span class="string">"judge.html"</span>);</span><br><span class="line"> m_check_state = CHECK_STATE_HEADER;</span><br><span class="line"> <span class="keyword">return</span> NO_REQUEST;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="parse-headers"><a href="#parse-headers" class="headerlink" title="parse_headers"></a>parse_headers</h5><p>解析完请求行,我们再来解析请求头部,根据报文格式,以及头部字段名的种类,我们有下面的代码</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> m_linger{}; <span class="comment">// 是否保持连接</span></span><br><span class="line"><span class="type">long</span> m_content_length{};</span><br><span class="line"><span class="type">char</span> *m_host{};</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//解析http请求的一个头部信息</span></span><br><span class="line"><span class="function">http_conn::HTTP_CODE <span class="title">http_conn::parse_headers</span><span class="params">(<span class="type">char</span> *text)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (text[<span class="number">0</span>] == <span class="string">'\0'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (m_content_length != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> m_check_state = CHECK_STATE_CONTENT;</span><br><span class="line"> <span class="keyword">return</span> NO_REQUEST;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> GET_REQUEST;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strncasecmp</span>(text, <span class="string">"Connection:"</span>, <span class="number">11</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> text += <span class="number">11</span>;</span><br><span class="line"> text += <span class="built_in">strspn</span>(text, <span class="string">" \t"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcasecmp</span>(text, <span class="string">"keep-alive"</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> m_linger = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strncasecmp</span>(text, <span class="string">"Content-length:"</span>, <span class="number">15</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> text += <span class="number">15</span>;</span><br><span class="line"> text += <span class="built_in">strspn</span>(text, <span class="string">" \t"</span>);</span><br><span class="line"> m_content_length = <span class="built_in">atol</span>(text);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">strncasecmp</span>(text, <span class="string">"Host:"</span>, <span class="number">5</span>) == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> text += <span class="number">5</span>;</span><br><span class="line"> text += <span class="built_in">strspn</span>(text, <span class="string">" \t"</span>);</span><br><span class="line"> m_host = text;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"oop!unknown header: %s"</span>, text)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> NO_REQUEST;</span><br></pre></td></tr></table></figure><h5 id="parse-content"><a href="#parse-content" class="headerlink" title="parse_content"></a>parse_content</h5><p>如果时post请求还要解析请求体</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* m_string{}; <span class="comment">//存储请求体数据</span></span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http_conn::HTTP_CODE <span class="title">http_conn::parse_content</span><span class="params">(<span class="type">char</span> *text)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(m_read_idx>=(m_content_length+m_check_idx)){</span><br><span class="line"> text[m_content_length]=<span class="string">'\0'</span>;</span><br><span class="line"> <span class="comment">//POST请求中最后为输入的用户名和密码</span></span><br><span class="line"> m_string=text;</span><br><span class="line"> <span class="keyword">return</span> GET_REQUEST;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> NO_REQUEST;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="process-read-1"><a href="#process-read-1" class="headerlink" title="process_read"></a>process_read</h5><p>在POST请求报文中,因为消息体结尾没有 <code>\r\n</code> ,所以不会触发<code>parse_line()</code>解析,所以我们只能根据主状态机进行条件判断进入循环。但这会有个问题,等POST请求报文全部解析完后,m_check_state依然是<code>CHECK_STATE_CONTENT</code>,还是不会退出循环。这不是我们所希望的,所以我们让它加上<code>line_status == LINE_OK</code>,这样当POST消息体全部解析完后,<code>line_status</code>会被赋值为<code>LINE_OPEN</code>,就不再进入主循环</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http_conn::HTTP_CODE <span class="title">http_conn::process_read</span><span class="params">()</span> </span>{</span><br><span class="line"> LINE_STATUS line_status=LINE_OK;</span><br><span class="line"> HTTP_CODE ret=NO_REQUEST;</span><br><span class="line"> <span class="type">char</span> *text=<span class="literal">nullptr</span>;</span><br><span class="line"> <span class="keyword">while</span> ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = <span class="built_in">parse_line</span>()) == LINE_OK))</span><br><span class="line"> {</span><br><span class="line"> text = <span class="built_in">get_line</span>();</span><br><span class="line"> m_start_line = m_check_idx;</span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"%s"</span>, text)</span><br><span class="line"> <span class="keyword">switch</span> (m_check_state)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> CHECK_STATE_REQUESTLINE:</span><br><span class="line"> {</span><br><span class="line"> ret = <span class="built_in">parse_request_line</span>(text);</span><br><span class="line"> <span class="keyword">if</span> (ret == BAD_REQUEST)</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> CHECK_STATE_HEADER:</span><br><span class="line"> {</span><br><span class="line"> ret = <span class="built_in">parse_headers</span>(text);</span><br><span class="line"> <span class="keyword">if</span> (ret == BAD_REQUEST)</span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (ret == GET_REQUEST)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">do_request</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> CHECK_STATE_CONTENT:</span><br><span class="line"> {</span><br><span class="line"> ret = <span class="built_in">parse_content</span>(text);</span><br><span class="line"> <span class="keyword">if</span> (ret == GET_REQUEST)</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">do_request</span>(); <span class="comment">// 才具备了执行do_request的充分条件</span></span><br><span class="line"> line_status = LINE_OPEN;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> INTERNAL_ERROR;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> NO_REQUEST;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="do-request"><a href="#do-request" class="headerlink" title="do_request"></a>do_request</h4><p>因为客户端主要从服务端获取<strong>MIME</strong>作为主要功能,客户发起了一个图片的请求<code>http://xxx/images/pic.jpg</code>,在服务端来说这是一个链接,在服务端那边就是一个请求资源文件的路径,服务端根据请求的<code>URL</code>路径,去其文件系统中寻找对应的文件。如果文件存在,服务端将继续处理;如果文件不存在,则通常返回404错误(资源未找到)。</p><p><code>do_request()</code>主要目的是解析HTTP请求URL,根据不同的路径(URL中的部分内容)来执行不同的操作,例如处理CGI请求(如登录、注册等),处理静态文件请求等。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> m_real_file[FILENAME_LEN]{};</span><br><span class="line"><span class="type">char</span>* doc_root{}; <span class="comment">// 服务器上用于存放网页文件的根目录的路径</span></span><br><span class="line"><span class="type">char</span> *m_file_address{}; <span class="comment">// 请求文件被mmap到内存中的位置</span></span><br></pre></td></tr></table></figure><p>其实本质上这个函数是为了得到<code>m_real_file</code>,资源文件的路径,得到<code>m_real_file</code>后<strong>映射文件内容到内存</strong>,这样操作系统可以利用虚拟内存系统来访问文件,文件的读取和写入就像访问普通内存一样高效。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http_conn::HTTP_CODE <span class="title">http_conn::do_request</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">strcpy</span>(m_real_file,doc_root);</span><br><span class="line"> <span class="type">int</span> len= <span class="built_in">strlen</span>(doc_root);</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *p= <span class="built_in">strrchr</span>(m_url,<span class="string">'/'</span>);</span><br><span class="line"> <span class="comment">//处理cgi</span></span><br><span class="line"> <span class="keyword">if</span> (cgi == <span class="number">1</span> && (*(p + <span class="number">1</span>) == <span class="string">'2'</span> || *(p + <span class="number">1</span>) == <span class="string">'3'</span>)) <span class="comment">// 服务器优化设计定的动作对应特定的数字标识 2代表登录 3代表注册</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//根据标志判断是登录检测还是注册检测</span></span><br><span class="line"> <span class="type">char</span> flag = m_url[<span class="number">1</span>]; <span class="comment">//m_url:/2CGISQL.cgi</span></span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/"</span>);</span><br><span class="line"> <span class="built_in">strcat</span>(m_url_real, m_url + <span class="number">2</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, FILENAME_LEN - len - <span class="number">1</span>);</span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//将用户名和密码提取出来</span></span><br><span class="line"> <span class="comment">//user=123&passwd=123</span></span><br><span class="line"> <span class="type">char</span> name[<span class="number">100</span>], password[<span class="number">100</span>];</span><br><span class="line"> <span class="type">int</span> i;</span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">5</span>; m_string[i] != <span class="string">'&'</span>; ++i)</span><br><span class="line"> name[i - <span class="number">5</span>] = m_string[i];</span><br><span class="line"> name[i - <span class="number">5</span>] = <span class="string">'\0'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> j = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (i = i + <span class="number">10</span>; m_string[i] != <span class="string">'\0'</span>; ++i, ++j)</span><br><span class="line"> password[j] = m_string[i];</span><br><span class="line"> password[j] = <span class="string">'\0'</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'3'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">//如果是注册,先检测数据库中是否有重名的</span></span><br><span class="line"> <span class="comment">//没有重名的,进行增加数据</span></span><br><span class="line"> <span class="type">char</span> *sql_insert = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">snprintf</span>(sql_insert, <span class="built_in">strlen</span>(sql_insert), <span class="string">"INSERT INTO user(username, passwd) VALUES('%s', '%s')"</span>,name,password);</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span> (users.<span class="built_in">find</span>(name) == users.<span class="built_in">end</span>())</span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> res = <span class="built_in">mysql_query</span>(mysql, sql_insert);</span><br><span class="line"> users.<span class="built_in">insert</span>(<span class="built_in">pair</span><string, string>(name, password));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!res)</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url, <span class="string">"/log.html"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">strcpy</span>(m_url, <span class="string">"/registerError.html"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">strcpy</span>(m_url, <span class="string">"/registerError.html"</span>);</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//如果是登录,直接判断</span></span><br><span class="line"> <span class="comment">//若浏览器端输入的用户名和密码在表中可以查找到,返回1,否则返回0</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'2'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (users.<span class="built_in">find</span>(name) != users.<span class="built_in">end</span>() && users[name] == password)</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url, <span class="string">"/welcome.html"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">strcpy</span>(m_url, <span class="string">"/logError.html"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'0'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/register.html"</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, <span class="built_in">strlen</span>(m_url_real));</span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'1'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/log.html"</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, <span class="built_in">strlen</span>(m_url_real));</span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'5'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/picture.html"</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, <span class="built_in">strlen</span>(m_url_real));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'6'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/video.html"</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, <span class="built_in">strlen</span>(m_url_real));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (*(p + <span class="number">1</span>) == <span class="string">'7'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">char</span> *m_url_real = (<span class="type">char</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">char</span>) * <span class="number">200</span>);</span><br><span class="line"> <span class="built_in">strcpy</span>(m_url_real, <span class="string">"/fans.html"</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url_real, <span class="built_in">strlen</span>(m_url_real));</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(m_url_real);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">strncpy</span>(m_real_file + len, m_url, FILENAME_LEN - len - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">stat</span>(m_real_file, &m_file_stat) < <span class="number">0</span>) <span class="comment">// 这里<0就代表文件不存在了</span></span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> NO_RESOURCE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!(m_file_stat.st_mode & S_IROTH)) <span class="comment">// S_IROTH,即是否设置了其他(other)用户的读权限</span></span><br><span class="line"> <span class="keyword">return</span> FORBIDDEN_REQUEST;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">S_ISDIR</span>(m_file_stat.st_mode)) <span class="comment">// 宏 S_ISDIR 检查 st_mode 是否表示这是一个目录</span></span><br><span class="line"> <span class="keyword">return</span> BAD_REQUEST;</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> fd = <span class="built_in">open</span>(m_real_file, O_RDONLY);</span><br><span class="line"> m_file_address = (<span class="type">char</span> *)<span class="built_in">mmap</span>(<span class="literal">nullptr</span>, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">close</span>(fd);</span><br><span class="line"> <span class="keyword">return</span> FILE_REQUEST;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="process-write"><a href="#process-write" class="headerlink" title="process_write"></a>process_write</h4><p>这个process_write主要来根据处理HTTP请求的结果构建相应的HTTP响应并准备发送数据的。</p><p>HTTP应答的部分内容如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">HTTP/<span class="number">1.0</span> <span class="number">200</span> OK</span><br><span class="line">Server:BWS/<span class="number">1.0</span></span><br><span class="line">Content-Length:<span class="number">8024</span></span><br><span class="line">Content-Type:text/html;charset=gbk</span><br><span class="line">Set-Cookie:BAIDUID=A5B6C72D68CF639CE8896FD79A03FBD8:FG=<span class="number">1</span>;expires=Wed,<span class="number">04</span>-Jul<span class="number">-42</span> <span class="number">00</span>:<span class="number">10</span>:<span class="number">47</span> GMT;path=/;domain=.baidu.com</span><br><span class="line">Via:<span class="number">1.0</span> localhost(squid/<span class="number">3.0</span> STABLE18)</span><br></pre></td></tr></table></figure><h5 id="add-response"><a href="#add-response" class="headerlink" title="add_response"></a>add_response</h5><p>这里定义一个基础的往HTTP<strong>响应的缓冲区</strong>添加格式化的数据的函数,并且使用可变参数<code>va_list</code>增加它的可复用性。这样我们后面的各种写数据就可以直接调用<code>add_response</code>了。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_response</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *format, ...)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (m_write_idx >= WRITE_BUFFER_SIZE)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> va_list arg_list;</span><br><span class="line"> <span class="built_in">va_start</span>(arg_list, format);</span><br><span class="line"> <span class="type">int</span> len = <span class="built_in">vsnprintf</span>(m_write_buf + m_write_idx, WRITE_BUFFER_SIZE - <span class="number">1</span> - m_write_idx, format, arg_list);</span><br><span class="line"> <span class="keyword">if</span> (len >= (WRITE_BUFFER_SIZE - <span class="number">1</span> - m_write_idx))</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">va_end</span>(arg_list);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> m_write_idx += len;</span><br><span class="line"> <span class="built_in">va_end</span>(arg_list);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">LOG_INFO</span>(<span class="string">"request:%s"</span>, m_write_buf)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>调用add_response的函数系列</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*添加状态行*/</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_status_line</span><span class="params">(<span class="type">int</span> status, <span class="type">const</span> <span class="type">char</span> *title)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"%s %d %s\r\n"</span>, <span class="string">"HTTP/1.1"</span>, status, title);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="comment">/*添加消息报头,具体的添加长度文本 连接状态 和空行*/</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_headers</span><span class="params">(<span class="type">int</span> content_len)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_content_length</span>(content_len) && <span class="built_in">add_linger</span>() && <span class="built_in">add_blank_line</span>();</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_content_length</span><span class="params">(<span class="type">int</span> content_len)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"Content-Length:%d\r\n"</span>, content_len);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_content_type</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"Content-Type:%s\r\n"</span>, <span class="string">"text/html"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_linger</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"Connection:%s\r\n"</span>,(m_linger == <span class="literal">true</span>) ? <span class="string">"keep-alive"</span> : <span class="string">"close"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_blank_line</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"%s"</span>, <span class="string">"\r\n"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::add_content</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *content)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">add_response</span>(<span class="string">"%s"</span>, content);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="process-write-1"><a href="#process-write-1" class="headerlink" title="process_write"></a>process_write</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">stat</span> m_file_stat{}; <span class="comment">// 目标文件的状态信息</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">iovec</span> m_iv[<span class="number">2</span>]{}; <span class="comment">// 用于writev操作的结构体数组。</span></span><br><span class="line"><span class="type">int</span> m_iv_count{}; <span class="comment">// 被用于输出的iovec结构体数量</span></span><br></pre></td></tr></table></figure><p>如果是<code>FILE_REQUEST</code>,我们要发两段信息,用了iovec,因为后面write用<code>writev</code>,一段是用于写缓冲区的给客户端的响应,一段是文件内容,是客户端请求的资源文件</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::process_write</span><span class="params">(http_conn::HTTP_CODE ret)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (ret)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> INTERNAL_ERROR:</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">add_status_line</span>(<span class="number">500</span>, error_500_title);</span><br><span class="line"> <span class="built_in">add_headers</span>(<span class="built_in">strlen</span>(error_500_form));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">add_content</span>(error_500_form))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> BAD_REQUEST:</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">add_status_line</span>(<span class="number">404</span>, error_404_title);</span><br><span class="line"> <span class="built_in">add_headers</span>(<span class="built_in">strlen</span>(error_404_form));</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">add_content</span>(error_404_form))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> FORBIDDEN_REQUEST:</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">add_status_line</span>(<span class="number">403</span>, error_403_title);</span><br><span class="line"> <span class="built_in">add_headers</span>(<span class="built_in">strlen</span>(error_403_form));</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">add_content</span>(error_403_form))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> FILE_REQUEST:</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">add_status_line</span>(<span class="number">200</span>, ok_200_title);</span><br><span class="line"> <span class="keyword">if</span> (m_file_stat.st_size != <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">add_headers</span>(m_file_stat.st_size);</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_base = m_write_buf;</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_len = m_write_idx;</span><br><span class="line"> m_iv[<span class="number">1</span>].iov_base = m_file_address;</span><br><span class="line"> m_iv[<span class="number">1</span>].iov_len = m_file_stat.st_size;</span><br><span class="line"> m_iv_count = <span class="number">2</span>;</span><br><span class="line"> bytes_to_send = m_write_idx + m_file_stat.st_size;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *ok_string = <span class="string">"<html><body></body></html>"</span>;</span><br><span class="line"> <span class="built_in">add_headers</span>(<span class="built_in">strlen</span>(ok_string));</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">add_content</span>(ok_string))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span>:{</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_base = m_write_buf;</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_len = m_write_idx;</span><br><span class="line"> m_iv_count = <span class="number">1</span>;</span><br><span class="line"> bytes_to_send = m_write_idx;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="write"><a href="#write" class="headerlink" title="write"></a>write</h3><p>在写缓冲区写满要发送的数据后,我们最后调用write将其发送给浏览器客户端,如果保持连接发送完了数据后要重置各参数不返回false</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> bytes_to_send{};</span><br><span class="line"><span class="type">int</span> bytes_has_send{};</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">http_conn::write</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> temp = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (bytes_to_send == <span class="number">0</span>) <span class="comment">// 所有响应数据已经成功发送</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">modfd</span>(m_epoll_fd, m_sock_fd, EPOLLIN, m_TRIGMode);</span><br><span class="line"> <span class="built_in">reset</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line"> {</span><br><span class="line"> temp = <span class="built_in">writev</span>(m_sock_fd, m_iv, m_iv_count);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (temp < <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (errno == EAGAIN) <span class="comment">// 缓冲写满了</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">modfd</span>(m_epoll_fd, m_sock_fd, EPOLLOUT, m_TRIGMode);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">unmap</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> bytes_has_send += temp;</span><br><span class="line"> bytes_to_send -= temp;</span><br><span class="line"> <span class="keyword">if</span> (bytes_has_send >= m_iv[<span class="number">0</span>].iov_len)</span><br><span class="line"> {</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_len = <span class="number">0</span>;</span><br><span class="line"> m_iv[<span class="number">1</span>].iov_base = m_file_address + (bytes_has_send - m_write_idx);</span><br><span class="line"> m_iv[<span class="number">1</span>].iov_len = bytes_to_send;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_base = m_write_buf + bytes_has_send;</span><br><span class="line"> m_iv[<span class="number">0</span>].iov_len = m_iv[<span class="number">0</span>].iov_len - bytes_has_send;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (bytes_to_send <= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">unmap</span>();</span><br><span class="line"> <span class="built_in">modfd</span>(m_epoll_fd, m_sock_fd, EPOLLIN, m_TRIGMode);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (m_linger)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">reset</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="初始化工作"><a href="#初始化工作" class="headerlink" title="初始化工作"></a>初始化工作</h3><p>现在列出类中的各种参数,这样就很清晰每个参数的使用途径了</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="type">static</span> <span class="type">int</span> m_epoll_fd;</span><br><span class="line"> <span class="type">static</span> <span class="type">int</span> m_user_count;</span><br><span class="line"> MYSQL *mysql{};</span><br><span class="line"> <span class="type">int</span> m_state{}; <span class="comment">// 0 读 1 写</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> m_sock_fd{};</span><br><span class="line"> sockaddr_in m_address{};</span><br><span class="line"> <span class="type">int</span> m_TRIGMode{}; <span class="comment">// 触发模式</span></span><br><span class="line"> <span class="type">char</span> *m_file_address{}; <span class="comment">// 请求文件被mmap到内存中的位置</span></span><br><span class="line"> <span class="type">char</span> m_read_buf[READ_BUFFER_SIZE]{};</span><br><span class="line"> <span class="type">int</span> m_read_idx{};</span><br><span class="line"> <span class="type">int</span> m_check_idx{};</span><br><span class="line"> <span class="type">char</span> m_write_buf[WRITE_BUFFER_SIZE]{};</span><br><span class="line"> <span class="type">int</span> m_write_idx{};</span><br><span class="line"> <span class="type">int</span> m_start_line{};</span><br><span class="line"> <span class="type">char</span> m_real_file[FILENAME_LEN]{};</span><br><span class="line"> <span class="type">char</span>* doc_root{}; <span class="comment">// 服务器上用于存放网页文件的根目录的路径</span></span><br><span class="line"> CHECK_STATE m_check_state;</span><br><span class="line"> METHOD m_method;</span><br><span class="line"> <span class="type">char</span> *m_url{};</span><br><span class="line"> <span class="type">char</span> *m_version{};</span><br><span class="line"> <span class="type">char</span> *m_host{};</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">stat</span> m_file_stat{}; <span class="comment">// 目标文件的状态信息</span></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">iovec</span> m_iv[<span class="number">2</span>]{}; <span class="comment">// 用于writev操作的结构体数组。</span></span><br><span class="line"> <span class="type">int</span> m_iv_count{}; <span class="comment">// 被用于输出的iovec结构体数量</span></span><br><span class="line"> <span class="type">int</span> cgi{}; <span class="comment">//是否启用的POST</span></span><br><span class="line"> <span class="type">char</span>* m_string{}; <span class="comment">//存储请求体数据</span></span><br><span class="line"> <span class="type">int</span> m_close_log{};</span><br><span class="line"> <span class="type">char</span> sql_user[<span class="number">100</span>]{};</span><br><span class="line"> <span class="type">char</span> sql_password[<span class="number">100</span>]{};</span><br><span class="line"> <span class="type">char</span> sql_name[<span class="number">100</span>]{};</span><br><span class="line"> <span class="type">int</span> bytes_to_send{};</span><br><span class="line"> <span class="type">int</span> bytes_has_send{};</span><br><span class="line"> <span class="comment">// 用户信息和数据库配置</span></span><br><span class="line"> map<string,string>m_users;</span><br><span class="line"> <span class="type">bool</span> m_linger{}; <span class="comment">// 是否保持连接</span></span><br><span class="line"> <span class="type">long</span> m_content_length{};</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">http_conn::init</span><span class="params">(<span class="type">int</span> sockfd, <span class="type">const</span> sockaddr_in &addr, <span class="type">char</span> *root, <span class="type">int</span> TRIGMode,<span class="type">int</span> close_log, string user, string passwd, string sqlname)</span> </span>{</span><br><span class="line"> m_sock_fd=sockfd;</span><br><span class="line"> m_TRIGMode=TRIGMode;</span><br><span class="line"> m_address=addr;</span><br><span class="line"> m_close_log=close_log;</span><br><span class="line"> <span class="built_in">addfd</span>(m_epoll_fd,m_sock_fd,<span class="literal">true</span>,TRIGMode);</span><br><span class="line"> m_user_count++;</span><br><span class="line"> <span class="comment">//当浏览器出现连接重置时,可能是网站根目录出错或http响应格式出错或者访问的文件中内容完全为空</span></span><br><span class="line"> doc_root=root;</span><br><span class="line"> <span class="built_in">strcpy</span>(sql_user,user.<span class="built_in">c_str</span>());</span><br><span class="line"> <span class="built_in">strcpy</span>(sql_password,passwd.<span class="built_in">c_str</span>());</span><br><span class="line"> <span class="built_in">strcpy</span>(sql_name,sqlname.<span class="built_in">c_str</span>());</span><br><span class="line"> <span class="built_in">reset</span>(); <span class="comment">// 主要进行私有无参的重置 在我们发送完所有的数据调用这个方法</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">http_conn::reset</span><span class="params">()</span> </span>{ <span class="comment">// 主要进行私有无参的重置 在我们发送完所有的数据调用这个方法</span></span><br><span class="line"> mysql= <span class="literal">nullptr</span>;</span><br><span class="line"> m_state=<span class="number">0</span>;</span><br><span class="line"> cgi=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> m_file_address= <span class="literal">nullptr</span>;</span><br><span class="line"> m_read_idx=<span class="number">0</span>;</span><br><span class="line"> m_check_idx=<span class="number">0</span>;</span><br><span class="line"> m_start_line=<span class="number">0</span>;</span><br><span class="line"> m_write_idx=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> m_check_state=CHECK_STATE_REQUESTLINE;</span><br><span class="line"> m_method=GET;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> m_url= <span class="literal">nullptr</span>;</span><br><span class="line"> m_version= <span class="literal">nullptr</span>;</span><br><span class="line"> m_host= <span class="literal">nullptr</span>;</span><br><span class="line"> m_string= <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line"> bytes_to_send=<span class="number">0</span>;</span><br><span class="line"> bytes_has_send=<span class="number">0</span>;</span><br><span class="line"> m_content_length=<span class="number">0</span>;</span><br><span class="line"> m_linger=<span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> timer_flag=<span class="number">0</span>;</span><br><span class="line"> improv=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(m_read_buf,<span class="string">'\0'</span>,READ_BUFFER_SIZE);</span><br><span class="line"> <span class="built_in">memset</span>(m_write_buf,<span class="string">'\0'</span>,WRITE_BUFFER_SIZE);</span><br><span class="line"> <span class="built_in">memset</span>(m_real_file,<span class="string">'\0'</span>,FILENAME_LEN);</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 一起做项目 </category>
</categories>
<tags>
<tag> 项目 </tag>
</tags>
</entry>
<entry>
<title>一起写webserver 项目(三)</title>
<link href="/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%B8%89)/"/>
<url>/2024/04/30/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%B8%89)/</url>
<content type="html"><![CDATA[<h2 id="数据库连接池的封装"><a href="#数据库连接池的封装" class="headerlink" title="数据库连接池的封装"></a>数据库连接池的封装</h2><h3 id="什么是池"><a href="#什么是池" class="headerlink" title="什么是池"></a>什么是池</h3><ul><li><p><strong>池的概念</strong></p><ul><li>“浪费”服务器的硬件<strong>资源</strong>,以换取其运行效率。</li><li>池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源分配。</li><li>直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的</li></ul></li><li><p><strong>为什么需要数据库连接池</strong>?</p><ul><li>每个逻辑单元可能都需要频繁地访问本地的某个数据库。</li><li>连接池是服务器预先和数据库程序建立的一组连接的集合。当某个逻辑单元需要访问数据库时,它可以直接从连接池中取得一个连接的实体并使用之。待完成数据库的访问之后,逻辑单元再将该连接返还给连接池</li></ul></li></ul><h3 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h3><p>单例模式的好处就不再赘述</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">connection_pool</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> connection_pool* <span class="title">Getinstance</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">static</span> connection_pool connPool;</span><br><span class="line"> <span class="keyword">return</span> &connPool;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="built_in">connection_pool</span>();</span><br><span class="line"> ~<span class="built_in">connection_pool</span>();</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>再把构造函数和析构函数补充完整,里面初始化的成员不要着急,后面会写;</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">connection_pool::<span class="built_in">connection_pool</span>() {</span><br><span class="line"> m_FreeConn = <span class="number">0</span>;</span><br><span class="line"> m_CurConn = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">connection_pool::~<span class="built_in">connection_pool</span>() {</span><br><span class="line"> <span class="built_in">DestroyPool</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><ul><li><p>整体思路</p><ul><li>这里初始化的操作就是建立起<code>max_con</code>个连接,这些连接的类型是<code>MYSQL *</code>,将这个 <code>max_con</code>个连接存到一个数据结构中,方便存取,这里采用的是链表<code>List</code>。</li><li>这里数据库的资源使用信号量进行同步,所以信号量的初始化为数据库的连接总数。 <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> string m_url;</span><br><span class="line"> string m_user;</span><br><span class="line"> string m_port;</span><br><span class="line"> string m_password;</span><br><span class="line"> string m_databaseName;</span><br><span class="line"> <span class="type">int</span> m_close_log{};</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> m_free_con; <span class="comment">// 空闲连接数</span></span><br><span class="line"> <span class="type">int</span> m_cur_con; <span class="comment">// 正在使用的连接数</span></span><br><span class="line"> <span class="type">int</span> m_max_con; <span class="comment">// 最大连接数</span></span><br><span class="line"> list<MYSQL *>connList;</span><br><span class="line"> locker lock;</span><br><span class="line"> sem reserve{};</span><br><span class="line"> <span class="built_in">connection_pool</span>();</span><br><span class="line"> ~<span class="built_in">connection_pool</span>();</span><br></pre></td></tr></table></figure></li></ul> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">connection_pool::init</span><span class="params">(string url, <span class="type">int</span> port, string user, string password, string dbName, <span class="type">int</span> close_log,<span class="type">int</span> max_con)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// ....</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<max_con;i++){</span><br><span class="line"> MYSQL *con= <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line"> con=<span class="built_in">mysql_init</span>(con);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(con== <span class="literal">nullptr</span>){</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"MySQL Error"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> con=<span class="built_in">mysql_real_connect</span>(con,m_url.<span class="built_in">c_str</span>(),m_user.<span class="built_in">c_str</span>(),m_password.<span class="built_in">c_str</span>(),m_databaseName.<span class="built_in">c_str</span>(),port, <span class="literal">nullptr</span>,<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span>(con== <span class="literal">nullptr</span>){</span><br><span class="line"> <span class="built_in">LOG_ERROR</span>(<span class="string">"MySQL Error"</span>);</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> connList.<span class="built_in">push_back</span>(con);</span><br><span class="line"> ++m_free_con;</span><br><span class="line"> }</span><br><span class="line"> m_max_con=m_free_con;</span><br><span class="line"> reserve = <span class="built_in">sem</span>(m_free_con); <span class="comment">// 信号量的初始化</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="数据库的访问"><a href="#数据库的访问" class="headerlink" title="数据库的访问"></a>数据库的访问</h3><p>这里的获取和释放数据库连接也是类<strong>生产者-消费者模型</strong>,这里用的是信号量+同步锁,这里的无论是获取,释放还是销毁,我们都要用mutex来保证线程同步,同时,获取连接前需要wait()阻塞等到临界区有资源,释放连接后需要post()来提醒其他线程临界区有新资源</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">MYSQL *<span class="title">connection_pool::getConnection</span><span class="params">()</span> </span>{ <span class="comment">// 消费者</span></span><br><span class="line"></span><br><span class="line"> MYSQL *con= <span class="literal">nullptr</span>;</span><br><span class="line"> <span class="keyword">if</span>(connList.<span class="built_in">empty</span>()) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line"> reserve.<span class="built_in">wait</span>();</span><br><span class="line"> lock.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line"> con=connList.<span class="built_in">front</span>();</span><br><span class="line"> connList.<span class="built_in">pop_front</span>();</span><br><span class="line"> --m_free_con;</span><br><span class="line"> ++m_cur_con;</span><br><span class="line"> lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> con;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">connection_pool::releaseConnection</span><span class="params">(MYSQL *con)</span> </span>{ <span class="comment">// 生产者</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(con== <span class="literal">nullptr</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> lock.<span class="built_in">lock</span>();</span><br><span class="line"> connList.<span class="built_in">push_back</span>(con);</span><br><span class="line"> ++m_free_con;</span><br><span class="line"> --m_cur_con;</span><br><span class="line"> lock.<span class="built_in">unlock</span>();</span><br><span class="line"> reserve.<span class="built_in">post</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="RAII-类"><a href="#RAII-类" class="headerlink" title="RAII 类"></a>RAII 类</h3><p>这里使用了RAII实现<strong>资源池的自动回收机制</strong></p><ul><li><code>ResourcePool</code>为资源池类,可以创建指定数量的资源,并提供获取和释放资源的接口。</li><li><code>ResourceWrapper</code>为资源包装类,用于获取资源,并在对象销毁时自动释放资源。</li><li><code>Resource</code>为资源类,用于模拟资源,通过id来标识,其构造函数和析构函数分别用于获取和释放资源。</li></ul><p>这里的<code>connection_pool</code>就是<code>ResourcePool</code>,而<code>MYSQL *</code>是<code>Resource</code>,这里可以创建了一个资源包装类<code>ResourceWrapper</code>,可以通过<strong>构造函数</strong>调用数据库连接函数,通过<strong>析构函数</strong>调用销毁这个数据的连接,这样就实现了资源的获取与释放与类的实例的生命周期绑定</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">connection_poolRAII</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">connection_poolRAII</span>(MYSQL **SQL,connection_pool *connPool) { </span><br><span class="line"> *SQL=connPool-><span class="built_in">getConnection</span>(); <span class="comment">// 构造就获取一个连接</span></span><br><span class="line"> connRAII=*SQL; </span><br><span class="line"> poolRAII=connPool; </span><br><span class="line">}</span><br><span class="line"> ~<span class="built_in">connection_poolRAII</span>(){ <span class="comment">// 析构就销毁这个连接</span></span><br><span class="line"> poolRAII-><span class="built_in">releaseConnection</span>(connRAII); </span><br><span class="line">}</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> MYSQL *connRAII;</span><br><span class="line"> connection_pool *poolRAII;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="定时器的封装"><a href="#定时器的封装" class="headerlink" title="定时器的封装"></a>定时器的封装</h2><h3 id="为什么需要定时器"><a href="#为什么需要定时器" class="headerlink" title="为什么需要定时器"></a>为什么需要定时器</h3><ul><li>为什么需要定时器?<ul><li>定时器是网路程序要处理的第三类事件,这里主要用来处理非活动连接,一个连接长时间没有响应,为了节省有限的系统资源,就要关闭这个这个连接,资源分给其他客户端,保证服务器的运行效率。</li><li>定时器能在预期的时间点发生,且不影响服务器的主要逻辑。</li></ul></li></ul><p>下面是我画的一个框架图</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430111306.png " alt="image.png" style="zoom:60%;" /><h3 id="定时器类"><a href="#定时器类" class="headerlink" title="定时器类"></a>定时器类</h3><p>我们先定义一个定时器类,定时器类里我们封装连接资源,定时事件指针,以及超时时间。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*util_timer前置声明,因为client_data使用了util_timer类*/</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">util_timer</span>;</span><br><span class="line"> </span><br><span class="line"><span class="comment">/*用户数据结构体(连接资源)*/</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">client_data</span>{</span><br><span class="line"> sockaddr_in address;<span class="comment">//客户端的socket地址</span></span><br><span class="line"> <span class="type">int</span> sockfd; <span class="comment">//socket文件描述符</span></span><br><span class="line"> util_timer *timer; <span class="comment">//定时器</span></span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line"><span class="comment">/*定时器类*/</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">util_timer</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">util_timer</span>():<span class="built_in">prev</span>(<span class="literal">nullptr</span>), <span class="built_in">next</span>(<span class="literal">nullptr</span>){}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="type">time_t</span> expire; <span class="comment">//超时时间</span></span><br><span class="line"> <span class="comment">/*回调函数声明:声明一个返回值为空的函数指针cb_func,传入clent_data指针作为函数参数*/</span></span><br><span class="line"> <span class="built_in">void</span> (*cb_func)(client_data *); <span class="comment">//回调函数指针</span></span><br><span class="line"> client_data *user_data; <span class="comment">//连接资源</span></span><br><span class="line"> </span><br><span class="line"> util_timer *prev; <span class="comment">//前向定时器</span></span><br><span class="line"> util_timer *next; <span class="comment">//后继定时器</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="上升链表类"><a href="#上升链表类" class="headerlink" title="上升链表类"></a>上升链表类</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">sort_timer_lst</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">sort_timer_lst</span>();</span><br><span class="line"> ~<span class="built_in">sort_timer_lst</span>();</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">add_timer</span><span class="params">(util_timer *timer)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">del_timer</span><span class="params">(util_timer *timer)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">adjust_timer</span><span class="params">(util_timer *timer)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">tick</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">add_timer</span><span class="params">(util_timer *timer,util_timer* lst_head)</span></span>;</span><br><span class="line"> util_timer *head;</span><br><span class="line"> util_timer *tail;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>下面的代码都是参考《Linux高性能服务器编程》</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><span class="line">sort_timer_lst::<span class="built_in">sort_timer_lst</span>() {</span><br><span class="line"> head= <span class="literal">nullptr</span>;</span><br><span class="line"> tail= <span class="literal">nullptr</span>;</span><br><span class="line">}</span><br><span class="line">sort_timer_lst::~<span class="built_in">sort_timer_lst</span>() {</span><br><span class="line"> util_timer *tmp=head;</span><br><span class="line"> <span class="keyword">while</span>(tmp){</span><br><span class="line"> head=tmp->next;</span><br><span class="line"> <span class="keyword">delete</span> tmp;</span><br><span class="line"> tmp=head;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sort_timer_lst::add_timer</span><span class="params">(util_timer *timer)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (timer== <span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">if</span>(head== <span class="literal">nullptr</span>&&tail== <span class="literal">nullptr</span>){</span><br><span class="line"> head=tail=timer;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 插在头部</span></span><br><span class="line"> <span class="keyword">if</span>(head!= <span class="literal">nullptr</span>&&timer->expire<head->expire){</span><br><span class="line"> timer->next=head;</span><br><span class="line"> head->prev=timer;</span><br><span class="line"> timer->prev= <span class="literal">nullptr</span>;</span><br><span class="line"> head=timer;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 插在尾部</span></span><br><span class="line"> <span class="keyword">if</span>(tail!= <span class="literal">nullptr</span>&&timer->expire>tail->expire){</span><br><span class="line"> tail->next=timer;</span><br><span class="line"> timer->prev=tail;</span><br><span class="line"> timer->next= <span class="literal">nullptr</span>;</span><br><span class="line"> tail=timer;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 插在中间,只能搜索了</span></span><br><span class="line"> <span class="built_in">add_timer</span>(timer,head);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sort_timer_lst::add_timer</span><span class="params">(util_timer *timer, util_timer *lst_head)</span> </span>{</span><br><span class="line"> <span class="comment">// 从头部开始查找第一个大于超时大于timer位置进行插入</span></span><br><span class="line"> util_timer *pre=lst_head;</span><br><span class="line"> util_timer *cur=lst_head->next;</span><br><span class="line"> <span class="keyword">while</span>(cur){</span><br><span class="line"> <span class="keyword">if</span>(cur->expire>timer->expire){</span><br><span class="line"> timer->prev=pre;</span><br><span class="line"> timer->next=cur;</span><br><span class="line"> pre->next=timer;</span><br><span class="line"> cur->prev=timer;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> cur=cur->next;</span><br><span class="line"> pre=pre->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(cur== <span class="literal">nullptr</span>){</span><br><span class="line"> pre->next=timer;</span><br><span class="line"> timer->prev=pre;</span><br><span class="line"> timer->next= <span class="literal">nullptr</span>;</span><br><span class="line"> tail=timer;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sort_timer_lst::del_timer</span><span class="params">(util_timer *timer)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (timer== <span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">if</span>((timer==head)&&(timer==tail)){</span><br><span class="line"> <span class="keyword">delete</span> timer;</span><br><span class="line"> head= <span class="literal">nullptr</span>;</span><br><span class="line"> tail= <span class="literal">nullptr</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(timer==head){</span><br><span class="line"> <span class="keyword">auto</span> tmp=head;</span><br><span class="line"> head=head->next;</span><br><span class="line"> head->prev= <span class="literal">nullptr</span>;</span><br><span class="line"> <span class="keyword">delete</span> timer;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(timer==tail){</span><br><span class="line"> <span class="keyword">auto</span> tmp=tail;</span><br><span class="line"> tail=tail->prev;</span><br><span class="line"> tail->next= <span class="literal">nullptr</span>;</span><br><span class="line"> <span class="keyword">delete</span> timer;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> timer->prev->next=timer->next;</span><br><span class="line"> timer->next->prev=timer->prev;</span><br><span class="line"> <span class="keyword">delete</span> timer;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sort_timer_lst::adjust_timer</span><span class="params">(util_timer *timer)</span> </span>{</span><br><span class="line"> <span class="comment">// 这个只负责延长时间</span></span><br><span class="line"> <span class="keyword">if</span> (timer==<span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">auto</span> tmp=timer->next;</span><br><span class="line"> <span class="comment">// 延长后比最小的还小</span></span><br><span class="line"> <span class="keyword">if</span>(tmp== <span class="literal">nullptr</span>||timer->expire<tmp->expire){</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(timer==head){</span><br><span class="line"> head=head->next;</span><br><span class="line"> head->prev= <span class="literal">nullptr</span>;</span><br><span class="line"> timer->next= <span class="literal">nullptr</span>;</span><br><span class="line"> <span class="built_in">add_timer</span>(timer,head);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">// 先删除后插入</span></span><br><span class="line"> timer->prev->next = timer->next;</span><br><span class="line"> timer->next->prev = timer->prev;</span><br><span class="line"> <span class="built_in">add_timer</span>(timer, timer->next);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>接下来是一个tick函数,就是从头节点开始查找直到一个未到期的定时器,之前的都要触发该定时器的回调函数并从链表定时器中删除,而该回调函数所作的事情就是将这个sockfd从epoll等待队列中删除并关闭这个文件描述符。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">sort_timer_lst::tick</span><span class="params">()</span> </span>{ <span class="comment">// 就是从头节点开始查找直到一个未到期的定时器,之前的都要触发该定时器的回调函数</span></span><br><span class="line"> <span class="keyword">if</span>(head== <span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> <span class="type">time_t</span> cur= <span class="built_in">time</span>(<span class="literal">nullptr</span>);</span><br><span class="line"> util_timer *tmp=head;</span><br><span class="line"> <span class="keyword">while</span>(tmp){</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(tmp->expire>cur){</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> tmp-><span class="built_in">cb_func</span>(tmp->user_data);</span><br><span class="line"></span><br><span class="line"> head=tmp->next;</span><br><span class="line"> <span class="keyword">if</span>(head){</span><br><span class="line"> head->prev= <span class="literal">nullptr</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">delete</span> tmp;</span><br><span class="line"> tmp=head;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">cb_func</span><span class="params">(client_data *uer_data)</span></span>{</span><br><span class="line"></span><br><span class="line"> <span class="built_in">epoll_ctl</span>(Utils::u_epollfd,EPOLL_CTL_DEL,uer_data->sockfd, <span class="literal">nullptr</span>);</span><br><span class="line"> <span class="built_in">assert</span>(uer_data);</span><br><span class="line"> <span class="built_in">close</span>(uer_data->sockfd);</span><br><span class="line"> <span class="comment">// 后面补充</span></span><br><span class="line"> http_conn::m_user_count--;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="抽象工具类"><a href="#抽象工具类" class="headerlink" title="抽象工具类"></a>抽象工具类</h3><p>定时器设计基本完成了,这个工具类就是用来合理使用它。</p><p>这个工具类的作用主要是通知主线程一些事件,这里的通知方式是采用<strong>信号</strong>,这里采用<strong>统一事件源</strong>,信号出现的时候不是立即去执行信号处理函数(或者真正的信号处理逻辑函数),而是通过管道发送给主进程信号的编号,主循环收到信号就记录下来,等其他IO事件完成之后,就调用tick()处理非活动连接。</p><p>下面使用这个工具类,具体可以上面的框架图</p><ol><li>主线程初始化这个Utils类</li><li>主线程调用addfd将pipe管道与epollfd相关联</li><li>主线程调用addsig将目标信号(SIGALRM SIGTERM)加入监听的信号集</li><li>主线程循环eventLoop()开始,服务器开始运行</li><li>多个客户连接长久未响应</li><li>经过TIMESLOT,触发信号,主循环收到信号</li><li>主循环调用tick()处理非活动连接</li></ol><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Utils</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Utils</span>() = <span class="keyword">default</span>;</span><br><span class="line"> ~<span class="built_in">Utils</span>() = <span class="keyword">default</span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">int</span> time_slot)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">addfd</span><span class="params">(<span class="type">int</span> epollfd,<span class="type">int</span> fd,<span class="type">bool</span> one_shot,<span class="type">int</span> TRIGMode)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">addsig</span><span class="params">(<span class="type">int</span> sig,<span class="type">void</span>(handler)(<span class="type">int</span>),<span class="type">bool</span> restart= <span class="literal">true</span>)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">sig_handler</span><span class="params">(<span class="type">int</span> sig)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show_errno</span><span class="params">(<span class="type">int</span> connfd,<span class="type">const</span> <span class="type">char</span> *info)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">time_handler</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="type">static</span> <span class="type">int</span> *u_pipefd;</span><br><span class="line"> <span class="type">static</span> <span class="type">int</span> u_epollfd;</span><br><span class="line"> <span class="type">int</span> m_TIMESLOT{};</span><br><span class="line"> sort_timer_lst m_time_lst;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>* Utils::u_pipefd=<span class="literal">nullptr</span>;</span><br><span class="line"><span class="type">int</span> Utils::u_epollfd=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::init</span><span class="params">(<span class="type">int</span> time_slot)</span> </span>{</span><br><span class="line"> m_TIMESLOT=time_slot;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">Utils::setnonblocking</span><span class="params">(<span class="type">int</span> fd)</span> </span>{</span><br><span class="line"> <span class="type">int</span> old_op=<span class="built_in">fcntl</span>(fd,F_GETFL);</span><br><span class="line"> <span class="type">int</span> new_op=old_op|O_NONBLOCK;</span><br><span class="line"> <span class="built_in">fcntl</span>(fd,F_SETFL,new_op);</span><br><span class="line"> <span class="keyword">return</span> old_op;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::addfd</span><span class="params">(<span class="type">int</span> epollfd, <span class="type">int</span> fd, <span class="type">bool</span> one_shot, <span class="type">int</span> TRIGMode)</span> </span>{</span><br><span class="line"> epoll_event event{};</span><br><span class="line"> event.data.fd=fd;</span><br><span class="line"> <span class="keyword">if</span>(one_shot)</span><br><span class="line"> event.events|=EPOLLONESHOT;</span><br><span class="line"> <span class="keyword">if</span>(TRIGMode==<span class="number">1</span>)</span><br><span class="line"> event.events=EPOLLIN|EPOLLET|EPOLLHUP;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> event.events=EPOLLIN|EPOLLHUP;</span><br><span class="line"> <span class="built_in">epoll_ctl</span>(epollfd,EPOLL_CTL_ADD,fd,&event);</span><br><span class="line"> <span class="built_in">setnonblocking</span>(fd);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::addsig</span><span class="params">(<span class="type">int</span> sig, <span class="type">void</span> (handler)(<span class="type">int</span>), <span class="type">bool</span> restart)</span> </span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">sigaction</span> sa{};</span><br><span class="line"> <span class="built_in">memset</span>(&sa,<span class="string">'\0'</span>, <span class="built_in">sizeof</span>(sa));</span><br><span class="line"> sa.sa_handler=handler;</span><br><span class="line"> <span class="keyword">if</span>(restart)</span><br><span class="line"> sa.sa_flags|=SA_RESTART;</span><br><span class="line"> <span class="built_in">sigfillset</span>(&sa.sa_mask);</span><br><span class="line"> <span class="built_in">assert</span>(<span class="built_in">sigaction</span>(sig, &sa, <span class="literal">nullptr</span>) != <span class="number">-1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::sig_handler</span><span class="params">(<span class="type">int</span> sig)</span> </span>{</span><br><span class="line"> <span class="type">int</span> olderrno=errno;</span><br><span class="line"> <span class="type">int</span> msg=sig;</span><br><span class="line"> <span class="built_in">send</span>(u_pipefd[<span class="number">1</span>],(<span class="type">char</span> *)&msg,<span class="number">1</span>,<span class="number">0</span>);</span><br><span class="line"> errno=olderrno;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::show_errno</span><span class="params">(<span class="type">int</span> connfd, <span class="type">const</span> <span class="type">char</span> *info)</span> </span>{</span><br><span class="line"> <span class="built_in">send</span>(connfd,info, <span class="built_in">strlen</span>(info), <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">close</span>(connfd);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Utils::time_handler</span><span class="params">()</span> </span>{</span><br><span class="line"> m_time_lst.<span class="built_in">tick</span>();</span><br><span class="line"> <span class="built_in">alarm</span>(m_TIMESLOT);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="半同步-半反应堆线程池"><a href="#半同步-半反应堆线程池" class="headerlink" title="半同步/半反应堆线程池"></a>半同步/半反应堆线程池</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>先来看下半同步半反应堆的框架吧</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240430112445.png" alt="image.png" style="zoom:60%;" /><p>几个问题?</p><ol><li>请求队列和线程池的关系? –> 请求队列就是线程池中常见的工作队列,放在I/O处理单元和逻辑单元之间是<strong>各单元之间的通信方式的抽象</strong>,请求请求存放的是TCP连接</li><li>哪些地方需要锁和信号量? –> 这里用锁和信号量完成生产者消费者模式,生产者就是往工作队列放任务,消费者就是线程池的工作线程处理任务</li><li>在线程池中数据库连接池完成什么作用 –> 主要用于reactor,因为在reactor模式下数据的读写操作由工作线程完成,这里用数据库的RAII类获得一个mysql连接,用于业务逻辑的增删改查。</li><li>请求队列放的是什么? –> 按线程池角度就是任务,但是按服务器编程框架来说,这里放的是客户端的连接,这个就是后面的<code>http</code>类,考虑代码的复用性,这里用了模板。</li></ol><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">thread_pool</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">thread_pool</span>(<span class="type">int</span> actor_mode,connection_pool* connPool,<span class="type">int</span> thread_num=<span class="number">8</span>,<span class="type">int</span> max_quest=<span class="number">10000</span>);</span><br><span class="line"> ~<span class="built_in">thread_pool</span>();</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">append</span><span class="params">(T *request,<span class="type">int</span> state)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">append_p</span><span class="params">(T *request)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">void</span>* <span class="title">worker</span><span class="params">(<span class="type">void</span> *arg)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">run</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> m_thread_num;</span><br><span class="line"> <span class="type">int</span> m_max_quest;</span><br><span class="line"> <span class="type">pthread_t</span>* m_threads;</span><br><span class="line"> connection_pool* m_connPool{};</span><br><span class="line"> <span class="type">int</span> m_actor_mode;</span><br><span class="line"> list<T *>m_work_queue; <span class="comment">// 请求队列 </span></span><br><span class="line"> locker m_queue_locker;</span><br><span class="line"> sem m_queue_stat;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="初始化-1"><a href="#初始化-1" class="headerlink" title="初始化"></a>初始化</h3><p>初始化操作就是创建thread_num个线程,这里用了线程分离,不会担心线程的资源回收问题,也避免了僵尸线程。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line">thread_pool<T>::<span class="built_in">thread_pool</span>(<span class="type">int</span> actor_mode, connection_pool *connPool, <span class="type">int</span> thread_num, <span class="type">int</span> max_quest):<span class="built_in">m_thread_num</span>(thread_num),<span class="built_in">m_max_quest</span>(max_quest),<span class="built_in">m_threads</span>(<span class="literal">nullptr</span>),<span class="built_in">m_connPool</span>(connPool),<span class="built_in">m_actor_mode</span>(actor_mode)</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span>(thread_num<=<span class="number">0</span>||max_quest<=<span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"> m_threads=<span class="keyword">new</span> <span class="type">pthread_t</span>[m_thread_num];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<thread_num;++i){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">pthread_create</span>(m_threads+i, <span class="literal">nullptr</span>,worker,<span class="keyword">this</span>)!=<span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">delete</span> []m_threads;</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">pthread_detach</span>(m_threads[i]))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">delete</span> []m_threads;</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="添加事件"><a href="#添加事件" class="headerlink" title="添加事件"></a>添加事件</h3><p>就是简单的链表添加操作。但注意,添加完后就可以唤醒线程来取事件处理进行处理。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="type">bool</span> thread_pool<T>::<span class="built_in">append</span>(T *request, <span class="type">int</span> state) {</span><br><span class="line"> m_queue_locker.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(!request||m_work_queue.<span class="built_in">size</span>()>=m_max_quest) {</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> request->m_state=state;</span><br><span class="line"> m_work_queue.<span class="built_in">push_back</span>(request);</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> m_queue_stat.<span class="built_in">post</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="type">bool</span> thread_pool<T>::<span class="built_in">append_p</span>(T *request) {</span><br><span class="line"></span><br><span class="line"> m_queue_locker.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(!request||m_work_queue.<span class="built_in">size</span>()>=m_max_quest) {</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> m_work_queue.<span class="built_in">push_back</span>(request);</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> m_queue_stat.<span class="built_in">post</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="worker和run"><a href="#worker和run" class="headerlink" title="worker和run"></a>worker和run</h3><p>woker函数就是创建线程的工作函数,这里用了类的静态成员函数作为工作函数解决了work函数不能有参数的问题,因为每个类的<strong>非静态的类成员函数都有一个隐藏参数this指针。</strong> run函数就是在循环中处理逻辑。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="type">void</span> *thread_pool<T>::<span class="built_in">worker</span>(<span class="type">void</span> *arg) {</span><br><span class="line"> <span class="keyword">auto</span> *pool=(thread_pool*)arg;</span><br><span class="line"> pool-><span class="built_in">run</span>();</span><br><span class="line"> <span class="keyword">return</span> pool;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="type">void</span> thread_pool<T>::<span class="built_in">run</span>() {</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>){</span><br><span class="line"> m_queue_stat.<span class="built_in">wait</span>();</span><br><span class="line"> m_queue_locker.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(m_work_queue.<span class="built_in">empty</span>()){</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> T* request=m_work_queue.<span class="built_in">front</span>();</span><br><span class="line"> m_work_queue.<span class="built_in">pop_front</span>();</span><br><span class="line"> m_queue_locker.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">if</span>(!request) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="comment">// 需要补充</span></span><br><span class="line"> <span class="keyword">if</span>(m_actor_mode==<span class="number">1</span>){ <span class="comment">// reator模式</span></span><br><span class="line"> <span class="keyword">if</span>(request->m_state==<span class="number">0</span>){ <span class="comment">// 读模式</span></span><br><span class="line"> <span class="keyword">if</span>(request-><span class="built_in">read_once</span>()){</span><br><span class="line"> request->improv=<span class="number">1</span>;</span><br><span class="line"> <span class="function">connection_poolRAII <span class="title">mysqlconn</span><span class="params">(&request->mysql,m_connPool)</span></span>;</span><br><span class="line"> request-><span class="built_in">process</span>();</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> request->improv=<span class="number">1</span>;</span><br><span class="line"> request->timer_flag=<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">// 写模式</span></span><br><span class="line"> <span class="keyword">if</span>(request-><span class="built_in">write</span>()){</span><br><span class="line"> request->improv=<span class="number">1</span>;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> request->improv=<span class="number">1</span>;</span><br><span class="line"> request->timer_flag=<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">// Proactor模式</span></span><br><span class="line"> connection_poolRAII <span class="built_in">mysqlconn</span>(&request->mysql,m_connPool);</span><br><span class="line"> request-><span class="built_in">process</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 一起做项目 </category>
</categories>
<tags>
<tag> 项目 </tag>
</tags>
</entry>
<entry>
<title>一起写webserver 项目(二)</title>
<link href="/2024/04/29/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%BA%8C)/"/>
<url>/2024/04/29/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%BA%8C)/</url>
<content type="html"><![CDATA[<h2 id="日志和同步原语的封装"><a href="#日志和同步原语的封装" class="headerlink" title="日志和同步原语的封装"></a>日志和同步原语的封装</h2><blockquote><p>写一个项目,一开始不知道从哪里开始,我的经验是大致看一下主函数,了解一下有哪些模块?然后从简单的模块开始逐个看,看完了记住流程思路,我们就可以复现,然后这样虽然阅读起来比整体一行一行看简单,但是各个模块之间相互穿插,单个模块看,理解不了它们之间的关系,所以建议所有模块看完后,再重新看一遍,把整个流程串通,这样整个项目就非常清晰了。</p></blockquote><p>其实我们也可以看作者的Readme的整体框架图</p><img src="https://camo.githubusercontent.com/326c456073716c6a81d925154df43a7787cf4088b794590c76a0f122274e7ef4/687474703a2f2f7777312e73696e61696d672e636e2f6c617267652f303035544a3263376c79316765306a3161747135686a33306736306c6d3077342e6a7067" alt="image.png" style="zoom:80%;" /><h3 id="Locker类"><a href="#Locker类" class="headerlink" title="Locker类"></a>Locker类</h3><p>这里的locker的封装是参考《Linux高性能服务器编程》,用了RAII的思想,即将<strong>资源的获取和释放绑定在对象的生命周期</strong>中。比较简单就不用怎么叙述了,其实这里完全可以c++的同步原语语法,感兴趣的读者可以自行尝试</p><ul><li><p>信号量</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">sem</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">sem</span>(){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sem_init</span>(&m_sem,<span class="number">0</span>,<span class="number">0</span>)!=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">sem</span><span class="params">(<span class="type">int</span> num)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">sem_init</span>(&m_sem,<span class="number">0</span>,num)!=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">sem</span>(){</span><br><span class="line"> <span class="built_in">sem_destroy</span>(&m_sem);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">wait</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">sem_wait</span>(&m_sem)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">post</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">sem_post</span>(&m_sem)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">sem_t</span> m_sem{};</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li><li><p>同步锁</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">locker</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">locker</span>(){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">pthread_mutex_init</span>(&mutex,<span class="literal">nullptr</span>)!=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">locker</span>(){</span><br><span class="line"> <span class="built_in">pthread_mutex_destroy</span>(&mutex);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">lock</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_mutex_lock</span>(&mutex)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">unlock</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_mutex_unlock</span>(&mutex)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">trylock</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_mutex_trylock</span>(&mutex)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">pthread_mutex_t</span> *<span class="title">get</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> &mutex;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">pthread_mutex_t</span> mutex{};</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li><li><p>条件变量</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">cond</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">cond</span>(){</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">pthread_cond_init</span>(&m_cond, <span class="literal">nullptr</span>)!=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">cond</span>(){</span><br><span class="line"> <span class="built_in">pthread_cond_destroy</span>(&m_cond);</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">wait</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_cond_wait</span>(&m_cond,mutex)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">timewait</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex,<span class="keyword">struct</span> timespec t)</span></span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_cond_timedwait</span>(&m_cond,mutex,&t)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">signal</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_cond_signal</span>(&m_cond)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">broadcast</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">pthread_cond_broadcast</span>(&m_cond)==<span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">pthread_cond_t</span> m_cond{};</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ul><h2 id="LOG类"><a href="#LOG类" class="headerlink" title="LOG类"></a>LOG类</h2><p>LOG类就是项目中常见的日志系统,由服务器自动创建,并记录运行状态,错误信息,访问数据的文件。<br>从框架图我们可以看出,这里的日志分为<strong>同步日志</strong>和<strong>异步日志</strong>。</p><p><strong>同步日志</strong>:日志写入函数与工作线程<strong>串行执行</strong>,由于涉及<strong>I/O操作</strong>,同步日志会阻塞整个处理流程,服务器所能处理的并发能力将有所下降,尤其是在访问峰值时,写日志可能会成为系统的瓶颈。</p><p><strong>异步日志</strong>:将工作线程所写的日志内容先存入<strong>阻塞队列</strong>,专门的一个线程与主线程并行执行的关系,这个线程从阻塞队列中取出内容,写入日志,从而不影响主线程。</p><p>其实异步日志是一个典型的<strong>生产者-消费者模型</strong>。其中工作线程时生产,写线程是消费者。生产者-消费者模型的临界区(缓冲区)是什么呢?在这个日志系统中,这个临界区就是一个<strong>阻塞队列</strong></p><h3 id="循环队列"><a href="#循环队列" class="headerlink" title="循环队列"></a>循环队列</h3><ul><li><p>阻塞队列实现</p> <figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">class</span> <span class="title class_">T</span>></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">block_queue</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 构造 析构 clear full empty size() maxsize() back front push pop pop()</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">block_queue</span><span class="params">(<span class="type">int</span> maxsize=<span class="number">1000</span>)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(maxsize<=<span class="number">0</span>){</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line"> }</span><br><span class="line"> m_size=<span class="number">0</span>;</span><br><span class="line"> m_maxsize=maxsize;</span><br><span class="line"> m_array=<span class="keyword">new</span> T[maxsize];</span><br><span class="line"> m_front=<span class="number">-1</span>;</span><br><span class="line"> m_back=<span class="number">-1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ~<span class="built_in">block_queue</span>(){</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">delete</span>[] m_array;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">()</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> m_size=<span class="number">0</span>;</span><br><span class="line"> m_front=<span class="number">-1</span>;</span><br><span class="line"> m_back=<span class="number">-1</span>;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">full</span><span class="params">()</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_size==m_maxsize) {</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">empty</span><span class="params">()</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_size==<span class="number">0</span>){</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">front</span><span class="params">(T &item)</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(m_size==<span class="number">0</span>){</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> m_array[m_front]=item;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">back</span><span class="params">(T &item)</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(m_size==<span class="number">0</span>){</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> m_array[m_back]=item;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">size</span><span class="params">()</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="type">int</span> tmp=<span class="number">0</span>;</span><br><span class="line"> tmp=m_size;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> tmp;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">maxsize</span><span class="params">()</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="type">int</span> tmp=<span class="number">0</span>;</span><br><span class="line"> tmp=m_maxsize;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> tmp;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">push</span><span class="params">(<span class="type">const</span> T&item)</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">if</span>(m_size==m_maxsize){ <span class="comment">//队列满了</span></span><br><span class="line"> m_cond.<span class="built_in">broadcast</span>();</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m_back=(m_back+<span class="number">1</span>)%m_maxsize;</span><br><span class="line"> m_array[m_back]=item;</span><br><span class="line"></span><br><span class="line"> m_size++;</span><br><span class="line"> m_cond.<span class="built_in">broadcast</span>();</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">pop</span><span class="params">(T &item)</span></span>{</span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">while</span> (m_size<=<span class="number">0</span>){ <span class="comment">// 防止虚假唤醒</span></span><br><span class="line"> <span class="keyword">if</span>(!m_cond.<span class="built_in">wait</span>(m_lock.<span class="built_in">get</span>())){</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> m_front=(m_front+<span class="number">1</span>)%m_maxsize;</span><br><span class="line"> item=m_array[m_front];</span><br><span class="line"></span><br><span class="line"> m_size--;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">pop</span><span class="params">(T &item,<span class="type">int</span> timeout)</span></span>{ <span class="comment">// 毫秒</span></span><br><span class="line"> m_lock.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timespec</span> t{<span class="number">0</span>,<span class="number">0</span>};</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timeval</span> now{<span class="number">0</span>,<span class="number">0</span>};</span><br><span class="line"></span><br><span class="line"> <span class="built_in">gettimeofday</span>(&now, <span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line"> t.tv_sec=now.tv_sec+timeout/<span class="number">1000</span>;</span><br><span class="line"> t.tv_nsec=(timeout%<span class="number">1000</span>)*<span class="number">1000</span>;</span><br><span class="line"> <span class="keyword">if</span>(m_size<=<span class="number">0</span>){</span><br><span class="line"> <span class="keyword">if</span>(!m_cond.<span class="built_in">timewait</span>(m_lock.<span class="built_in">get</span>(),t)){</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(m_size<=<span class="number">0</span>){ <span class="comment">// 锁加条件双重验证</span></span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m_front=(m_front+<span class="number">1</span>)%m_maxsize;</span><br><span class="line"> item=m_array[m_front];</span><br><span class="line"> m_size--;</span><br><span class="line"> m_lock.<span class="built_in">unlock</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> cond m_cond;</span><br><span class="line"> locker m_lock;</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> m_maxsize{};</span><br><span class="line"> <span class="type">int</span> m_size{};</span><br><span class="line"> T* m_array;</span><br><span class="line"> <span class="type">int</span> m_front{};</span><br><span class="line"> <span class="type">int</span> m_back{};</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p> 可以看出这里除了构造,大部分都是要加锁的,这里生产和消费的同步用的条件变量+同步锁</p></li></ul><h3 id="log"><a href="#log" class="headerlink" title="log"></a>log</h3><h4 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h4><p>这个项目的很多模块都用单例,单例模式保证了一个类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。</p><p>单例模式也分为两种,一种是懒汉模式:顾名思义,懒汉模式非常懒,当没有人用它的时候它就不初始化,只有被第一次使用时才去初始化;另一种是饿汉模式:与懒汉模式相反,程序运行时就立刻创建实例进行初始化。</p><p>经典的懒汉模式一般要使用<strong>双检测锁</strong>。但C++11之后,可以使用静态局部变量初始化,就不再需要锁,编译器会负责线程安全的问题。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Log</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="comment">// 采用懒汉的单例模式</span></span><br><span class="line"> <span class="function"><span class="type">static</span> Log* <span class="title">get_instance</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="type">static</span> Log instance;</span><br><span class="line"> <span class="keyword">return</span> &instance;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">void</span>* <span class="title">flush_log_thread</span><span class="params">(<span class="type">void</span> *args)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> Log::<span class="built_in">get_instance</span>()-><span class="built_in">async_write_log</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">init</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* file_name,<span class="type">int</span> close_log,<span class="type">int</span> log_buf_size,<span class="type">int</span> split_size,<span class="type">int</span> max_queue_size=<span class="number">0</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">write_log</span><span class="params">(<span class="type">int</span> level,<span class="type">const</span> <span class="type">char</span> *format,...)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">flush</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">static</span> tm <span class="title">get_time</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="built_in">Log</span>();</span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">Log</span>(){}</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">char</span> * m_buf{};</span><br><span class="line"> <span class="type">bool</span> m_is_async; <span class="comment">//是否同步</span></span><br><span class="line"> FILE *m_fp{}; <span class="comment">// 文件描述符</span></span><br><span class="line"> locker m_mutex; <span class="comment">// 互斥锁</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>一些关键的条件变量</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">char</span> log_name[<span class="number">128</span>]{}; <span class="comment">//日志文件名</span></span><br><span class="line"> <span class="type">char</span> dir_name[<span class="number">128</span>]{}; <span class="comment">//目录名称</span></span><br><span class="line"> <span class="type">int</span> m_close_log{};</span><br><span class="line"> <span class="type">long</span> <span class="type">long</span> m_count; <span class="comment">//日志行数</span></span><br><span class="line"> <span class="type">int</span> m_split_size{}; <span class="comment">//日志最大行数</span></span><br><span class="line"> <span class="type">int</span> m_log_buf_size{}; <span class="comment">// 日志缓冲区大小</span></span><br><span class="line"> <span class="type">int</span> m_today{};</span><br><span class="line"> <span class="type">char</span> * m_buf{};</span><br><span class="line"> block_queue<std::string> *m_block_queue{}; <span class="comment">// 阻塞队列(异步使用)</span></span><br><span class="line"> <span class="type">bool</span> m_is_async; <span class="comment">//是否同步</span></span><br><span class="line"> FILE *m_fp{}; <span class="comment">// 文件描述符</span></span><br><span class="line"> locker m_mutex; <span class="comment">// 互斥锁</span></span><br></pre></td></tr></table></figure><h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><p>这里的根据是否设置有阻塞队列的长度判断是否是异步,因为同步用不到阻塞队列,采用异步,我们就要创建一个子进程,这个子进程会worker是这个log类的一个静态方法,<br>这个静态方法会调用log静态实例的一个私有方法,这个私有方法会不断检测阻塞队列中是否有信息,从阻塞队列的代码我们知道这个会阻塞,如果没有信息,有信息就会进行fputs的系统调用IO操作</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Log::init</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *name, <span class="type">int</span> close_log, <span class="type">int</span> log_buf_size=<span class="number">8192</span>, <span class="type">int</span> split_size=<span class="number">5000000</span>, <span class="type">int</span> max_queue_size)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(max_queue_size><span class="number">0</span>){ <span class="comment">// 异步</span></span><br><span class="line"> m_is_async= <span class="literal">true</span>;</span><br><span class="line"> m_block_queue=<span class="keyword">new</span> <span class="built_in">block_queue</span><std::string>(max_queue_size);</span><br><span class="line"> <span class="type">pthread_t</span> tid;</span><br><span class="line"> <span class="built_in">pthread_create</span>(&tid, <span class="literal">nullptr</span>,Log::flush_log_thread, <span class="literal">nullptr</span>);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span>* <span class="title">flush_log_thread</span><span class="params">(<span class="type">void</span> *args)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> Log::<span class="built_in">get_instance</span>()-><span class="built_in">async_write_log</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> *<span class="title">async_write_log</span><span class="params">()</span></span>{</span><br><span class="line"> std::string log_str;</span><br><span class="line"> <span class="keyword">while</span>(m_block_queue-><span class="built_in">pop</span>(log_str)){</span><br><span class="line"> m_mutex.<span class="built_in">lock</span>();</span><br><span class="line"> <span class="built_in">fputs</span>(log_str.<span class="built_in">c_str</span>(),m_fp);</span><br><span class="line"> m_mutex.<span class="built_in">unlock</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>下面的操作就是得到一个日志全名称full_log_name,这个名称就是日志名称+日期,我们文件就会创建或者打开这个full_log_name的文件</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">Log::init</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *name, <span class="type">int</span> close_log, <span class="type">int</span> log_buf_size=<span class="number">8192</span>, <span class="type">int</span> split_size=<span class="number">5000000</span>, <span class="type">int</span> max_queue_size)</span> </span>{</span><br><span class="line"></span><br><span class="line"> m_close_log=close_log;</span><br><span class="line"> m_split_size=split_size;</span><br><span class="line"> m_buf=<span class="keyword">new</span> <span class="type">char</span>[log_buf_size];</span><br><span class="line"> <span class="built_in">memset</span>(m_buf,<span class="string">'\0'</span>, m_log_buf_size);</span><br><span class="line"></span><br><span class="line"> tm my_tm=Log::<span class="built_in">get_time</span>();</span><br><span class="line"></span><br><span class="line"> m_today=my_tm.tm_mday;</span><br><span class="line"></span><br><span class="line"> <span class="type">const</span> <span class="type">char</span> *p=<span class="built_in">strchr</span>(name,<span class="string">'/'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">char</span> full_log_name[<span class="number">256</span>];</span><br><span class="line"> <span class="keyword">if</span>(p== <span class="literal">nullptr</span>){ <span class="comment">// 没有目录</span></span><br><span class="line"> <span class="built_in">strcpy</span>(log_name,name);</span><br><span class="line"> <span class="built_in">snprintf</span>(full_log_name,<span class="number">255</span>,<span class="string">"%d_%02d_%02d_%s"</span>,my_tm.tm_year+<span class="number">1900</span>,my_tm.tm_mon+<span class="number">1</span>,my_tm.tm_mday,log_name);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{ <span class="comment">//有目录</span></span><br><span class="line"> <span class="built_in">strcpy</span>(log_name,p+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">strncpy</span>(dir_name,name,p- name+<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">snprintf</span>(full_log_name,<span class="number">255</span>,<span class="string">"%s%d_%02d_%02d_%s"</span>,dir_name,my_tm.tm_year+<span class="number">1900</span>,my_tm.tm_mon+<span class="number">1</span>,my_tm.tm_mday,log_name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m_fp= <span class="built_in">fopen</span>(full_log_name,<span class="string">"a"</span>);</span><br><span class="line"> <span class="keyword">if</span>(m_fp== <span class="literal">nullptr</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><h4 id="write-log"><a href="#write-log" class="headerlink" title="write_log"></a>write_log</h4><p>这里的日志分了等级<br>Log分级:</p><ul><li>Debug,调试代码时的输出,在系统实际运行时,一般不使用。</li><li>Warn,这种警告与调试时终端的warning类似,同样是调试代码时使用。</li><li>Info,报告系统当前的状态,当前执行的流程或接收的信息等。</li><li>Erro,输出系统的错误信息</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Log::write_log</span><span class="params">(<span class="type">int</span> level,<span class="type">const</span> <span class="type">char</span> *format,...)</span> </span>{</span><br><span class="line"></span><br><span class="line"> tm my_tm=Log::<span class="built_in">get_time</span>();</span><br><span class="line"></span><br><span class="line"> <span class="type">char</span> s[<span class="number">16</span>]={<span class="number">0</span>};</span><br><span class="line"> <span class="keyword">switch</span> (level) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line"> <span class="built_in">strcpy</span>(s,<span class="string">"[debug]:"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="built_in">strcpy</span>(s,<span class="string">"[info]:"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> <span class="built_in">strcpy</span>(s,<span class="string">"[warn]:"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> <span class="built_in">strcpy</span>(s,<span class="string">"[error]:"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="built_in">strcpy</span>(s,<span class="string">"[info]:"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>这里的为了日志也是会被分文件的,有下面两种情况</p><ol><li>到了新的一天,这时的日志全名称就变了,就是新文件</li><li>日志写行数超过了限制的最大行数,这个就要序号分文件了</li></ol><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">m_mutex.<span class="built_in">lock</span>();</span><br><span class="line">m_count++;</span><br><span class="line"><span class="keyword">if</span>(my_tm.tm_mday!=m_today||m_count%m_split_size==<span class="number">0</span>){</span><br><span class="line"> <span class="type">char</span> new_log[<span class="number">256</span>]={<span class="number">0</span>};</span><br><span class="line"> <span class="built_in">fflush</span>(m_fp);</span><br><span class="line"> <span class="built_in">fclose</span>(m_fp);</span><br><span class="line"> <span class="type">char</span> tail[<span class="number">16</span>]={<span class="number">0</span>};</span><br><span class="line"> <span class="built_in">snprintf</span>(tail,<span class="number">16</span>,<span class="string">"%d_%02d_%02d_"</span>,my_tm.tm_year+<span class="number">1900</span>,my_tm.tm_mon+<span class="number">1</span>,my_tm.tm_mday);</span><br><span class="line"> <span class="keyword">if</span>(my_tm.tm_mday!=m_today){ <span class="comment">// 日期不同,要新建日志文件</span></span><br><span class="line"> m_count=<span class="number">0</span>;</span><br><span class="line"> <span class="built_in">snprintf</span>(new_log,<span class="number">255</span>,<span class="string">"%s%s%s"</span>,dir_name,tail,log_name);</span><br><span class="line"> m_today=my_tm.tm_mday;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(m_count%m_split_size==<span class="number">0</span>){</span><br><span class="line"> <span class="built_in">snprintf</span>(new_log, <span class="number">255</span>, <span class="string">"%s%s%s.%lld"</span>, dir_name, tail, log_name, m_count / m_split_size);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> m_fp=<span class="built_in">fopen</span>(new_log,<span class="string">"a"</span>);</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">m_mutex.<span class="built_in">unlock</span>();</span><br></pre></td></tr></table></figure><p>这里write_log其实是用了c语言的可变参数的,同时搭配了vsnprintf,让传参数更灵活</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">va_list list;</span><br><span class="line"><span class="built_in">va_start</span>(list,format);</span><br><span class="line">std::string log_str;</span><br><span class="line">m_mutex.<span class="built_in">lock</span>();</span><br><span class="line"><span class="type">int</span> n= <span class="built_in">snprintf</span>(m_buf,<span class="number">48</span>,<span class="string">"%d-%01d-%01d %01d:%01d:%01d %s"</span>,my_tm.tm_year+<span class="number">1900</span>,my_tm.tm_mon+<span class="number">1</span>,my_tm.tm_mday,my_tm.tm_hour,my_tm.tm_min,my_tm.tm_sec,s);</span><br><span class="line"><span class="type">int</span> m= <span class="built_in">vsnprintf</span>(m_buf+n,m_log_buf_size-n<span class="number">-1</span>,format,list);</span><br><span class="line">m_buf[m+n]=<span class="string">'\n'</span>;</span><br><span class="line">m_buf[m+n+<span class="number">1</span>]=<span class="string">'\0'</span>;</span><br><span class="line">log_str=m_buf;</span><br></pre></td></tr></table></figure><p>平时调用写日志是用定义成不同等级的宏,这样方便书写</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> LOG_DEBUG(foramt,...) <span class="keyword">if</span>(m_close_log==0) { Log::get_instance()->write_log(0,foramt, ##__VA_ARGS__); Log::get_instance()->flush();};</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LOG_INFO(foramt,...) <span class="keyword">if</span>(m_close_log==0) { Log::get_instance()->write_log(1,foramt, ##__VA_ARGS__); Log::get_instance()->flush();};</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LOG_WARN(foramt,...) <span class="keyword">if</span>(m_close_log==0) { Log::get_instance()->write_log(2,foramt, ##__VA_ARGS__); Log::get_instance()->flush();};</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LOG_ERROR(foramt,...) <span class="keyword">if</span>(m_close_log==0) { Log::get_instance()->write_log(3,foramt, ##__VA_ARGS__); Log::get_instance()->flush();};</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 一起做项目 </category>
</categories>
<tags>
<tag> 项目 </tag>
</tags>
</entry>
<entry>
<title>一起写webserver 项目(一)</title>
<link href="/2024/04/29/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%B8%80)/"/>
<url>/2024/04/29/%E4%B8%80%E8%B5%B7%E5%86%99webserver%20%E9%A1%B9%E7%9B%AE(%E4%B8%80)/</url>
<content type="html"><![CDATA[<p>在做整个项目之前建议看完《Linux高性能服务器编程》-中国-游双,这个项目会用到里面的很多知识。</p><h2 id="webserver-环境配置"><a href="#webserver-环境配置" class="headerlink" title="webserver 环境配置"></a>webserver 环境配置</h2><h3 id="安装Linux环境"><a href="#安装Linux环境" class="headerlink" title="安装Linux环境"></a>安装Linux环境</h3><p>webserver作为c++的一个经典项目,虽然烂大街,但是对于网络编程和系统编程非常重要,几乎等同于spring于java,可以不用,但基本要会。</p><p>选择一个github的项目 <a href="https://github.com/qinguoyi/TinyWebServer">GitHub - qinguoyi/TinyWebServer: :fire: Linux下C++轻量级WebServer服务器</a></p><p>我用的是wsl2子系统,发行版是Debian系统,gcc、g++默认都已经安装了,推荐大家用云服务器,这样项目运行访问时就不必是回环地址,这样更贴合生产环境。</p><h3 id="安装MYSQL"><a href="#安装MYSQL" class="headerlink" title="安装MYSQL"></a>安装MYSQL</h3><p>使用<strong>apt</strong>包管理器:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get install mysql-server</span><br></pre></td></tr></table></figure><p>其他发行版也同理</p><h3 id="运行项目"><a href="#运行项目" class="headerlink" title="运行项目"></a>运行项目</h3><p>然后就是克隆项目运行</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git init <span class="comment">## 将本地仓库初始化</span></span><br><span class="line">git <span class="built_in">clone</span> <url> <span class="comment">## 将需要的项⽬从 github 上克隆下来,url为项⽬地址</span></span><br></pre></td></tr></table></figure><p>测试前确认已安装MySQL数据库(mysql的配置)</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator">/</span><span class="operator">/</span> 建⽴yourdb库</span><br><span class="line"><span class="keyword">create</span> database yourdb;</span><br><span class="line"><span class="operator">/</span><span class="operator">/</span> 创建<span class="keyword">user</span>表</span><br><span class="line">USE yourdb;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">user</span>(</span><br><span class="line">username <span class="type">char</span>(<span class="number">50</span>) <span class="keyword">NULL</span>,</span><br><span class="line">passwd <span class="type">char</span>(<span class="number">50</span>) <span class="keyword">NULL</span></span><br><span class="line">)ENGINE<span class="operator">=</span>InnoDB;</span><br><span class="line"><span class="operator">/</span><span class="operator">/</span> 添加数据</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> <span class="keyword">user</span>(username, passwd) <span class="keyword">VALUES</span>(<span class="string">'name'</span>, <span class="string">'passwd'</span>);</span><br></pre></td></tr></table></figure><p>修改main.cpp中的数据库初始化信息</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">//数据库登录名,密码,库名</span><br><span class="line">string user = <span class="string">"root"</span>;</span><br><span class="line">string passwd = <span class="string">"root"</span>;</span><br><span class="line">string databasename = <span class="string">"yourdb"</span>;</span><br></pre></td></tr></table></figure><p>随后我们执行</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh ./build.sh</span><br></pre></td></tr></table></figure><p>出现了BUG</p><p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240429210138.png" alt="image.png"></p><p>这里是缺少mysql库文件,我们去查一下GitHub上的issue,发现作者给了解决方法</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240429210238.png" alt="image.png" style="zoom:60%;" /><p>执行代码</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install libmysqlclient-dev</span><br></pre></td></tr></table></figure><p>再make一遍,果然不再报库文件缺失,至于warning不用管。<br>这时候ls一下,可以看到server可运行文件了</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./server</span><br></pre></td></tr></table></figure><img src="http://img-blog.csdnimg.cn/img_convert/e009c7109caf9c7308f3f6b3f5e2a390.png" alt="image.png" style="zoom:60%;" /><p>光标不动了,说明运行成功。</p><h3 id="浏览器访问"><a href="#浏览器访问" class="headerlink" title="浏览器访问"></a>浏览器访问</h3><p>接下来就是浏览器访问了,在保持服务器运行的情况下,打开浏览器</p><p>如果是虚拟机的同学,可以使用回环地址(不知道的翻一下计网的书)</p><blockquote><p>127.0.0.1:9006</p></blockquote><p>云服务器的同学,可以去管理台查一下自己的云服务器的公网IP,然后输入</p><blockquote><p>IP:9006</p></blockquote><p>如果发现打不开,就去服务器实例的防火墙(腾讯云)/安全组(阿里云)里面把9006端口设置为允许</p><img src="http://yesho-web.oss-cn-hangzhou.aliyuncs.com/img/20240429210447.png" alt="image.png" style="zoom:60%;" /><p>下面开始正式写代码!</p>]]></content>
<categories>
<category> 一起做项目 </category>
</categories>
<tags>
<tag> 项目 </tag>
</tags>
</entry>
<entry>
<title>hexo 教程</title>
<link href="/2024/03/20/hello-world/"/>
<url>/2024/03/20/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">hexo clean && hexo generate && hexo deploy // Git BASH终端</span><br><span class="line">hexo clean; hexo generate; hexo deploy // VSCODE终端</span><br></pre></td></tr></table></figure><ul><li>hexo clean:删除之前生成的文件,若未生成过静态文件,可忽略此命令。</li><li>hexo generate:生成静态文章,可以用hexo g缩写</li><li>hexo deploy:部署文章,可以用hexo d缩写</li></ul>]]></content>
<tags>
<tag> 教程 </tag>
</tags>
</entry>
</search>