-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
324 lines (141 loc) · 247 KB
/
atom.xml
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Lixiang's Blog</title>
<link href="https://lixiang365.github.io/atom.xml" rel="self"/>
<link href="https://lixiang365.github.io/"/>
<updated>2024-03-17T12:52:46.000Z</updated>
<id>https://lixiang365.github.io/</id>
<author>
<name>Lixiang</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>如何理解SO_REUSEADDR和SO_REUSEPORT?</title>
<link href="https://lixiang365.github.io/post/70e417a9.html"/>
<id>https://lixiang365.github.io/post/70e417a9.html</id>
<published>2024-03-17T12:52:00.000Z</published>
<updated>2024-03-17T12:52:46.000Z</updated>
<content type="html"><![CDATA[<p>注:(这篇文章原文是stack overflow的回答,觉得写的甚好,就转载保存下。<a href="https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ">原文连接</a>)</p><p>Welcome to the wonderful world of portability… or rather the lack of it. Before we start analyzing these two options in detail and take a deeper look how different operating systems handle them, it should be noted that the BSD socket implementation is the mother of all socket implementations. Basically all other systems copied the BSD socket implementation at some point in time (or at least its interfaces) and then started evolving it on their own. Of course the BSD socket implementation was evolved as well at the same time and thus systems that copied it later got features that were lacking in systems that copied it earlier. Understanding the BSD socket implementation is the key to understanding all other socket implementations, so you should read about it even if you don’t care to ever write code for a BSD system.<br>欢迎来到便携性的美妙世界……或者更确切地说是缺乏它。在我们开始详细分析这两个选项并更深入地了解不同的操作系统如何处理它们之前,应该注意的是,BSD 套接字实现是所有套接字实现之母。基本上,所有其他系统都在某个时间点(或至少是它的接口)复制了 BSD 套接字的实现,然后开始自己发展它。当然,BSD 套接字的实现也是同时发展起来的,因此后来复制它的系统获得了早期复制它的系统所缺乏的功能。了解 BSD 套接字实现是理解所有其他套接字实现的关键,因此即使您不想为 BSD 系统编写代码,也应该阅读它。</p><p>There are a couple of basics you should know before we look at these two options. A TCP/UDP connection is identified by a tuple of five values:<br>在我们研究这两个选项之前,您应该了解一些基础知识。TCP/UDP 连接由五个值的元组标识:</p><p><code>{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}</code></p><p>Any unique combination of these values identifies a connection. As a result, no two connections can have the same five values, otherwise the system would not be able to distinguish these connections any longer.<br>这些值的任何唯一组合都标识连接。因此,任何两个连接都不能具有相同的五个值,否则系统将无法再区分这些连接。</p><p>The protocol of a socket is set when a socket is created with the socket() function. The source address and port are set with the bind() function. The destination address and port are set with the connect() function. Since UDP is a connectionless protocol, UDP sockets can be used without connecting them. Yet it is allowed to connect them and in some cases very advantageous for your code and general application design. In connectionless mode, UDP sockets that were not explicitly bound when data is sent over them for the first time are usually automatically bound by the system, as an unbound UDP socket cannot receive any (reply) data. Same is true for an unbound TCP socket, it is automatically bound before it will be connected.<br>套接字的协议是在使用 socket() 函数创建套接字时设置的。源地址和端口通过 bind() 函数设置。目标地址和端口通过该 connect() 功能设置。由于 UDP 是一种无连接协议,因此可以在不连接 UDP 套接字的情况下使用 UDP 套接字。然而,它被允许连接它们,在某些情况下对你的代码和一般应用程序设计非常有利。在无连接模式下,首次通过UDP套接字发送数据时未显式绑定的UDP套接字通常由系统自动绑定,因为未绑定的UDP套接字无法接收任何(应答)数据。未绑定的 TCP 套接字也是如此,它在连接之前会自动绑定。</p><p>If you explicitly bind a socket, it is possible to bind it to port 0, which means “any port”. Since a socket cannot really be bound to all existing ports, the system will have to choose a specific port itself in that case (usually from a predefined, OS specific range of source ports). A similar wildcard exists for the source address, which can be “any address” (0.0.0.0 in case of IPv4 and :: in case of IPv6). Unlike in case of ports, a socket can really be bound to “any address” which means “all source IP addresses of all local interfaces”. If the socket is connected later on, the system has to choose a specific source IP address, since a socket cannot be connected and at the same time be bound to any local IP address. Depending on the destination address and the content of the routing table, the system will pick an appropriate source address and replace the “any” binding with a binding to the chosen source IP address.<br>如果显式绑定套接字,则可以将其绑定到 port 0 ,这意味着“任何端口”。由于套接字不能真正绑定到所有现有端口,因此在这种情况下,系统必须选择特定端口本身(通常来自预定义的、特定于操作系统的源端口范围)。源地址也存在类似的通配符,可以是“任何地址”( 0.0.0.0 在 IPv4 和 :: IPv6 的情况下)。与端口不同,套接字实际上可以绑定到“任何地址”,这意味着“所有本地接口的所有源 IP 地址”。如果稍后连接套接字,系统必须选择特定的源 IP 地址,因为套接字无法连接并同时绑定到任何本地 IP 地址。根据目标地址和路由表的内容,系统将选择适当的源地址,并将“any”绑定替换为与所选源 IP 地址的绑定。</p><p>By default, no two sockets can be bound to the same combination of source address and source port. As long as the source port is different, the source address is actually irrelevant. Binding socketA to ipA:portA and socketB to ipB:portB is always possible if ipA != ipB holds true, even when portA == portB. E.g. socketA belongs to a FTP server program and is bound to 192.168.0.1:21 and socketB belongs to another FTP server program and is bound to 10.0.0.1:21, both bindings will succeed. Keep in mind, though, that a socket may be locally bound to “any address”. If a socket is bound to 0.0.0.0:21, it is bound to all existing local addresses at the same time and in that case no other socket can be bound to port 21, regardless which specific IP address it tries to bind to, as 0.0.0.0 conflicts with all existing local IP addresses.<br>默认情况下,两个套接字不能绑定到源地址和源端口的相同组合。只要源端口不同,源地址其实是无关紧要的。如果成立 true,则始终可以绑定 socketA 到 ipA:portA 和 socketB to ipB:portB ,即使 portA == portB . ipA != ipB 例如, socketA 属于一个FTP服务器程序,并且绑定到 192.168.0.1:21 另一个FTP服务器程序并 socketB 属于另一个FTP服务器程序,并且绑定到 10.0.0.1:21 ,两个绑定都会成功。但请记住,套接字可能在本地绑定到“任何地址”。如果套接字绑定到 0.0.0.0:21 ,则它同时绑定到所有现有的本地地址,在这种情况下,其他套接字都不能绑定到 端口 21 ,无论它尝试绑定到哪个特定的 IP 地址,因为 0.0.0.0 与所有现有的本地 IP 地址冲突。</p><p>Anything said so far is pretty much equal for all major operating system. Things start to get OS specific when address reuse comes into play. We start with BSD, since as I said above, it is the mother of all socket implementations.<br>到目前为止,对于所有主要操作系统来说,所说的任何事情几乎都是平等的。当地址重用发挥作用时,事情开始变得特定于操作系统。我们从 BSD 开始,因为正如我上面所说,它是所有套接字实现之母。</p><h2 id="BSD"><a href="#BSD" class="headerlink" title="BSD"></a>BSD</h2><h3 id="SO-REUSEADDR"><a href="#SO-REUSEADDR" class="headerlink" title="SO_REUSEADDR"></a>SO_REUSEADDR</h3><p>If SO_REUSEADDR is enabled on a socket prior to binding it, the socket can be successfully bound unless there is a conflict with another socket bound to exactly the same combination of source address and port. Now you may wonder how is that any different than before? The keyword is “exactly”. SO_REUSEADDR mainly changes the way how wildcard addresses (“any IP address”) are treated when searching for conflicts.<br>如果 SO_REUSEADDR 在绑定套接字之前在套接字上启用了套接字,则可以成功绑定套接字,除非与绑定到完全相同的源地址和端口组合的另一个套接字发生冲突。现在你可能想知道这和以前有什么不同?关键词是“确切地”。 SO_REUSEADDR 主要更改了搜索冲突时处理通配符地址(“任何 IP 地址”)的方式。</p><p>Without SO_REUSEADDR, binding socketA to 0.0.0.0:21 and then binding socketB to 192.168.0.1:21 will fail (with error EADDRINUSE), since 0.0.0.0 means “any local IP address”, thus all local IP addresses are considered in use by this socket and this includes 192.168.0.1, too. With SO_REUSEADDR it will succeed, since 0.0.0.0 and 192.168.0.1 are not exactly the same address, one is a wildcard for all local addresses and the other one is a very specific local address. Note that the statement above is true regardless in which order socketA and socketB are bound; without SO_REUSEADDR it will always fail, with SO_REUSEADDR it will always succeed.<br>如果没有 SO_REUSEADDR ,绑定 socketA 到 0.0.0.0:21 然后绑定 socketB 到 192.168.0.1:21 将失败(有错误 EADDRINUSE ),因为 0.0.0.0 表示“任何本地 IP 地址”,因此所有本地 IP 地址都被视为由此套接字使用,这也包括 192.168.0.1 。有了 SO_REUSEADDR 它,它就会成功,因为 0.0.0.0 和 192.168.0.1 不是完全相同的地址,一个是所有本地地址的通配符,另一个是非常具体的本地地址。请注意,无论以何种顺序 socketA 和 socketB 绑定,上述陈述都是正确的;没有 SO_REUSEADDR 它,它将永远失败,它 SO_REUSEADDR 将永远成功。</p><p>To give you a better overview, let’s make a table here and list all possible combinations:<br>为了给您一个更好的概述,让我们在这里制作一个表格并列出所有可能的组合:</p><figure class="highlight shell"><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">SO_REUSEADDR socketA socketB Result</span><br><span class="line">---------------------------------------------------------------------</span><br><span class="line"> ON/OFF 192.168.0.1:21 192.168.0.1:21 Error (EADDRINUSE)</span><br><span class="line"> ON/OFF 192.168.0.1:21 10.0.0.1:21 OK</span><br><span class="line"> ON/OFF 10.0.0.1:21 192.168.0.1:21 OK</span><br><span class="line"> OFF 0.0.0.0:21 192.168.1.0:21 Error (EADDRINUSE)</span><br><span class="line"> OFF 192.168.1.0:21 0.0.0.0:21 Error (EADDRINUSE)</span><br><span class="line"> ON 0.0.0.0:21 192.168.1.0:21 OK</span><br><span class="line"> ON 192.168.1.0:21 0.0.0.0:21 OK</span><br><span class="line"> ON/OFF 0.0.0.0:21 0.0.0.0:21 Error (EADDRINUSE)</span><br></pre></td></tr></table></figure><p>The table above assumes that socketA has already been successfully bound to the address given for socketA, then socketB is created, either gets SO_REUSEADDR set or not, and finally is bound to the address given for socketB. Result is the result of the bind operation for socketB. If the first column says ON/OFF, the value of SO_REUSEADDR is irrelevant to the result.<br>上表假设 socketA 已经成功绑定到 的 socketA 地址,然后 socketB 创建,设置或不设置 SO_REUSEADDR ,最后绑定到 给定 socketB 的地址。 Result 是 的绑定操作的结果 socketB 。如果第一列说 ON/OFF ,则 的 SO_REUSEADDR 值与结果无关。</p><p>Okay, SO_REUSEADDR has an effect on wildcard addresses, good to know. Yet that isn’t its only effect it has. There is another well known effect which is also the reason why most people use SO_REUSEADDR in server programs in the first place. For the other important use of this option we have to take a deeper look on how the TCP protocol works.<br>好的, SO_REUSEADDR 对通配符地址有影响,很高兴知道。然而,这并不是它唯一的影响。还有另一个众所周知的效果,这也是大多数人首先在服务器程序中使用 SO_REUSEADDR 的原因。对于此选项的另一个重要用途,我们必须更深入地了解 TCP 协议的工作原理。</p><p>If a TCP socket is being closed, normally a 3-way handshake is performed; the sequence is called FIN-ACK. The problem here is, that the last ACK of that sequence may have arrived on the other side or it may not have arrived and only if it has, the other side also considers the socket as being fully closed. To prevent re-using an address+port combination, that may still be considered open by some remote peer, the system will not immediately consider a socket as dead after sending the last ACK but instead put the socket into a state commonly referred to as TIME_WAIT. It can be in that state for minutes (system dependent setting). On most systems you can get around that state by enabling lingering and setting a linger time of zero1 but there is no guarantee that this is always possible, that the system will always honor this request, and even if the system honors it, this causes the socket to be closed by a reset (RST), which is not always a great idea. To learn more about linger time, have a look at my answer about this topic.<br>如果 TCP 套接字正在关闭,通常会执行 3 次握手;该序列称为 FIN-ACK 。这里的问题是,该序列的最后一个 ACK 可能已经到达另一端,也可能没有到达,只有当它到达时,另一端也认为套接字是完全关闭的。为了防止重复使用地址+端口组合,该组合可能仍被某些远程对等方视为打开,系统不会在发送最后一个 ACK 套接字后立即将套接字视为死套接字,而是将套接字置于通常称为 TIME_WAIT 的状态。它可以处于该状态几分钟(取决于系统设置)。在大多数系统上,您可以通过启用延迟并将延迟时间设置为 zero1 来绕过该状态,但不能保证这始终是可能的,系统将始终遵守此请求,即使系统遵守它,这也会导致套接字通过重置 ( RST ) 关闭,这并不总是一个好主意。要了解有关逗留时间的更多信息,请查看我对此主题的回答。</p><p>The question is, how does the system treat a socket in state TIME_WAIT? If SO_REUSEADDR is not set, a socket in state TIME_WAIT is considered to still be bound to the source address and port and any attempt to bind a new socket to the same address and port will fail until the socket has really been closed. So don’t expect that you can rebind the source address of a socket immediately after closing it. In most cases this will fail. However, if SO_REUSEADDR is set for the socket you are trying to bind, another socket bound to the same address and port in state TIME_WAIT is simply ignored, after all its already “half dead”, and your socket can bind to exactly the same address without any problem. In that case it plays no role that the other socket may have exactly the same address and port. Note that binding a socket to exactly the same address and port as a dying socket in TIME_WAIT state can have unexpected, and usually undesired, side effects in case the other socket is still “at work”, but that is beyond the scope of this answer and fortunately those side effects are rather rare in practice.<br>问题是,系统如何处理处于状态 TIME_WAIT 的套接字?如果 SO_REUSEADDR 未设置,则认为处于状态 TIME_WAIT 的套接字仍绑定到源地址和端口,并且任何将新套接字绑定到同一地址和端口的尝试都将失败,直到套接字真正关闭。所以不要指望在关闭套接字后立即重新绑定套接字的源地址。在大多数情况下,这将失败。但是,如果 SO_REUSEADDR 为您尝试绑定的套接字设置了套接字,则绑定到相同地址和端口状态 TIME_WAIT 的另一个套接字将被忽略,毕竟它已经“半死不活”了,并且您的套接字可以绑定到完全相同的地址而不会出现任何问题。在这种情况下,另一个套接字可能具有完全相同的地址和端口,这不起作用。请注意,如果另一个套接字仍在“工作”,将一个套接字绑定到与 TIME_WAIT 处于状态的垂死套接字完全相同的地址和端口可能会产生意想不到的副作用,而且通常是不希望的副作用,但这超出了这个答案的范围,幸运的是,这些副作用在实践中相当罕见。</p><p>There is one final thing you should know about SO_REUSEADDR. Everything written above will work as long as the socket you want to bind to has address reuse enabled. It is not necessary that the other socket, the one which is already bound or is in a TIME_WAIT state, also had this flag set when it was bound. The code that decides if the bind will succeed or fail only inspects the SO_REUSEADDR flag of the socket fed into the bind() call, for all other sockets inspected, this flag is not even looked at.<br>还有最后一件事你应该知道 SO_REUSEADDR 。只要您要绑定到的套接字启用了地址重用,上面写的所有内容都会起作用。另一个套接字(已绑定或处于某种 TIME_WAIT 状态的套接字)在绑定时也不必设置此标志。决定绑定是成功还是失败的代码仅检查馈入调用的 bind() 套接字 SO_REUSEADDR 的标志,对于检查的所有其他套接字,甚至不查看此标志。</p><h3 id="SO-REUSEPORT"><a href="#SO-REUSEPORT" class="headerlink" title="SO_REUSEPORT"></a>SO_REUSEPORT</h3><p>SO_REUSEPORT is what most people would expect SO_REUSEADDR to be. Basically, SO_REUSEPORT allows you to bind an arbitrary number of sockets to exactly the same source address and port as long as all prior bound sockets also had SO_REUSEPORT set before they were bound. If the first socket that is bound to an address and port does not have SO_REUSEPORT set, no other socket can be bound to exactly the same address and port, regardless if this other socket has SO_REUSEPORT set or not, until the first socket releases its binding again. Unlike in case of SO_REUSEADDR the code handling SO_REUSEPORT will not only verify that the currently bound socket has SO_REUSEPORT set but it will also verify that the socket with a conflicting address and port had SO_REUSEPORT set when it was bound.<br>SO_REUSEPORT 是大多数人所期望 SO_REUSEADDR 的。基本上, SO_REUSEPORT 允许您将任意数量的套接字绑定到完全相同的源地址和端口,只要所有先前绑定的套接字在绑定之前也已 SO_REUSEPORT 设置。如果绑定到地址和端口的第一个套接字未 SO_REUSEPORT 设置,则在第一个套接字再次释放其绑定之前,不能将其他套接字绑定到完全相同的地址和端口,无论该其他套接字是否已 SO_REUSEPORT 设置。与代码不同的是 SO_REUSEADDR ,处理 SO_REUSEPORT 不仅会验证当前绑定的套接字是否已 SO_REUSEPORT 设置,还会验证具有冲突地址和端口的套接字在绑定时是否已 SO_REUSEPORT 设置。</p><p>SO_REUSEPORT does not imply SO_REUSEADDR. This means if a socket did not have SO_REUSEPORT set when it was bound and another socket has SO_REUSEPORT set when it is bound to exactly the same address and port, the bind fails, which is expected, but it also fails if the other socket is already dying and is in TIME_WAIT state. To be able to bind a socket to the same addresses and port as another socket in TIME_WAIT state requires either SO_REUSEADDR to be set on that socket or SO_REUSEPORT must have been set on both sockets prior to binding them. Of course it is allowed to set both, SO_REUSEPORT and SO_REUSEADDR, on a socket.<br>SO_REUSEPORT 并不意味着 SO_REUSEADDR .这意味着,如果一个套接字在绑定时没有 SO_REUSEPORT 设置,而另一个套接字在绑定到完全相同的地址和端口时设置 SO_REUSEPORT 了,则绑定会失败,这是意料之中的,但如果另一个套接字已经死亡并处于 TIME_WAIT 状态,则绑定也会失败。为了能够将套接字绑定到与 TIME_WAIT 处于状态的另一个套接字相同的地址和端口,需要 SO_REUSEADDR 在该套接字上设置,或者 SO_REUSEPORT 必须在绑定两个套接字之前在两个套接字上设置它们。当然, SO_REUSEPORT 允许在套接字上同时设置 和 SO_REUSEADDR 。</p><p>There is not much more to say about SO_REUSEPORT other than that it was added later than SO_REUSEADDR, that’s why you will not find it in many socket implementations of other systems, which “forked” the BSD code before this option was added, and that there was no way to bind two sockets to exactly the same socket address in BSD prior to this option.<br>除了添加它晚于 SO_REUSEADDR 之外,没有什么可说 SO_REUSEPORT 的,这就是为什么你不会在其他系统的许多套接字实现中找到它的原因,这些系统在添加此选项之前“分叉”了 BSD 代码,并且没有办法将两个套接字绑定到此选项之前 BSD 中完全相同的套接字地址。</p><h3 id="Connect-Returning-EADDRINUSE"><a href="#Connect-Returning-EADDRINUSE" class="headerlink" title="Connect() Returning EADDRINUSE?"></a>Connect() Returning EADDRINUSE?</h3><p>connect() 返回 EADDRINUSE?<br>Most people know that bind() may fail with the error EADDRINUSE, however, when you start playing around with address reuse, you may run into the strange situation that connect() fails with that error as well. How can this be? How can a remote address, after all that’s what connect adds to a socket, be already in use? Connecting multiple sockets to exactly the same remote address has never been a problem before, so what’s going wrong here?<br>大多数人都知道 bind() 可能会因错误 EADDRINUSE 而失败,但是,当您开始尝试地址重用时,您可能会遇到该错误 connect() 失败的奇怪情况。这怎么可能?远程地址,毕竟这是连接添加到套接字的内容,怎么可能已经在使用中?将多个套接字连接到完全相同的远程地址以前从未成为问题,那么这里出了什么问题呢?</p><p>As I said on the very top of my reply, a connection is defined by a tuple of five values, remember? And I also said, that these five values must be unique otherwise the system cannot distinguish two connections any longer, right? Well, with address reuse, you can bind two sockets of the same protocol to the same source address and port. That means three of those five values are already the same for these two sockets. If you now try to connect both of these sockets also to the same destination address and port, you would create two connected sockets, whose tuples are absolutely identical. This cannot work, at least not for TCP connections (UDP connections are no real connections anyway). If data arrived for either one of the two connections, the system could not tell which connection the data belongs to. At least the destination address or destination port must be different for either connection, so that the system has no problem to identify to which connection incoming data belongs to.<br>正如我在回复的最上面所说,连接是由五个值组成的元组定义的,还记得吗?我还说,这五个值必须是唯一的,否则系统就无法再区分两个连接了,对吧?好吧,通过地址重用,您可以将同一协议的两个套接字绑定到相同的源地址和端口。这意味着这两个套接字的这五个值中的三个已经相同。如果现在尝试将这两个套接字也连接到相同的目标地址和端口,则将创建两个连接的套接字,其元组完全相同。这行不通,至少对于TCP连接不起作用(UDP连接无论如何都不是真正的连接)。如果数据到达两个连接中的任何一个,系统无法判断数据属于哪个连接。对于任一连接,至少目标地址或目标端口必须不同,以便系统在识别传入数据属于哪个连接时没有问题。</p><p>So if you bind two sockets of the same protocol to the same source address and port and try to connect them both to the same destination address and port, connect() will actually fail with the error EADDRINUSE for the second socket you try to connect, which means that a socket with an identical tuple of five values is already connected.<br>因此,如果将相同协议的两个套接字绑定到相同的源地址和端口,并尝试将它们都连接到相同的目标地址和端口, connect() 则实际上会失败,并出现您尝试连接的第二个套接字的错误 EADDRINUSE ,这意味着具有相同元组的五个值的套接字已经连接。</p><h3 id="Multicast-Addresses-组播地址"><a href="#Multicast-Addresses-组播地址" class="headerlink" title="Multicast Addresses 组播地址"></a>Multicast Addresses 组播地址</h3><p>Most people ignore the fact that multicast addresses exist, but they do exist. While unicast addresses are used for one-to-one communication, multicast addresses are used for one-to-many communication. Most people got aware of multicast addresses when they learned about IPv6 but multicast addresses also existed in IPv4, even though this feature was never widely used on the public Internet.<br>大多数人忽略了多播地址存在的事实,但它们确实存在。单播地址用于一对一通信,而组播地址用于一对多通信。大多数人在了解 IPv6 时就知道组播地址,但组播地址也存在于 IPv4 中,尽管此功能从未在公共 Internet 上广泛使用。</p><p>The meaning of SO_REUSEADDR changes for multicast addresses as it allows multiple sockets to be bound to exactly the same combination of source multicast address and port. In other words, for multicast addresses SO_REUSEADDR behaves exactly as SO_REUSEPORT for unicast addresses. Actually, the code treats SO_REUSEADDR and SO_REUSEPORT identically for multicast addresses, that means you could say that SO_REUSEADDR implies SO_REUSEPORT for all multicast addresses and the other way round.<br>SO_REUSEADDR 更改组播地址的含义,因为它允许将多个套接字绑定到完全相同的源组播地址和端口组合。换言之,对于组播地址 SO_REUSEADDR 的行为与 SO_REUSEPORT 单播地址的行为完全相同。实际上,代码对组播地址的处理 SO_REUSEADDR 方式 SO_REUSEPORT 相同,这意味着您可以说这意味着 SO_REUSEADDR SO_REUSEPORT 所有组播地址,反之亦然。</p><h2 id="FreeBSD-OpenBSD-NetBSD"><a href="#FreeBSD-OpenBSD-NetBSD" class="headerlink" title="FreeBSD/OpenBSD/NetBSD"></a>FreeBSD/OpenBSD/NetBSD</h2><p>All these are rather late forks of the original BSD code, that’s why they all three offer the same options as BSD and they also behave the same way as in BSD.<br>所有这些都是原始 BSD 代码的后期分支,这就是为什么它们都提供与 BSD 相同的选项,并且它们的行为方式也与 BSD 相同。</p><h3 id="macOS-MacOS-X"><a href="#macOS-MacOS-X" class="headerlink" title="macOS (MacOS X)"></a>macOS (MacOS X)</h3><p>macOS (MacOS X)<br>At its core, macOS is simply a BSD-style UNIX named “Darwin”, based on a rather late fork of the BSD code (BSD 4.3), which was then later on even re-synchronized with the (at that time current) FreeBSD 5 code base for the Mac OS 10.3 release, so that Apple could gain full POSIX compliance (macOS is POSIX certified). Despite having a microkernel at its core (“Mach”), the rest of the kernel (“XNU”) is basically just a BSD kernel, and that’s why macOS offers the same options as BSD and they also behave the same way as in BSD.<br>从本质上讲,macOS 只是一个名为“Darwin”的 BSD 风格的 UNIX,它基于 BSD 代码(BSD 4.3)的一个相当晚的分支,后来甚至与 Mac OS 10.3 版本的 FreeBSD 5 代码库重新同步,以便 Apple 可以获得完全的 POSIX 合规性(macOS 已获得 POSIX 认证)。尽管其核心有一个微内核(“Mach”),但内核的其余部分(“XNU”)基本上只是一个 BSD 内核,这就是为什么 macOS 提供与 BSD 相同的选项,并且它们的行为方式也与 BSD 相同。</p><h3 id="iOS-watchOS-tvOS"><a href="#iOS-watchOS-tvOS" class="headerlink" title="iOS / watchOS / tvOS"></a>iOS / watchOS / tvOS</h3><p>iOS/watchOS / tvOS<br>iOS is just a macOS fork with a slightly modified and trimmed kernel, somewhat stripped down user space toolset and a slightly different default framework set. watchOS and tvOS are iOS forks, that are stripped down even further (especially watchOS). To my best knowledge they all behave exactly as macOS does.<br>iOS 只是一个 macOS 分支,具有略微修改和修剪的内核、略微精简的用户空间工具集和略有不同的默认框架集。watchOS 和 tvOS 是 iOS 的分支,它们被进一步精简(尤其是 watchOS)。据我所知,它们的行为都与 macOS 完全相同。</p><h2 id="Linux-Linux操作系统"><a href="#Linux-Linux操作系统" class="headerlink" title="Linux Linux操作系统"></a>Linux Linux操作系统</h2><h3 id="Linux-3-9"><a href="#Linux-3-9" class="headerlink" title="Linux < 3.9"></a>Linux < 3.9</h3><p>Prior to Linux 3.9, only the option SO_REUSEADDR existed. This option behaves generally the same as in BSD with two important exceptions:<br>在 Linux 3.9 之前,只有该选项 SO_REUSEADDR 存在。此选项的行为与 BSD 中的行为大致相同,但有两个重要的例外:</p><p>As long as a listening (server) TCP socket is bound to a specific port, the SO_REUSEADDR option is entirely ignored for all sockets targeting that port. Binding a second socket to the same port is only possible if it was also possible in BSD without having SO_REUSEADDR set. E.g. you cannot bind to a wildcard address and then to a more specific one or the other way round, both is possible in BSD if you set SO_REUSEADDR. What you can do is you can bind to the same port and two different non-wildcard addresses, as that’s always allowed. In this aspect Linux is more restrictive than BSD.<br>只要侦听(服务器)TCP 套接字绑定到特定端口,则对于面向该端口的所有套接字,该 SO_REUSEADDR 选项将被完全忽略。只有在 BSD 中也可以将第二个套接字绑定到同一端口而无需 SO_REUSEADDR 设置时才有可能。例如,你不能绑定到一个通配符地址,然后绑定到一个更具体的地址,或者相反,如果你设置 SO_REUSEADDR 了 .您可以做的是绑定到相同的端口和两个不同的非通配符地址,因为这始终是允许的。在这方面,Linux 比 BSD 更具限制性。</p><p>The second exception is that for client sockets, this option behaves exactly like SO_REUSEPORT in BSD, as long as both had this flag set before they were bound. The reason for allowing that was simply that it is important to be able to bind multiple sockets to exactly to the same UDP socket address for various protocols and as there used to be no SO_REUSEPORT prior to 3.9, the behavior of SO_REUSEADDR was altered accordingly to fill that gap. In that aspect Linux is less restrictive than BSD.<br>第二个例外是,对于客户端套接字,此选项的行为与 BSD 中的行为完全相同 SO_REUSEPORT ,只要两者在绑定之前都设置了此标志即可。允许这样做的原因很简单,能够将多个套接字绑定到各种协议的同一 UDP 套接字地址非常重要,并且由于 3.9 之前没有 SO_REUSEPORT ,因此相应地更改了 的行为 SO_REUSEADDR 以填补这一空白。在这方面,Linux 的限制比 BSD 少。</p><h3 id="Linux-3-9-1"><a href="#Linux-3-9-1" class="headerlink" title="Linux >= 3.9"></a>Linux >= 3.9</h3><p>Linux 3.9 added the option SO_REUSEPORT to Linux as well. This option behaves exactly like the option in BSD and allows binding to exactly the same address and port number as long as all sockets have this option set prior to binding them.<br>Linux 3.9 也为 Linux 添加了该选项 SO_REUSEPORT 。此选项的行为与 BSD 中的选项完全相同,并且允许绑定到完全相同的地址和端口号,只要所有套接字在绑定之前都设置了此选项。</p><p>Yet, there are still two differences to SO_REUSEPORT on other systems:<br>然而,与其他 SO_REUSEPORT 系统相比,仍然有两个区别:</p><p>To prevent “port hijacking”, there is one special limitation: All sockets that want to share the same address and port combination must belong to processes that share the same effective user ID! So one user cannot “steal” ports of another user. This is some special magic to somewhat compensate for the missing SO_EXCLBIND/SO_EXCLUSIVEADDRUSE flags.<br>为了防止“端口劫持”,有一个特殊的限制:所有想要共享相同地址和端口组合的套接字必须属于共享相同有效用户 ID 的进程!因此,一个用户不能“窃取”另一个用户的端口。这是一些特殊的魔法,可以在一定程度上弥补丢失 SO_EXCLBIND 的/ SO_EXCLUSIVEADDRUSE 标志。</p><p>Additionally the kernel performs some “special magic” for SO_REUSEPORT sockets that isn’t found in other operating systems: For UDP sockets, it tries to distribute datagrams evenly, for TCP listening sockets, it tries to distribute incoming connect requests (those accepted by calling accept()) evenly across all the sockets that share the same address and port combination. Thus an application can easily open the same port in multiple child processes and then use SO_REUSEPORT to get a very inexpensive load balancing.<br>此外,内核还对 SO_REUSEPORT 套接字执行了一些在其他操作系统中没有的“特殊魔术”:对于 UDP 套接字,它尝试均匀地分配数据报,对于 TCP 侦听套接字,它尝试在共享相同地址和端口组合的所有套接字之间均匀地分配传入的连接请求(通过调用 accept() 接受的请求)。因此,应用程序可以很容易地在多个子进程中打开同一个端口,然后用于 SO_REUSEPORT 获得非常便宜的负载平衡。</p><h3 id="Android"><a href="#Android" class="headerlink" title="Android"></a>Android</h3><p>Even though the whole Android system is somewhat different from most Linux distributions, at its core works a slightly modified Linux kernel, thus everything that applies to Linux should apply to Android as well.<br>尽管整个 Android 系统与大多数 Linux 发行版有些不同,但其核心工作是略微修改的 Linux 内核,因此适用于 Linux 的所有内容也应该适用于 Android。</p><h2 id="Windows"><a href="#Windows" class="headerlink" title="Windows"></a>Windows</h2><p>Windows only knows the SO_REUSEADDR option, there is no SO_REUSEPORT. Setting SO_REUSEADDR on a socket in Windows behaves like setting SO_REUSEPORT and SO_REUSEADDR on a socket in BSD, with one exception:<br>Windows只知道 SO_REUSEADDR 这个选项,没有 SO_REUSEPORT .在 Windows 中的套接字上进行设置 SO_REUSEADDR 的行为类似于 BSD 中的设置 SO_REUSEPORT 和 SO_REUSEADDR 套接字,但有一个例外:</p><p>Prior to Windows 2003, a socket with SO_REUSEADDR could always been bound to exactly the same source address and port as an already bound socket, even if the other socket did not have this option set when it was bound. This behavior allowed an application “to steal” the connected port of another application. Needless to say that this has major security implications!<br>在 Windows 2003 之前,一个 SO_REUSEADDR 套接字始终可以绑定到与已绑定套接字完全相同的源地址和端口,即使另一个套接字在绑定时没有设置此选项。此行为允许应用程序“窃取”另一个应用程序的连接端口。毋庸置疑,这具有重大的安全隐患!</p><p>Microsoft realized that and added another important socket option: SO_EXCLUSIVEADDRUSE. Setting SO_EXCLUSIVEADDRUSE on a socket makes sure that if the binding succeeds, the combination of source address and port is owned exclusively by this socket and no other socket can bind to them, not even if it has SO_REUSEADDR set.<br>Microsoft意识到了这一点,并添加了另一个重要的套接字选项: SO_EXCLUSIVEADDRUSE .在套接字上设置 SO_EXCLUSIVEADDRUSE 可确保如果绑定成功,源地址和端口的组合仅由此套接字拥有,并且没有其他套接字可以绑定到它们,即使它已经 SO_REUSEADDR 设置了。</p><p>This default behavior was changed first in Windows 2003, Microsoft calls that “Enhanced Socket Security” (funny name for a behavior that is default on all other major operating systems). For more details just visit this page. There are three tables: The first one shows the classic behavior (still in use when using compatibility modes!), the second one shows the behavior of Windows 2003 and up when the bind() calls are made by the same user, and the third one when the bind() calls are made by different users.<br>此默认行为首先在 Windows 2003 中更改,Microsoft 将其称为“增强套接字安全性”(在所有其他主要操作系统上默认的行为的有趣名称)。有关更多详细信息,请访问此页面。有三个表:第一个表显示经典行为(使用兼容模式时仍在使用!),第二个表显示 Windows 2003 及更高版本由同一用户进行 bind() 调用时的行为,第三个表显示不同用户进行 bind() 调用时的行为。</p><h2 id="Solaris"><a href="#Solaris" class="headerlink" title="Solaris"></a>Solaris</h2><p>Solaris is the successor of SunOS. SunOS was originally based on a fork of BSD, SunOS 5 and later was based on a fork of SVR4, however SVR4 is a merge of BSD, System V, and Xenix, so up to some degree Solaris is also a BSD fork, and a rather early one. As a result Solaris only knows SO_REUSEADDR, there is no SO_REUSEPORT. The SO_REUSEADDR behaves pretty much the same as it does in BSD. As far as I know there is no way to get the same behavior as SO_REUSEPORT in Solaris, that means it is not possible to bind two sockets to exactly the same address and port.<br>Solaris 是 SunOS 的继任者。SunOS 最初是基于 BSD 的一个分支,SunOS 5 和后来的 SVR4 是基于 SVR4 的一个分支,但 SVR4 是 BSD、System V 和 Xenix 的合并,所以在某种程度上 Solaris 也是一个 BSD 分支,而且是一个相当早期的分支。结果Solaris只知道 SO_REUSEADDR ,没有 SO_REUSEPORT 。其 SO_REUSEADDR 行为与在 BSD 中的行为几乎相同。据我所知,没有办法获得与Solaris相同的行为 SO_REUSEPORT ,这意味着不可能将两个套接字绑定到完全相同的地址和端口。</p><p>Similar to Windows, Solaris has an option to give a socket an exclusive binding. This option is named SO_EXCLBIND. If this option is set on a socket prior to binding it, setting SO_REUSEADDR on another socket has no effect if the two sockets are tested for an address conflict. E.g. if socketA is bound to a wildcard address and socketB has SO_REUSEADDR enabled and is bound to a non-wildcard address and the same port as socketA, this bind will normally succeed, unless socketA had SO_EXCLBIND enabled, in which case it will fail regardless the SO_REUSEADDR flag of socketB.<br>与 Windows 类似,Solaris 可以选择为套接字提供独占绑定。此选项名为 SO_EXCLBIND 。如果在绑定套接字之前在套接字上设置了此选项,则在测试两个套接字是否存在地址冲突时,在另一个套接字上设置 SO_REUSEADDR 该选项将不起作用。例如,如果 socketA 绑定到通配符地址并 socketB 已 SO_REUSEADDR 启用并绑定到非通配符地址和与 socketA 相同的端口,则此绑定通常会成功,除非已 SO_EXCLBIND 启用,在这种情况下 socketA ,无论 . SO_REUSEADDR socketB</p><h2 id="Other-Systems-其他系统"><a href="#Other-Systems-其他系统" class="headerlink" title="Other Systems 其他系统"></a>Other Systems 其他系统</h2><p>In case your system is not listed above, I wrote a little test program that you can use to find out how your system handles these two options. Also if you think my results are wrong, please first run that program before posting any comments and possibly making false claims.<br>如果你的系统没有在上面列出,我写了一个小测试程序,你可以用它来了解你的系统如何处理这两个选项。另外,如果您认为我的结果是错误的,请先运行该程序,然后再发表任何评论并可能做出虚假声明。</p><p>All that the code requires to build is a bit POSIX API (for the network parts) and a C99 compiler (actually most non-C99 compiler will work as well as long as they offer inttypes.h and stdbool.h; e.g. gcc supported both long before offering full C99 support).<br>构建代码所需的只是一个 POSIX API(用于网络部分)和一个 C99 编译器(实际上,大多数非 C99 编译器只要它们提供 inttypes.h 和 stdbool.h 就可以工作;例如, gcc 在提供完整的 C99 支持之前很久就支持两者)。</p><p>All that the program needs to run is that at least one interface in your system (other than the local interface) has an IP address assigned and that a default route is set which uses that interface. The program will gather that IP address and use it as the second “specific address”.<br>程序运行所需的只是系统中至少有一个接口(本地接口除外)分配了一个 IP 地址,并且设置了使用该接口的默认路由。该程序将收集该 IP 地址并将其用作第二个“特定地址”。</p><p>It tests all possible combinations you can think of:<br>它测试了您能想到的所有可能的组合:</p><p>TCP and UDP protocol<br>TCP 和 UDP 协议<br>Normal sockets, listen (server) sockets, multicast sockets<br>普通套接字、侦听(服务器)套接字、组播套接字<br>SO_REUSEADDR set on socket1, socket2, or both sockets<br>SO_REUSEADDR 在 socket1、socket2 或两个套接字上设置<br>SO_REUSEPORT set on socket1, socket2, or both sockets<br>SO_REUSEPORT 在 socket1、socket2 或两个套接字上设置<br>All address combinations you can make out of 0.0.0.0 (wildcard), 127.0.0.1 (specific address), and the second specific address found at your primary interface (for multicast it’s just 224.1.2.3 in all tests)<br>您可以从 0.0.0.0 (通配符)、 127.0.0.1 (特定地址)和在主接口找到的第二个特定地址组合(对于多播,它只是 224.1.2.3 在所有测试中)<br>and prints the results in a nice table. It will also work on systems that don’t know SO_REUSEPORT, in which case this option is simply not tested.<br>并将结果打印在一个漂亮的表格中。它也将在不知道 SO_REUSEPORT 的系统上运行,在这种情况下,此选项根本没有经过测试。</p><p>What the program cannot easily test is how SO_REUSEADDR acts on sockets in TIME_WAIT state as it’s very tricky to force and keep a socket in that state. Fortunately most operating systems seems to simply behave like BSD here and most of the time programmers can simply ignore the existence of that state.<br>程序无法轻松测试的是,套接字如何 SO_REUSEADDR 作用于处于 TIME_WAIT 该状态的套接字,因为强制并保持套接字处于该状态非常棘手。幸运的是,大多数操作系统在这里似乎只是简单地表现得像 BSD,大多数时候程序员可以简单地忽略该状态的存在。</p><p>Here’s the code (I cannot include it here, answers have a size limit and the code would push this reply over the limit).<br><a href="https://rextester.com/BUAFK86204">这是代码</a>(我不能在这里包含它,答案有大小限制,代码会把这个回复推到限制之外)。</p>]]></content>
<summary type="html"><p>注:(这篇文章原文是stack overflow的回答,觉得写的甚好,就转载保存下。<a</summary>
<category term="网络编程" scheme="https://lixiang365.github.io/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
<category term="网络" scheme="https://lixiang365.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>kbe脚本逻辑层带宽优化方向</title>
<link href="https://lixiang365.github.io/post/362d36fe.html"/>
<id>https://lixiang365.github.io/post/362d36fe.html</id>
<published>2024-01-20T12:00:00.000Z</published>
<updated>2024-01-20T12:00:00.000Z</updated>
<content type="html"><![CDATA[<h3 id="kbe脚本逻辑层带宽优化方向"><a href="#kbe脚本逻辑层带宽优化方向" class="headerlink" title="kbe脚本逻辑层带宽优化方向"></a>kbe脚本逻辑层带宽优化方向</h3><h4 id="分析消息带宽流量"><a href="#分析消息带宽流量" class="headerlink" title="分析消息带宽流量"></a>分析消息带宽流量</h4><p>可以从两个方向来分析监测带宽流量</p><h5 id="1-消息协议必需字节说明"><a href="#1-消息协议必需字节说明" class="headerlink" title="1. 消息协议必需字节说明"></a>1. 消息协议必需字节说明</h5><p>msgid : 消息id 2个字节 uint16<br>msglen : 消息长度 ,1、无参数的就没有消息长度,2、固定长度的消息也没有这个 3、可变长的消息长度默认是2个字节 uint16 消息过长,则会扩展 4个字节 uint32<br>msgbody: 消息内容字节 (消息内容部分消息也会有固定字节占用,例如属性更新的entityid\aliasid,属性utype等等。不详细展开)</p><h5 id="2-程序内部记录发送的字节数"><a href="#2-程序内部记录发送的字节数" class="headerlink" title="2. 程序内部记录发送的字节数"></a>2. 程序内部记录发送的字节数</h5><h6 id="1-服务器内部有一个网络流量记录对象"><a href="#1-服务器内部有一个网络流量记录对象" class="headerlink" title="1. 服务器内部有一个网络流量记录对象"></a>1. 服务器内部有一个网络流量记录对象</h6><p>打开guiconsole工具<br><img src="https://s2.loli.net/2024/03/06/3O7shSwYQFxNyVb.png" alt="gui-network-profile.png"></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="keyword">class</span> <span class="title class_">NetworkStats</span> : <span class="keyword">public</span> Singleton<NetworkStats></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="keyword">enum</span> <span class="title class_">S_OP</span>{</span><br><span class="line"> SEND,</span><br><span class="line"> RECV</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">Stats</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">Stats</span>()</span><br><span class="line"> {</span><br><span class="line"> name = <span class="string">""</span>;</span><br><span class="line"> send_count = <span class="number">0</span>;</span><br><span class="line"> send_size = <span class="number">0</span>;</span><br><span class="line"> recv_size = <span class="number">0</span>;</span><br><span class="line"> recv_count = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> std::string name;</span><br><span class="line"> uint32 send_size; <span class="comment">// 发送字节</span></span><br><span class="line"> uint32 send_count;<span class="comment">// 发送次数</span></span><br><span class="line"> uint32 recv_size;</span><br><span class="line"> uint32 recv_count;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">typedef</span> KBEUnordered_map<std::string, Stats> STATS;</span><br><span class="line"> <span class="built_in">NetworkStats</span>();</span><br><span class="line"> ~<span class="built_in">NetworkStats</span>();</span><br><span class="line"> <span class="comment">// 追踪消息接口</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">trackMessage</span><span class="params">(S_OP op, <span class="type">const</span> MessageHandler& msgHandler, uint32 size)</span></span>;</span><br><span class="line"> <span class="function">NetworkStats::STATS& <span class="title">stats</span><span class="params">()</span></span>{ <span class="keyword">return</span> stats_; }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">addHandler</span><span class="params">(NetworkStatsHandler* pHandler)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">removeHandler</span><span class="params">(NetworkStatsHandler* pHandler)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> STATS stats_;</span><br><span class="line"> std::vector<NetworkStatsHandler*> handlers_;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>// 服务器发送的所有消息发送次数和发送字节都会存在 MessageHandler上</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="keyword">class</span> <span class="title class_">MessageHandler</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">MessageHandler</span>();</span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">MessageHandler</span>();</span><br><span class="line"></span><br><span class="line"> std::string name;</span><br><span class="line"> MessageID msgID;</span><br><span class="line"> MessageArgs* pArgs;</span><br><span class="line"> int32 msgLen; <span class="comment">// 如果长度为-1则为非固定长度消息</span></span><br><span class="line"> <span class="type">bool</span> exposed;</span><br><span class="line"> MessageHandlers* pMessageHandlers;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// stats</span></span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">mutable</span> uint32 send_size;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">mutable</span> uint32 send_count;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">mutable</span> uint32 recv_size;</span><br><span class="line"> <span class="keyword">volatile</span> <span class="keyword">mutable</span> uint32 recv_count;</span><br><span class="line"> </span><br><span class="line"> <span class="function">uint32 <span class="title">sendsize</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> send_size; }</span><br><span class="line"> <span class="function">uint32 <span class="title">sendcount</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> send_count; }</span><br><span class="line"> <span class="function">uint32 <span class="title">sendavgsize</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> (send_count <= <span class="number">0</span>) ? <span class="number">0</span> : send_size / send_count; }</span><br><span class="line"></span><br><span class="line"> <span class="function">uint32 <span class="title">recvsize</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> recv_size; }</span><br><span class="line"> <span class="function">uint32 <span class="title">recvcount</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> recv_count; }</span><br><span class="line"> <span class="function">uint32 <span class="title">recvavgsize</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> (recv_count <= <span class="number">0</span>) ? <span class="number">0</span> : recv_size / recv_count; }</span><br><span class="line"> <span class="comment">// Ellipsis...</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>// 当使用 kbe console工具开启监控时,网络流量同时保存在了NetworkProfileHandler对象里</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">NetworkProfileHandler</span> : <span class="keyword">public</span> ProfileHandler, <span class="keyword">public</span> Network::NetworkStatsHandler</span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">NetworkProfileHandler</span>(Network::NetworkInterface & networkInterface, uint32 timinglen, </span><br><span class="line"> std::string name, <span class="type">const</span> Network::Address& addr);</span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">NetworkProfileHandler</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">timeout</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">sendStream</span><span class="params">(MemoryStream* s)</span></span>;</span><br><span class="line"> <span class="comment">// 关键函数</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">onSendMessage</span><span class="params">(<span class="type">const</span> Network::MessageHandler& msgHandler, <span class="type">int</span> size)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">onRecvMessage</span><span class="params">(<span class="type">const</span> Network::MessageHandler& msgHandler, <span class="type">int</span> size)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">ProfileVal</span></span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">ProfileVal</span>()</span><br><span class="line"> {</span><br><span class="line"> name = <span class="string">""</span>;</span><br><span class="line"> send_size = <span class="number">0</span>; <span class="comment">// 监控期间发送的字节</span></span><br><span class="line"> send_count = <span class="number">0</span>; <span class="comment">// 监控期间发送的次数</span></span><br><span class="line"> send_avgsize = <span class="number">0</span>; <span class="comment">// 服务器启动以来这个消息handler发送的平均字节,(不是监控期间)</span></span><br><span class="line"> total_send_size = <span class="number">0</span>;<span class="comment">// 服务器启动以来这个消息handler发送的字节</span></span><br><span class="line"> total_send_count = <span class="number">0</span>;<span class="comment">// 服务器启动以来这个消息handler发送次数</span></span><br><span class="line"></span><br><span class="line"> recv_size = <span class="number">0</span>;</span><br><span class="line"> recv_count = <span class="number">0</span>;</span><br><span class="line"> recv_avgsize = <span class="number">0</span>;</span><br><span class="line"> total_recv_size = <span class="number">0</span>;</span><br><span class="line"> total_recv_count = <span class="number">0</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"> std::string name;</span><br><span class="line"></span><br><span class="line"> uint32 send_size;</span><br><span class="line"> uint32 send_avgsize;</span><br><span class="line"> uint32 send_count;</span><br><span class="line"></span><br><span class="line"> uint32 total_send_size;</span><br><span class="line"> uint32 total_send_count;</span><br><span class="line"></span><br><span class="line"> uint32 recv_size;</span><br><span class="line"> uint32 recv_count;</span><br><span class="line"> uint32 recv_avgsize;</span><br><span class="line"></span><br><span class="line"> uint32 total_recv_size;</span><br><span class="line"> uint32 total_recv_count;</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">typedef</span> KBEUnordered_map<std::string, ProfileVal> PROFILEVALS;</span><br><span class="line"> PROFILEVALS profileVals_;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>优点:比较方便直观,直接统计所有消息流量。<br>弊端:因为有很多消息包有嵌套,例如一个消息包内可能嵌套好几个条的其他的消息,导致这个统计无法体现出更详细的每个消息的细节信息</p><h6 id="2-客户端插件新增流量监控"><a href="#2-客户端插件新增流量监控" class="headerlink" title="2. 客户端插件新增流量监控"></a>2. 客户端插件新增流量监控</h6><p>因为服务器端的弊端,同时也为了方便统计发送给一个客户端的详细消息,在客户端U3D插件加入网络消息追踪<code>NetworkMessagesProfile</code></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="comment">// 默认追踪10s</span></span><br><span class="line">NetworkMessagesProfile.getInstance().startRecvTrackRecvMessags();</span><br></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/03/06/CVzeGYWLyJ7OXxc.png" alt="u3d-network-profile-16878506741996.png"><br>![](### kbe脚本逻辑层带宽优化方向.assets/u3d-network-profile-16878506741996.png)</p><p>详细体现出一段时间流量细节</p><h5 id="3-使用监测工具"><a href="#3-使用监测工具" class="headerlink" title="3. 使用监测工具"></a>3. 使用监测工具</h5><p>例如Linux:iftop等,windows:AppNetworkCounter、NetworkTrafficView等</p><p>总结:通过内部或者外部方法工具详细的追踪到每个消息的每字节然后对应到协议的每个字段类型,就可以针对性的进行优化。同时根据同步方式也可以用来优化引擎。减少字节传输</p><h4 id="根据kbe实体同步原理来编写数据更少的实体的几个提示"><a href="#根据kbe实体同步原理来编写数据更少的实体的几个提示" class="headerlink" title="根据kbe实体同步原理来编写数据更少的实体的几个提示"></a>根据kbe实体同步原理来编写数据更少的实体的几个提示</h4><p>好的开始比后期优化更让人身心愉悦</p><h5 id="1-属性相关"><a href="#1-属性相关" class="headerlink" title="1. 属性相关"></a>1. 属性相关</h5><ol><li><p>广播形式<br> 如果客户端用不到,就不要设置同步客户端,因为每次实体初始化都必定要把客户端相关属性同步过去。同时还应该注意,是否需要传给自己。不需要的就用<code>OTHER_CLIENTS</code>,</p></li><li><p>属性更改<br> 属性的修改每次都会同步给客户端,不管值是不是有变化,只要修改就会发(相同的值赋值两次就会发两次),注意不要重复赋值</p></li><li><p>属性初始化<br> 尽量在def文件中写默认值(就算不写,也会有类型自己默认值),或者在创建实体时,传递字典属性参数。这时实体在创建时同步到客户端就会把这些值更新过去。如果再次在python脚本中初始化属性,就会把这个属性同步两次,所以尽量不要在python文件中初始化属性。</p></li></ol><h5 id="2-方法相关"><a href="#2-方法相关" class="headerlink" title="2. 方法相关"></a>2. 方法相关</h5><ol><li>远程方法标志<br> 使用远程调用时,需要关注是用allClients,还是OhterClients,Client。</li></ol><h5 id="实体同步原理"><a href="#实体同步原理" class="headerlink" title="实体同步原理"></a>实体同步原理</h5><p>稍后补充 TODO()!!</p>]]></content>
<summary type="html"><h3 id="kbe脚本逻辑层带宽优化方向"><a href="#kbe脚本逻辑层带宽优化方向" class="headerlink" title="kbe脚本逻辑层带宽优化方向"></a>kbe脚本逻辑层带宽优化方向</h3><h4 id="分析消息带宽流量"><a</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="网络编程" scheme="https://lixiang365.github.io/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
<category term="KBE引擎" scheme="https://lixiang365.github.io/categories/KBE%E5%BC%95%E6%93%8E/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
<category term="网络" scheme="https://lixiang365.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>Python读取Excel配置表</title>
<link href="https://lixiang365.github.io/post/4faaacff.html"/>
<id>https://lixiang365.github.io/post/4faaacff.html</id>
<published>2024-01-20T12:00:00.000Z</published>
<updated>2024-01-20T12:00:00.000Z</updated>
<content type="html"><![CDATA[<p>python 读取 Excel配置</p><p>读取Excel配置表 需要使用第三方库 <code>openpyxl</code></p><figure class="highlight python"><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><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">XlsxItem</span>:</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">xlsx中sheet的一项,不需要被继承</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">XlsxSheet</span>:</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">xlsx中sheet,新定义的配置类继承至这个类</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"><span class="comment"># XlsxItem,格式:{ id值1: XlsxItem对象1, id值2: XlsxItem对象2 }</span></span><br><span class="line">self.items = {}</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getItem</span>(<span class="params">self, <span class="built_in">id</span></span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">使用id获取XlsxItem,不会改变配置值</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">copyItem = copy.deepcopy(self.items[<span class="built_in">id</span>])</span><br><span class="line"><span class="keyword">return</span> copyItem</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getAllItem</span>(<span class="params">self</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">获取所有XlsxItem,不会改变配置值</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">return</span> copy.deepcopy(self.items)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">XlsxSheetManage</span>:</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">管理单个xlsx文件的所有sheet</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, dirpath, filename</span>):</span><br><span class="line">self.filename = filename</span><br><span class="line">self.filepath = dirpath + filename</span><br><span class="line"><span class="comment"># sheet元素,格式:{ "sheet1":[], "sheet2":[{...}, {...}] }</span></span><br><span class="line">self.sheetElements = {}</span><br><span class="line"><span class="comment"># 标题 xlsx行列号</span></span><br><span class="line">self.titleCoordinate = <span class="string">'A1'</span></span><br><span class="line"><span class="comment"># 实际内容的第一列(列号)</span></span><br><span class="line">self.firstColumn = <span class="number">2</span></span><br><span class="line"><span class="comment"># 第一行python对象(行号)</span></span><br><span class="line">self.firstPyobjRow = <span class="number">3</span></span><br><span class="line"><span class="comment"># 第一行python类型(行号)</span></span><br><span class="line">self.firstPytypeRow = <span class="number">4</span></span><br><span class="line"><span class="comment"># 第一行配置值(行号)</span></span><br><span class="line">self.firstItemRow = <span class="number">6</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">reload</span>(<span class="params">self</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">接口:调用此函数刷新配置项(如:当xlsx文件改变时)</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line">book = pyxl.load_workbook(self.filepath)</span><br><span class="line"><span class="keyword">for</span> sheetName <span class="keyword">in</span> book.sheetnames:</span><br><span class="line">sheet = book[sheetName]</span><br><span class="line">title = sheet[self.titleCoordinate]</span><br><span class="line"><span class="keyword">if</span> title <span class="keyword">is</span> <span class="literal">None</span>: <span class="comment"># 没有标题则表示object对象使用,不进行加载</span></span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line">self.sheetElements[sheetName] = self.getLoadSheetElements(book, sheetName)</span><br><span class="line">DEBUG_MSG(<span class="string">"sheetName:{} | value:{}"</span>.<span class="built_in">format</span>(sheetName,self.sheetElements[sheetName]))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getLoadSheetElements</span>(<span class="params">self, book, sheetName</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">从单个文件的指定sheet中获取内容</span></span><br><span class="line"><span class="string">book: excel文件对象</span></span><br><span class="line"><span class="string">sheetName: 工作表名字</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="comment"># 获取工作表对象</span></span><br><span class="line">sheet = book[sheetName]</span><br><span class="line">column = self.firstColumn</span><br><span class="line">itemRow = self.firstItemRow</span><br><span class="line">resultSheetElements = []</span><br><span class="line">hasId = <span class="literal">False</span> <span class="comment"># 必须有id这一列</span></span><br><span class="line"><span class="comment"># 遍历sheet表格</span></span><br><span class="line"><span class="keyword">for</span> row <span class="keyword">in</span> sheet.iter_rows(min_row=itemRow,min_col=self.firstColumn):</span><br><span class="line"><span class="comment"># 内容行的dict对象{ key: 第三行的所表示的字段名,value: 某行对应这个字段的值}</span></span><br><span class="line">contentRowObj = {}</span><br><span class="line"><span class="keyword">for</span> colIndex,cell <span class="keyword">in</span> <span class="built_in">enumerate</span>(row):</span><br><span class="line"><span class="keyword">if</span> colIndex == <span class="number">0</span> <span class="keyword">and</span> cell.value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"sheetName:{} cell:{} "</span>.<span class="built_in">format</span>(sheetName,cell.value))</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="comment"># 获取这个单元格对应的python对象名字和类型</span></span><br><span class="line">pyObjName = sheet.cell(row=self.firstPyobjRow, column=self.firstColumn+colIndex)</span><br><span class="line">pyType = sheet.cell(row=self.firstPytypeRow, column=self.firstColumn+colIndex)</span><br><span class="line"><span class="keyword">if</span> pyType.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> <span class="built_in">str</span>(pyType.value).strip() != <span class="string">""</span>:</span><br><span class="line"><span class="keyword">if</span> pyObjName.value <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> <span class="built_in">str</span>(pyObjName.value).strip() == <span class="string">""</span>: <span class="comment"># 不存在python对象名则直接遍历下一列</span></span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">if</span> pyType.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> <span class="built_in">str</span>(pyType.value).strip() != <span class="string">""</span>:</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">str</span>(pyObjName.value) == <span class="string">"id"</span>:</span><br><span class="line">hasId = <span class="literal">True</span></span><br><span class="line">contentRowObj[pyObjName.value] = self.getTypeValue(book, pyType, pyObjName, cell)</span><br><span class="line"><span class="keyword">if</span> contentRowObj[pyObjName.value] <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">ERROR_MSG(<span class="string">"XlsxSheetManage: (%s-%s) unknown type: \"%s\""</span> % (self.filename, sheetName, <span class="built_in">str</span>(pyType.value)))</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">break</span> <span class="comment"># 遍历到空type</span></span><br><span class="line">resultSheetElements.append(contentRowObj)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> hasId: <span class="comment"># 没有id则重置sheetElements</span></span><br><span class="line"><span class="comment"># KBEDebug.ERROR_MSG("XlsxSheetManage: (%s-%s) has not id column" % (self.filename, sheetName))</span></span><br><span class="line">resultSheetElements = []</span><br><span class="line"><span class="keyword">return</span> resultSheetElements</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getSheetElement</span>(<span class="params">self, sheetName</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">接口:获取指定sheet名称的xlsx配置,一个数组对象</span></span><br><span class="line"><span class="string">sheetName:sheet名称</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">return</span> self.sheetElements[sheetName]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getSheetObject</span>(<span class="params">self, sheetName, className</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">接口:指定sheet名称+类名,获取Sheet对象</span></span><br><span class="line"><span class="string">sheetName:sheet名称</span></span><br><span class="line"><span class="string">className:类名称,XlsxSheet的子类</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line">xlsxSheet = className()</span><br><span class="line">sheetElement = self.getSheetElement(sheetName)</span><br><span class="line"><span class="keyword">for</span> sheetElementObject <span class="keyword">in</span> sheetElement:</span><br><span class="line">xlsxItem = XlsxItem()</span><br><span class="line">self.getObjectAndSetAttr(xlsxItem, sheetElementObject)</span><br><span class="line">xlsxSheet.items[sheetElementObject[<span class="string">'id'</span>]] = xlsxItem</span><br><span class="line"><span class="keyword">return</span> xlsxSheet</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">findItemById</span>(<span class="params">self, book, sheetName, xlsxItemId</span>):</span><br><span class="line">sheet = book[sheetName]</span><br><span class="line">column = self.firstColumn</span><br><span class="line">itemRow = self.firstItemRow</span><br><span class="line">obj = {}</span><br><span class="line">hasId = <span class="literal">False</span></span><br><span class="line">splitChar = <span class="string">'_'</span> <span class="comment"># 数组分隔字符</span></span><br><span class="line"><span class="comment"># 遍历sheet表格</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">item = sheet.cell(row=itemRow, column=column)</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="literal">None</span>: <span class="comment"># 第一列数据没有值则跳出循环</span></span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">pyobj = sheet.cell(row=self.firstPyobjRow, column=column)</span><br><span class="line">pytype = sheet.cell(row=self.firstPytypeRow, column=column)</span><br><span class="line">column += <span class="number">1</span></span><br><span class="line"><span class="keyword">if</span> pytype.value <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> <span class="built_in">str</span>(pytype.value).strip() == <span class="string">""</span>:</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"><span class="keyword">if</span> pyobj.value <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> <span class="built_in">str</span>(pyobj.value).strip() == <span class="string">""</span>: <span class="comment"># 不存在python对象名则直接遍历下一列</span></span><br><span class="line">item = sheet.cell(row=itemRow, column=column)</span><br><span class="line"><span class="keyword">continue</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">if</span> pytype.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> <span class="built_in">str</span>(pytype.value).strip() != <span class="string">""</span>:</span><br><span class="line">obj[pyobj.value] = self.getTypeValue(book, pytype, pyobj, item)</span><br><span class="line"><span class="keyword">if</span> obj[pyobj.value] <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">ERROR_MSG(<span class="string">"XlsxSheetManage: (%s-%s) unknown type: \"%s\""</span> % (</span><br><span class="line">self.filename, sheetName, <span class="built_in">str</span>(pytype.value)))</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">str</span>(pyobj.value) == <span class="string">"id"</span>:</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'int'</span> <span class="keyword">and</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> <span class="built_in">int</span>(item.value) == xlsxItemId:</span><br><span class="line">hasId = <span class="literal">True</span></span><br><span class="line">item = sheet.cell(row=itemRow, column=column)</span><br><span class="line"><span class="keyword">if</span> hasId:</span><br><span class="line"><span class="keyword">return</span> obj</span><br><span class="line">column = self.firstColumn <span class="comment"># 第一列</span></span><br><span class="line">itemRow += <span class="number">1</span> <span class="comment"># 下一行</span></span><br><span class="line">obj = {}</span><br><span class="line"><span class="keyword">return</span> {}</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getTypeValue</span>(<span class="params">self, book, pytype, pyobj, item</span>):</span><br><span class="line">splitChar = <span class="string">'_'</span> <span class="comment"># 数组分隔字符</span></span><br><span class="line"><span class="comment"># type定义 ###########################################</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'int'</span>:</span><br><span class="line">cursetval = item.value</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">cursetval = <span class="number">0</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">int</span>(cursetval)</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'str'</span>:</span><br><span class="line">cursetval = item.value</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">cursetval = <span class="string">""</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">str</span>(cursetval)</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'float'</span>:</span><br><span class="line">cursetval = item.value</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">cursetval = <span class="number">0.0</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">float</span>(cursetval)</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'bool'</span>:</span><br><span class="line">cursetval = <span class="built_in">str</span>(item.value)</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">cursetval = <span class="string">"false"</span></span><br><span class="line"><span class="keyword">if</span> cursetval.lower() == <span class="string">"true"</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'arr(int)'</span>:</span><br><span class="line">cursetval = []</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">arr = <span class="built_in">str</span>(item.value).split(splitChar)</span><br><span class="line"><span class="keyword">for</span> arrobj <span class="keyword">in</span> arr:</span><br><span class="line">cursetval.append(<span class="built_in">int</span>(arrobj))</span><br><span class="line"><span class="keyword">return</span> cursetval</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'arr(str)'</span>:</span><br><span class="line">cursetval = []</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">arr = <span class="built_in">str</span>(item.value).split(splitChar)</span><br><span class="line"><span class="keyword">for</span> arrobj <span class="keyword">in</span> arr:</span><br><span class="line">cursetval.append(<span class="built_in">str</span>(arrobj))</span><br><span class="line"><span class="keyword">return</span> cursetval</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'arr(float)'</span>:</span><br><span class="line">cursetval = []</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">arr = <span class="built_in">str</span>(item.value).split(splitChar)</span><br><span class="line"><span class="keyword">for</span> arrobj <span class="keyword">in</span> arr:</span><br><span class="line">cursetval.append(<span class="built_in">float</span>(arrobj))</span><br><span class="line"><span class="keyword">return</span> cursetval</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'arr(bool)'</span>:</span><br><span class="line">cursetval = []</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">arr = <span class="built_in">str</span>(item.value).split(splitChar)</span><br><span class="line"><span class="keyword">for</span> arrobj <span class="keyword">in</span> arr:</span><br><span class="line"><span class="keyword">if</span> arrobj.lower() == <span class="string">"true"</span>:</span><br><span class="line">cursetval.append(<span class="literal">True</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">cursetval.append(<span class="literal">False</span>)</span><br><span class="line"><span class="keyword">return</span> cursetval</span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">str</span>(pytype.value) == <span class="string">'object'</span>:</span><br><span class="line"><span class="keyword">if</span> item.value <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line"><span class="keyword">return</span> self.findItemById(book, pyobj.value, <span class="built_in">int</span>(item.value))</span><br><span class="line"><span class="keyword">return</span> {}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getObjectAndSetAttr</span>(<span class="params">self, obj, element</span>):</span><br><span class="line"><span class="keyword">for</span> key <span class="keyword">in</span> element: <span class="comment"># 将格式:{ "XXX":12, "YYY":"str", "ZZZ":{"key1":12, "key2":true} } 转化为对象</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">str</span>(key) != <span class="string">""</span>:</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">isinstance</span>(element[key], <span class="built_in">dict</span>):</span><br><span class="line"><span class="built_in">setattr</span>(obj, key, XlsxItem())</span><br><span class="line">self.getObjectAndSetAttr(<span class="built_in">getattr</span>(obj, key), element[key])</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="built_in">setattr</span>(obj, key, element[key])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">XlsxFileManage</span>:</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">管理指定目录下所有的xlsx文件</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, dirpath</span>):</span><br><span class="line"><span class="comment"># 所有XlsxSheetManage对象</span></span><br><span class="line">self.xlsxSheetManage = []</span><br><span class="line"><span class="comment"># xlsx所在文件夹,dirpath:xlsx所在文件夹路径,读取后缀为".xlsx"</span></span><br><span class="line">self.dirpath = dirpath + <span class="string">"/"</span></span><br><span class="line">DEBUG_MSG(<span class="string">"XlsxFileManage: xlsx use directory: %s"</span> % self.dirpath)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">reload</span>(<span class="params">self</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">接口:调用此函数刷新文件夹下所有配置(如:当某些xlsx文件改变时)</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line">xlsxFiles = self.getXlsxFile(self.dirpath)</span><br><span class="line"><span class="keyword">for</span> xlsxFile <span class="keyword">in</span> xlsxFiles:</span><br><span class="line">sheetManage = XlsxSheetManage(self.dirpath, xlsxFile)</span><br><span class="line">sheetManage.reload()</span><br><span class="line">DEBUG_MSG(<span class="string">"XlsxFileManage: load %s success"</span> % xlsxFile)</span><br><span class="line">self.xlsxSheetManage.append(sheetManage)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getXlsxSheetManage</span>(<span class="params">self, fileName</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">接口:获取XlsxSheetManage对象,失败返回None</span></span><br><span class="line"><span class="string">fileName:xlsx文件名(不包含目录)</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">for</span> sheetManage <span class="keyword">in</span> self.xlsxSheetManage:</span><br><span class="line"><span class="keyword">if</span> sheetManage.filename <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> sheetManage.filename == fileName:</span><br><span class="line"><span class="keyword">return</span> sheetManage</span><br><span class="line"><span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getXlsxFile</span>(<span class="params">self, dirpath</span>):</span><br><span class="line">files = os.listdir(dirpath)</span><br><span class="line">xlsxFiles = []</span><br><span class="line"><span class="keyword">for</span> file <span class="keyword">in</span> files:</span><br><span class="line"><span class="keyword">if</span> os.path.isfile(dirpath + file):</span><br><span class="line"><span class="keyword">if</span> file.endswith(<span class="string">'.xlsx'</span>) <span class="keyword">and</span> <span class="keyword">not</span> file.startswith(<span class="string">'.'</span>) <span class="keyword">and</span> <span class="keyword">not</span> file.startswith(<span class="string">'~'</span>):</span><br><span class="line">xlsxFiles.append(file)</span><br><span class="line"><span class="keyword">return</span> xlsxFiles</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="comment"># from SkillConfig import *</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">obj = XlsxFileManage(<span class="string">"../../res/config/xls"</span>)</span><br><span class="line">obj.reload()</span><br><span class="line">xlsxSheetManage = obj.getXlsxSheetManage(<span class="string">'buff.xlsx'</span>)</span><br><span class="line"><span class="keyword">if</span> xlsxSheetManage <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">xlsxSheet = xlsxSheetManage.getSheetObject(<span class="string">"common"</span>, XlsxSheet)</span><br><span class="line">sss = xlsxSheet.getItem(<span class="number">1001</span>)</span><br><span class="line"><span class="built_in">print</span>(sss)</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>python 读取 Excel配置</p>
<p>读取Excel配置表 需要使用第三方库 <code>openpyxl</code></p>
<figure class="highlight python"><table><tr><td</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
</entry>
<entry>
<title>一次socket发送缓冲区溢出bug排查</title>
<link href="https://lixiang365.github.io/post/f2e7e827.html"/>
<id>https://lixiang365.github.io/post/f2e7e827.html</id>
<published>2024-01-20T12:00:00.000Z</published>
<updated>2024-01-20T12:00:00.000Z</updated>
<content type="html"><![CDATA[<!-- # 一次socket发送缓冲区溢出bug排查 --><h3 id="bug表现"><a href="#bug表现" class="headerlink" title="bug表现"></a>bug表现</h3><p>果冻大战中,突然客户端感觉很卡,然后大概2~3s后会恢复。偶现,近期才出现的(近期变动,修改了道具刷新策略)</p><h3 id="排查过程"><a href="#排查过程" class="headerlink" title="排查过程"></a>排查过程</h3><p>1、首先查看了日志,发现确实在相关的时间点出现了,baseapp到客户端的socket发送缓冲区溢出,此时猜测有两种可能,一个是确实把发送缓冲区填满了。二是客户端网络延迟(这个是有可能出现的情况,并且也是偶尔出现的,符合特性),排查日志过程中,发现同时有cellapp到logger的缓冲区溢出,时间点比较接近。<br>2、查看了Ubuntu系统默认的socket缓冲区大小,约200KB左右,另外因为bug是偶发的,并且存在时间非常短。如果出现了,很难在两三秒内查看系统状态,极难排查。<br>3、因为有cellapp到logger的缓冲区溢出,这个时间点太巧了,如此接近,不得不令人心生怀疑。所以开始转变思路。先排查为什么logger缓冲区溢出(是发送还是接收的问题)<br>4、因为最开始日志几十万行,无法一行一行看,都是搜索关键字查看的,所以错过了很多日志。这次我直接使用时间搜索,突然发现,在1s之内生成了1万条日志,问题明朗<br>5、通过关键点打印,发现是道具逻辑的bug,魔鬼果冻的功能是把人减到1滴血,铁布衫的功能是免疫伤害,但是铁布衫实现是给人加了一个9999的护甲,然后当一个人捡了魔鬼果冻,就会循环去减少这个人的护甲直到只剩1滴血,所以就出现了,短时间内大量循环(并不是死循环)。还好有logger缓冲区溢出,否则这个问题极难排查。</p>]]></content>
<summary type="html"><!-- # 一次socket发送缓冲区溢出bug排查 -->
<h3 id="bug表现"><a href="#bug表现" class="headerlink"</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="网络编程" scheme="https://lixiang365.github.io/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
<category term="KBE引擎" scheme="https://lixiang365.github.io/categories/KBE%E5%BC%95%E6%93%8E/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
<category term="网络" scheme="https://lixiang365.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
<category term="问题排查" scheme="https://lixiang365.github.io/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/"/>
</entry>
<entry>
<title>有限空间内毫米精度定位数据压缩算法</title>
<link href="https://lixiang365.github.io/post/6e31043d.html"/>
<id>https://lixiang365.github.io/post/6e31043d.html</id>
<published>2024-01-20T12:00:00.000Z</published>
<updated>2024-01-20T12:00:00.000Z</updated>
<content type="html"><![CDATA[<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>在空间内的高精度定位使用float传输必然占用大量带宽。对于position,我们进行空间精度分割。首先按大的精度进行分割,然后在粗略的精度块内进行细致的精度,分割空间为每个高精度块,用每个块来标识最后的position在哪里。direction一样的,对角度进行不同精度的分割。</p><h4 id="position"><a href="#position" class="headerlink" title="position"></a>position</h4><p>假设需要<code>1mm</code>精度<br>对空间进行以<code>12mm</code>为单位长度的三个坐标轴划分,使用3个字节进行存储x、y、z坐标(为什么使用12mm单位,因为我们要在一个小的空间内用一个字节标识所有高精度的空间块。因为最终我们的点取在空间块的中心,所以我们单元,所以单元长度是<code>2*精度</code>。高精度空间块数量必须<=256。取每个边6个比较好 12mm = 6*2mm)。</p><p>对每个<code>12mm</code>边长的正方形空间内,以<code>2mm</code>为单位长度划分空间块,一共216个空间块,使用1个字节存储块标识。<br>特殊情况:点如果在坐标原点的(precision * 6)范围内,就会丢失正负信息,所以使用-128这个特殊值,来标记这种特殊情况.</p><p>空间大小限制: 1mm <em>2</em> 6 <em>256=边长3.072m的正方形。<br>同理 精度越低,空间大小就可以越大<br>例如精度5mm</em> 2 <em>6</em> 256 = 15.36m的边长</p><p>见下方示例图<br><img src="https://s2.loli.net/2024/03/06/TXJKLp9rPINGOeW.png" alt="有限空间毫米精度位置数据压缩.png"></p><h4 id="direction"><a href="#direction" class="headerlink" title="direction"></a>direction</h4><p>对xyz三个轴旋转的角度,360°进行256划分,然后每个轴使用1个字节存储<br>物体半径越小,角度划分大一些,看起来并不明显<br>物体半径越大,角度划分要小。效果更好</p><h4 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h4><p>在比限制大小的空间内高精度定位,例如VR手部位置同步。使用这种position 4个字节,direction 3个字节,共<strong>7</strong>个字节。相比使用float的position和direction共<strong>24</strong>个字节。优化2/3。</p><h4 id="python代码实现"><a href="#python代码实现" class="headerlink" title="python代码实现"></a>python代码实现</h4><p>未优化仅作参考</p><figure class="highlight python"><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><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># 优化位置和方向字节</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> copy</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Vector3</span>:</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,x=<span class="number">0.0</span>,y=<span class="number">0.0</span>,z=<span class="number">0.0</span></span>):</span><br><span class="line"> self.x = x</span><br><span class="line"> self.y = y</span><br><span class="line"> self.z = z</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">str</span>(<span class="string">"Vector3({},{},{})"</span>.<span class="built_in">format</span>(self.x,self.y,self.z))</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OptimizePosition</span>:</span><br><span class="line"> </span><br><span class="line"> isSpaceIndexsInit = <span class="literal">False</span></span><br><span class="line"> spaceIndexs = [[[<span class="number">0</span> <span class="keyword">for</span> z <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>)] <span class="keyword">for</span> y <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>)] <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>)]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.x = <span class="number">0</span></span><br><span class="line"> self.y = <span class="number">0</span></span><br><span class="line"> self.z = <span class="number">0</span></span><br><span class="line"> self.spaceIndex = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> OptimizePosition.isSpaceIndexsInit:</span><br><span class="line"> self.spaceIndexsInit()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"OptimizePosition (x:{} y:{} z:{} i:{})"</span>.<span class="built_in">format</span>(self.x,self.y,self.z,self.spaceIndex)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">spaceIndexsInit</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> OptimizePosition.isSpaceIndexsInit:</span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line"> <span class="keyword">for</span> y <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line"> <span class="keyword">for</span> z <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">6</span>):</span><br><span class="line"> OptimizePosition.spaceIndexs[x][y][z] = count</span><br><span class="line"> count += <span class="number">1</span></span><br><span class="line"> <span class="comment"># # DEBUG_MSG("OptimizePosition::spaceIndexsInit - OptimizePosition.spaceIndes[{}][{}][{}]:{}".format(x,y,z,OptimizePosition.spaceIndexs[x][y][z]))</span></span><br><span class="line"> OptimizePosition.isSpaceIndexsInit = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">OptimizePositionFromVector3</span>(<span class="params">self, pos,precision = <span class="number">2</span></span>):</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> position 位置</span></span><br><span class="line"><span class="string"> precision 精度 毫米mm</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> OptimizePosition.isSpaceIndexsInit:</span><br><span class="line"> self.spaceIndexsInit()</span><br><span class="line"> position = copy.copy(pos)</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - origin pos: x:{} | y:{} | z:{}".format(pos.x,pos.y,pos.z))</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 针对精度的最大最小边界</span></span><br><span class="line"> sideMax = precision * <span class="number">6</span> * <span class="number">127</span></span><br><span class="line"> sideMin = precision * <span class="number">6</span> * -<span class="number">127</span> <span class="comment"># 这里留出-128,后边有用</span></span><br><span class="line"> <span class="comment"># 转换为mm单位</span></span><br><span class="line"> position.x *= <span class="number">1000</span></span><br><span class="line"> position.y *= <span class="number">1000</span></span><br><span class="line"> position.z *= <span class="number">1000</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> position.x >= sideMax:</span><br><span class="line"> position.x = sideMax - <span class="number">1</span></span><br><span class="line"> <span class="keyword">elif</span> position.x <= sideMin:</span><br><span class="line"> position.x = sideMin + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.y >= sideMax:</span><br><span class="line"> position.y = sideMax - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">elif</span> position.y <= sideMin:</span><br><span class="line"> position.y = sideMin + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.z >= sideMax:</span><br><span class="line"> position.z = sideMax - <span class="number">1</span></span><br><span class="line"> <span class="keyword">elif</span> position.z <= sideMin:</span><br><span class="line"> position.z = sideMin + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将坐标按照6*precision 取整。为新的坐标</span></span><br><span class="line"> self.x = <span class="built_in">int</span>(position.x / (precision * <span class="number">6</span>))</span><br><span class="line"> self.y = <span class="built_in">int</span>(position.y / (precision * <span class="number">6</span>))</span><br><span class="line"> self.z = <span class="built_in">int</span>(position.z / (precision * <span class="number">6</span>))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 特殊情况:点如果在坐标原点的(precision * 6)范围内,就会丢失正负信息,所以使用-128这个特殊值,来标记这种特殊情况</span></span><br><span class="line"> <span class="keyword">if</span> position.x < <span class="number">0</span> <span class="keyword">and</span> self.x == <span class="number">0</span>:</span><br><span class="line"> self.x = -<span class="number">128</span></span><br><span class="line"> <span class="keyword">if</span> position.y < <span class="number">0</span> <span class="keyword">and</span> self.y == <span class="number">0</span>:</span><br><span class="line"> self.y = -<span class="number">128</span></span><br><span class="line"> <span class="keyword">if</span> position.z < <span class="number">0</span> <span class="keyword">and</span> self.z == <span class="number">0</span>:</span><br><span class="line"> self.z = -<span class="number">128</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - new position: x:{} | y:{} | z:{}".format(self.x,self.y,self.z))</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - (position.x % (precision * 6)):{} | position.x:{} | (precision * 6):{}".format(position.x % (precision * 6),position.x,precision * 6))</span></span><br><span class="line"> <span class="comment"># result = (dividend % divisor + divisor) % divisor</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> position.x < <span class="number">0</span>: <span class="comment"># 因为python 对于正数取余和负数取余操作是不一样的,需要分开处理</span></span><br><span class="line"> minBoxX = position.x % -(precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> minBoxX = position.x % (precision * <span class="number">6</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.y < <span class="number">0</span>:</span><br><span class="line"> minBoxY = position.y % -(precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> minBoxY = position.y % (precision * <span class="number">6</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.z < <span class="number">0</span>:</span><br><span class="line"> minBoxZ = position.z % -(precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> minBoxZ = position.z % (precision * <span class="number">6</span>)</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - new minBox: minBoxX:{} | minBoxY:{} | minBoxZ:{}".format(minBoxX,minBoxY,minBoxZ))</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 转化为以精度为单位的坐标</span></span><br><span class="line"> unitX = <span class="built_in">int</span>(minBoxX / precision)</span><br><span class="line"> unitY = <span class="built_in">int</span>(minBoxY / precision)</span><br><span class="line"> unitZ = <span class="built_in">int</span>(minBoxZ / precision)</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - new unit-1: unitX:{} | unitY:{} | unitZ:{} | 7.0/3:int(7.1 / 2):{}".format(unitX,unitY,unitZ,int(7.1 / 2)))</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> position.x < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitX != <span class="number">0</span>: unitX = <span class="number">6</span> + unitX</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.y < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitY != <span class="number">0</span>: unitY = <span class="number">6</span> + unitY</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> position.z < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitZ != <span class="number">0</span>: unitZ = <span class="number">6</span> + unitZ</span><br><span class="line"></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - new unit-2: unitX:{} | unitY:{} | unitZ:{}".format(unitX,unitY,unitZ))</span></span><br><span class="line"> self.spaceIndex = OptimizePosition.spaceIndexs[unitX][unitY][unitZ]</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePosition - spaceIndex:{} ".format(self.spaceIndex))</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">OptimizePositinoToVector3</span>(<span class="params">self, precision = <span class="number">2</span></span>):</span><br><span class="line"> res = Vector3()</span><br><span class="line"> <span class="comment"># 首先处理值为-128的特殊情况</span></span><br><span class="line"> <span class="keyword">if</span> self.x != -<span class="number">128</span>:</span><br><span class="line"> res.x = self.x * (precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.x = <span class="number">0</span></span><br><span class="line"> <span class="keyword">if</span> self.y != -<span class="number">128</span>:</span><br><span class="line"> res.y = self.y * (precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.y = <span class="number">0</span></span><br><span class="line"> <span class="keyword">if</span> self.z != -<span class="number">128</span>:</span><br><span class="line"> res.z = self.z * (precision * <span class="number">6</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.z = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePositinoToVector3 - res: x:{} | y:{} | z:{} | spaceIndex:{}".format(res.x,res.y,res.z,self.spaceIndex))</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.spaceIndex><span class="number">215</span>:</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePositinoToVector3 - minbox precision loss!!!!!! self.spaceIndex:{}".format(self.spaceIndex))</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"> unitX = <span class="built_in">int</span>(self.spaceIndex / (<span class="number">6</span> * <span class="number">6</span>))</span><br><span class="line"> unitY = <span class="built_in">int</span>((self.spaceIndex / <span class="number">6</span>) % <span class="number">6</span>)</span><br><span class="line"> unitZ = <span class="built_in">int</span>(self.spaceIndex % <span class="number">6</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePositinoToVector3 - new unit-2: unitX:{} | unitY:{} | unitZ:{} ".format(unitX,unitY,unitZ))</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.x < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitX != <span class="number">0</span>: unitX -= <span class="number">6</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.y < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitY != <span class="number">0</span>: unitY -= <span class="number">6</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.z < <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">if</span> unitZ != <span class="number">0</span>: unitZ -= <span class="number">6</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePositinoToVector3 - new unit-1: unitX:{} | unitY:{} | unitZ:{} ".format(unitX,unitY,unitZ))</span></span><br><span class="line"></span><br><span class="line"> unitX *= precision</span><br><span class="line"> unitY *= precision</span><br><span class="line"> unitZ *= precision</span><br><span class="line"></span><br><span class="line"> res.x += unitX</span><br><span class="line"> res.y += unitY</span><br><span class="line"> res.z += unitZ</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 取小盒子中心点,接收值和原值的差值在(precision/2.0f)范围内</span></span><br><span class="line"> <span class="keyword">if</span> self.x >= <span class="number">0</span>:</span><br><span class="line"> res.x += (precision / <span class="number">2.0</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.x -= (precision / <span class="number">2.0</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.y >= <span class="number">0</span>:</span><br><span class="line"> res.y += (precision / <span class="number">2.0</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.y -= (precision / <span class="number">2.0</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> self.z >= <span class="number">0</span>:</span><br><span class="line"> res.z += (precision / <span class="number">2.0</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> res.z -= (precision / <span class="number">2.0</span>)</span><br><span class="line"></span><br><span class="line"> res.x /= <span class="number">1000</span></span><br><span class="line"> res.y /= <span class="number">1000</span></span><br><span class="line"> res.z /= <span class="number">1000</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizePositinoToVector3 - res: x:{} | y:{} | z:{}".format(res.x,res.y,res.z))</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OptimizeDirection</span>:</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.x = <span class="number">0</span></span><br><span class="line"> self.y = <span class="number">0</span></span><br><span class="line"> self.z = <span class="number">0</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"OptimizeDirection (x:{} y:{} z:{})"</span>.<span class="built_in">format</span>(self.x,self.y,self.z)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">OptimizeDirectionFromVector3</span>(<span class="params">self,direction</span>):</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 从Vector3创建</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizeDirection - direction: x:{} | y:{} | z:{}".format(direction.x,direction.y,direction.z))</span></span><br><span class="line"> <span class="keyword">if</span> direction.x > <span class="number">360</span>:</span><br><span class="line"> direction.x = <span class="number">360</span></span><br><span class="line"> <span class="keyword">elif</span> direction.x < <span class="number">0</span>:</span><br><span class="line"> direction.x = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> direction.y > <span class="number">360</span>:</span><br><span class="line"> direction.y = <span class="number">360</span></span><br><span class="line"> <span class="keyword">elif</span> direction.y < <span class="number">0</span>:</span><br><span class="line"> direction.y = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> direction.z > <span class="number">360</span>:</span><br><span class="line"> direction.z = <span class="number">360</span></span><br><span class="line"> <span class="keyword">elif</span> direction.z < <span class="number">0</span>:</span><br><span class="line"> direction.z = <span class="number">0</span></span><br><span class="line"> self.x = <span class="built_in">int</span>(<span class="number">255</span> * (direction.x / <span class="number">360.0</span>))</span><br><span class="line"> self.y = <span class="built_in">int</span>(<span class="number">255</span> * (direction.y / <span class="number">360.0</span>))</span><br><span class="line"> self.z = <span class="built_in">int</span>(<span class="number">255</span> * (direction.z / <span class="number">360.0</span>))</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizeDirection - new direction: x:{} | y:{} | z:{}".format(self.x,self.y,self.z))</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">OptimizeDirectionToVector3</span>(<span class="params">self</span>):</span><br><span class="line"> res = Vector3()</span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizeDirectionToVector3 - optimizeDirection: x:{} | y:{} | z:{}".format(self.x,self.y,self.z))</span></span><br><span class="line"></span><br><span class="line"> res.x = (self.x / <span class="number">255.0</span>) * <span class="number">360</span></span><br><span class="line"> res.y = (self.y / <span class="number">255.0</span>) * <span class="number">360</span></span><br><span class="line"> res.z = (self.z / <span class="number">255.0</span>) * <span class="number">360</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("OptimizeDirectionToVector3 - res: x:{} | y:{} | z:{}".format(res.x,res.y,res.z))</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">testPosition</span>():</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 位置单元测试</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">15230</span>, -<span class="number">15000</span>, -<span class="number">1</span>):</span><br><span class="line"> v = Vector3(i / <span class="number">10000.0</span>, <span class="number">0.00881</span>, <span class="number">0.0002131</span>)</span><br><span class="line"> op = OptimizePosition()</span><br><span class="line"> op.OptimizePositionFromVector3(v)</span><br><span class="line"> v2 = OptimizePosition.OptimizePositinoToVector3(op)</span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">round</span>(<span class="built_in">abs</span>(v2.x - v.x), <span class="number">3</span>) > <span class="number">0.001</span> <span class="keyword">and</span> v.x < <span class="number">1.523</span>:</span><br><span class="line"> <span class="comment"># DEBUG_MSG("test failed - presion loss", i, "v2.x", v2.x, "v.x", v.x, "abs(v2.x - v.x)", abs(v2.x - v.x))</span></span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("test done, all successful")</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">testDirection</span>():</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 旋转角度单元测试</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">365000</span>, -<span class="number">365000</span>, -<span class="number">1</span>):</span><br><span class="line"> v = Vector3(i / <span class="number">1000.0</span>, <span class="number">0.0</span>, <span class="number">0.000</span>)</span><br><span class="line"> od = OptimizeDirection()</span><br><span class="line"> od.OptimizeDirectionFromVector3(v)</span><br><span class="line"> v2 = od.OptimizeDirectionToVector3()</span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">round</span>(<span class="built_in">abs</span>(v2.x - v.x), <span class="number">3</span>) > <span class="number">1.5</span> :</span><br><span class="line"> <span class="comment"># DEBUG_MSG("optimize direction test failed - presion loss", i, "v2.x", v2.x, "v.x", v.x, "abs(v2.x - v.x)", abs(v2.x - v.x))</span></span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> <span class="comment"># DEBUG_MSG("test done, all successful")</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> <span class="comment"># p = Vector3()</span></span><br><span class="line"> <span class="comment"># p.x = -0.001</span></span><br><span class="line"> <span class="comment"># p.y = 0.002</span></span><br><span class="line"> <span class="comment"># p.z = 0.0033</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># op = OptimizePosition()</span></span><br><span class="line"> <span class="comment"># op.OptimizePositionFromVector3(p)</span></span><br><span class="line"> <span class="comment"># # DEBUG_MSG(op.OptimizePositinoToVector3())</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># test()</span></span><br><span class="line"></span><br><span class="line"> d = Vector3()</span><br><span class="line"> d.x = <span class="number">360</span></span><br><span class="line"> d.y = <span class="number">0</span></span><br><span class="line"> d.z = <span class="number">185</span></span><br><span class="line"></span><br><span class="line"> od = OptimizeDirection()</span><br><span class="line"> od.OptimizeDirectionFromVector3(d)</span><br><span class="line"> od.OptimizeDirectionToVector3()</span><br><span class="line"></span><br><span class="line"> testDirection()</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h4 id="原理"><a href="#原理" class="headerlink"</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
</entry>
<entry>
<title>记一次iftop带宽显示和实际程序发送的字节不相等问题排查</title>
<link href="https://lixiang365.github.io/post/cb10f09a.html"/>
<id>https://lixiang365.github.io/post/cb10f09a.html</id>
<published>2024-01-18T12:00:00.000Z</published>
<updated>2024-01-18T12:00:00.000Z</updated>
<content type="html"><![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>在做带宽优化时,把应用层socket发送的字节统计出来,然后在Linux使用iftop发现和统计的字节不相等,大概有2~3倍的差距。</p><h2 id="分析问题"><a href="#分析问题" class="headerlink" title="分析问题"></a>分析问题</h2><ol><li>最开始觉得是TCP/IP协议头占了多余的字节。然后开始在网上查其他人写的文档说,iftop是统计数据的,不涉及协议头。现在问题的关键就变成了验证iftop</li><li>后来换了环境,在windows系统下测了下,发现捕捉到的字节和socket发送的一致。Windows统计的是数据,不包括协议头。</li><li>后来怀疑是iftop工具问题,换了几个其他类型的工具,发现和iftop差不多。</li><li>网上找不到更多的iftop的相关文档,后来开始去github上找到<code>iftop源码</code>看,花费了几个小时终于找到了,iftop统计的是IP数据包,包括IP、TCP协议头、数据。破解了心中的疑惑。</li></ol><h2 id="有效负载"><a href="#有效负载" class="headerlink" title="有效负载"></a>有效负载</h2><p>有了正常的带宽情况、发送的字节数,可以计算出有效负载,发现有效负载很少,协议开销比较大。原因就是发送的数据包偏小,导致TCP\IP协议头占了大部分字节。TCP Socket有一个选项是<code>TCP_NODELAY</code>,如果是false的话,调用send,socket会立刻发送,当是true的话,tcp有算法(TODO稍后补充算法细节),会延迟发送数据,会把多个小的数据包合成一个大包,发送。这样就减少了协议头的开销。这个选项多个平台实默认是false。</p><h2 id="排查这个问题的教训"><a href="#排查这个问题的教训" class="headerlink" title="排查这个问题的教训"></a>排查这个问题的教训</h2><p>过于相信网上的三方文档,当心中有疑惑时,应该去找官网文档,或者看源码自己分析。一步步求真。多次验证,交叉验证。</p>]]></content>
<summary type="html"><h2 id="问题描述"><a href="#问题描述" class="headerlink"</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="网络编程" scheme="https://lixiang365.github.io/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
<category term="网络" scheme="https://lixiang365.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
<category term="问题排查" scheme="https://lixiang365.github.io/tags/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/"/>
</entry>
<entry>
<title>多人网络位置同步</title>
<link href="https://lixiang365.github.io/post/4a39f9e5.html"/>
<id>https://lixiang365.github.io/post/4a39f9e5.html</id>
<published>2024-01-13T12:47:00.000Z</published>
<updated>2024-01-13T12:47:00.000Z</updated>
<content type="html"><![CDATA[<h2 id="多人游戏位置同步"><a href="#多人游戏位置同步" class="headerlink" title="多人游戏位置同步"></a>多人游戏位置同步</h2><p>关于多人游戏的位置同步研究还是比较多的。这里我们主要描述下业内经典的同步方式</p><ol><li>首先明确,服务器位置是权威的。</li><li>客户端操作和客户端自己的位置显示是分开的。</li><li>理想情况下,客户端显示位置应该和服务器相同。</li></ol><p>正常情况是,客户端把操作发给服务器,比如向前走2s,然后服务器计算位置,在一定服务器帧率下(例如10HZ)把位置同步给所有客户端或者也可以把操作同步给所有客户端(包括自己)。然后客户端自己再修改显示自己的位置,客户端修改其他人的位置是通过收到服务器发过来的位置,通过插值修改。</p><p><strong>同步出现的问题</strong>:自己客户端出现自己位置修改延迟,自己客户端其他人卡顿,卡视野,以下的方法都是为了解决这些问题。</p><h3 id="预测"><a href="#预测" class="headerlink" title="预测"></a>预测</h3><p>正常情况下因为自己把操作同步给服务器后,服务器再把位置发给自己,这个过程考虑网络延迟需要时间。就造成客户端的操作和自己玩家的显示有延迟。</p><p>预测其实是指A客户端在通过摇杆,方向键修改位置时,把方向和时间同步给服务器后,然后立刻把位置更改应用的自己的显示上,<strong>解决操作和显示不顺畅</strong>的问题。因为网络延迟,实际上此时服务器还是上次同步的位置。等服务器收到了A客户端新的操作,然后服务器移动位置,然后把新的位置发给所有客户端(包括A客户端),A客户端此时可能已经往前走了更远的距离,服务器发过来的还是之前的位置。因为服务器是权威的,要把服务器的位置设置给自己。这样就把自己往后拉了,就出现了<strong>拉扯</strong>的问题。</p><h3 id="和解"><a href="#和解" class="headerlink" title="和解"></a>和解</h3><p>客户端预测会造成一个问题,当客户端把操作发给服务器后,服务器又把位置发送给所有人(包括自己),因为服务器位置是权威的,所以要把服务器位置设置给自己。因为自己客户端预测已经往前走了一点距离,这时就会把自己拉回服务器的位置,造成<strong>拉扯</strong>。</p><p><strong>和解</strong>就是为了解决自己客户端显示拉扯的问题<br>具体实现:<br>给服务器同步的操作都会有一个序列号,本地也有一个操作列表,当服务器把位置变更传回来时,先回滚至服务器的权威位置。然后再查找本地的操作序列,比服务器旧的直接删除,比服务器位置新的,重新设置上去,自己就不会回到原来的位置。</p><h3 id="插值"><a href="#插值" class="headerlink" title="插值"></a>插值</h3><p>自己客户端内的其他人的位置,因为服务器同步位置也是有时间间隔的,如果实时同步,成本太高。所以两个位置之间可能相差较远,如果直接设置,就会产生瞬移卡顿之类,所以通过<code>匀速插值</code>。会比较顺滑</p><h3 id="服务器的下发帧率"><a href="#服务器的下发帧率" class="headerlink" title="服务器的下发帧率"></a>服务器的下发帧率</h3><p>当服务器下发帧率比较高时,其他客户端的位置变化就更及时,插值也就更顺畅,如果帧率比较低,可能就会出现卡视野,穿墙的问题。不过下发帧率涉及到带宽成本。需要权衡。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://www.gabrielgambetta.com/client-side-prediction-live-demo.html">位置同步在线演示的demo</a></p><p><a href="https://www.codersblock.org/blog/client-side-prediction-in-unity-2018">Unity中使用物理的客户端预测</a></p><p><a href="https://tsrpc.cn/fight/index.html">使用同步实现demo Cocos Creator | frontend (tsrpc.cn)</a></p><p><a href="https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html">客户端预测和服务器和解 - Gabriel Gambetta</a></p><p><a href="https://www.gabrielgambetta.com/client-server-game-architecture.html">游戏架构</a></p>]]></content>
<summary type="html"><h2 id="多人游戏位置同步"><a href="#多人游戏位置同步" class="headerlink"</summary>
<category term="游戏" scheme="https://lixiang365.github.io/categories/%E6%B8%B8%E6%88%8F/"/>
<category term="游戏" scheme="https://lixiang365.github.io/tags/%E6%B8%B8%E6%88%8F/"/>
<category term="网络" scheme="https://lixiang365.github.io/tags/%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>Rust生命周期</title>
<link href="https://lixiang365.github.io/post/63a698fd.html"/>
<id>https://lixiang365.github.io/post/63a698fd.html</id>
<published>2023-10-17T02:52:00.000Z</published>
<updated>2023-10-17T02:52:46.000Z</updated>
<content type="html"><![CDATA[<p>根据rustlings里的例子,了解下lifetimes</p><h2 id="lifetimes"><a href="#lifetimes" class="headerlink" title="lifetimes"></a>lifetimes</h2><p>引用本身是一个指针,rust使用所有权系统来确保引用有效,但是编译器是相对保守的。在一些使用引用作为参数和返回值的函数,有时候代码没错但是却编译不过,因为编译器无法正确推理出引用的生命周期,无法保证安全,此时我们就需要使用生命周期标注告诉编译器引用的生命周期。<br>从引用方向理解,生命周期就是要确保引用比所有者活的更久,因为引用依赖所有者。否则就出现了悬垂引用,rust要确保永远不会出现这种情况。</p><h3 id="生命周期的特点"><a href="#生命周期的特点" class="headerlink" title="生命周期的特点"></a>生命周期的特点</h3><ol><li>生命周期告诉编译器在给定情况下如何检查引用存活久到被认为有效。比如生命周期说 “确保参数 ‘a’ 和参数 ‘b’ 存活一样久使得返回值有效”。</li><li>它们<code>仅在借用(即引用)时是必需的</code>,因为复制或移动的参数在其作用域内,无法在外部引用。</li><li>生命周期意味可以检查例如函数的调用代码,以确保其参数有效。生命周期是由它们的调用者决定的.</li></ol><h3 id="lifetimes1"><a href="#lifetimes1" class="headerlink" title="lifetimes1"></a>lifetimes1</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// lifetimes1.rs</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Rust 编译器需要知道如何确认提供的引用是有效的,</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"></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="comment">// 这个函数编译后可以从编译报错中知道原因</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">longest</span>(x: &<span class="type">str</span>, y: &<span class="type">str</span>) <span class="punctuation">-></span> &<span class="type">str</span> {</span><br><span class="line"> <span class="keyword">if</span> x.<span class="title function_ invoke__">len</span>() > y.<span class="title function_ invoke__">len</span>() {</span><br><span class="line"> x</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> y</span><br><span class="line"> }</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">fn</span> <span class="title function_">longest</span><<span class="symbol">'a</span>>(x: &<span class="symbol">'a</span> <span class="type">str</span>, y: &<span class="symbol">'static</span> <span class="type">str</span>) <span class="punctuation">-></span> &<span class="symbol">'a</span> <span class="type">str</span> {</span><br><span class="line"> <span class="keyword">if</span> x.<span class="title function_ invoke__">len</span>() > y.<span class="title function_ invoke__">len</span>() {</span><br><span class="line"> x</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> y</span><br><span class="line"> }</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="comment">// <'a,'b:'a> 意思是b比a活的时间要长。只要a存在,b一定存在</span></span><br><span class="line"><span class="comment">// 有点类似于子类与父类</span></span><br><span class="line"><span class="comment">// 在这里返回a,相当于b的生命包裹a的生命周期,返回短的生命周期也是可以的</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">longest</span><<span class="symbol">'a</span>,<span class="symbol">'b</span>:<span class="symbol">'a</span>>(x: &<span class="symbol">'a</span> <span class="type">str</span>, y: &<span class="symbol">'b</span> <span class="type">str</span>) <span class="punctuation">-></span> &<span class="symbol">'a</span> <span class="type">str</span> {</span><br><span class="line"> <span class="keyword">if</span> x.<span class="title function_ invoke__">len</span>() > y.<span class="title function_ invoke__">len</span>() {</span><br><span class="line"> x</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> y</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// string1 生命周期是main函数内</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">string1</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"abcd"</span>);</span><br><span class="line"> <span class="comment">// string2 生命周期是'static</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">string2</span> = <span class="string">"xyz"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 编译器不知道result的生命周期,因为传入两个引用返回一个引用。</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">// 编译器在编译main函数在判断result生命周期时,只会根据函数签名来判断,不会去进入函数体判断。</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">longest</span>(string1.<span class="title function_ invoke__">as_str</span>(), string2);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The longest string is '{}'"</span>, result);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="lifetimes2"><a href="#lifetimes2" class="headerlink" title="lifetimes2"></a>lifetimes2</h3><figure class="highlight rust"><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="keyword">fn</span> <span class="title function_">longest</span><<span class="symbol">'a</span>>(x: &<span class="symbol">'a</span> <span class="type">str</span>, y: &<span class="symbol">'a</span> <span class="type">str</span>) <span class="punctuation">-></span> &<span class="symbol">'a</span> <span class="type">str</span> {</span><br><span class="line"> <span class="keyword">if</span> x.<span class="title function_ invoke__">len</span>() > y.<span class="title function_ invoke__">len</span>() {</span><br><span class="line"> x</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> y</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">string1</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"long string is long"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// modified</span></span><br><span class="line"> <span class="comment">// 将string2 移动到此处,这样,string2生命周期一直到函数末尾,到使用的时候依然有效,和string1有公共的生命周期</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">string2</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"xyz"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span>;</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// string2 的生命周期不够长。</span></span><br><span class="line"> <span class="comment">// let string2 = String::from("xyz");</span></span><br><span class="line"> result = <span class="title function_ invoke__">longest</span>(string1.<span class="title function_ invoke__">as_str</span>(), string2.<span class="title function_ invoke__">as_str</span>());</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The longest string is '{}'"</span>, result);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="lifetimes3"><a href="#lifetimes3" class="headerlink" title="lifetimes3"></a>lifetimes3</h3><figure class="highlight rust"><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><br><span class="line"><span class="comment">// 对于struct 要自己显式写生命周期</span></span><br><span class="line"><span class="comment">// 生命周期可以自己写与意义的名字</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Book</span><<span class="symbol">'bookInfo</span>> {</span><br><span class="line"> author: &<span class="symbol">'bookInfo</span> <span class="type">str</span>,</span><br><span class="line"> title: &<span class="symbol">'bookInfo</span> <span class="type">str</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">name</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"Jill Smith"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">title</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"Fish Flying"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">book</span> = Book { author: &name, title: &title };</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} by {}"</span>, book.title, book.author);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>根据rustlings里的例子,了解下lifetimes</p>
<h2 id="lifetimes"><a href="#lifetimes" class="headerlink"</summary>
<category term="Rust基础" scheme="https://lixiang365.github.io/categories/Rust%E5%9F%BA%E7%A1%80/"/>
<category term="Rust专题" scheme="https://lixiang365.github.io/categories/Rust%E4%B8%93%E9%A2%98/"/>
<category term="Rust" scheme="https://lixiang365.github.io/tags/Rust/"/>
</entry>
<entry>
<title>Python热更思路</title>
<link href="https://lixiang365.github.io/post/1654ffe7.html"/>
<id>https://lixiang365.github.io/post/1654ffe7.html</id>
<published>2023-09-05T09:19:00.000Z</published>
<updated>2023-09-05T09:19:53.000Z</updated>
<content type="html"><![CDATA[<h1 id="python-热更思路"><a href="#python-热更思路" class="headerlink" title="python 热更思路"></a>python 热更思路</h1><h2 id="关于python类型的理解"><a href="#关于python类型的理解" class="headerlink" title="关于python类型的理解"></a>关于python类型的理解</h2><p>1、对于Python,最重要的一点理解是,万物皆对象。 Python 中的一切皆是对象,因此 Python 中的每个类本身也是一个对象,属于另外一个类的实例。Python中所有类都继承于这个object基类。</p><p>2、Object 类是所有 Python 对象的基类。在 Python 中,每个对象都是 Object 类的实例对象。因此,我们可以使用 Object 类中的方法和属性来操作和访问任何对象。</p><p>3、Class类是用于创建新类的类。当定义一个类的时候,这个类就是Class类的实例对象,但是这种说法不是很准确,准确来说,这个类的是Type类的实例对象,而不是Class类的实例对象。Class类和type类的关系是当每次使用Class创建新的类,python会自动调用type类的构造函数__new__()来创建一个新的类对象,并返回该对象的引用。然后,Python 解释器会进一步处理 class 语句中的代码,将其中定义的属性和方法等信息保存到该类对象的 dict 属性中。class 类本身并没有实例对象,可以将Class看做是用于创建类对象的语法糖。</p><p>4、Type类是元类,Python中所有类的元类,类的实例对象,Object类也是它的实例对象。可以理解成神话中的盘古,万物都是他演化的。<br>Object类可以理解为类的父类</p><p>这里不做过多的展开,需要记住,定义的Class是Type类的实例对象,保存有类的方法,属性,定义函数也是一个对象,可以使用dir来查看对象拥有的属性等</p><h2 id="代码层热更"><a href="#代码层热更" class="headerlink" title="代码层热更"></a>代码层热更</h2><p>通过动态执行一段代码完成逻辑更新</p><h3 id="修改全局变量"><a href="#修改全局变量" class="headerlink" title="修改全局变量"></a>修改全局变量</h3><figure class="highlight python"><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="comment"># Config模块</span></span><br><span class="line">GLOBAL_CONFIG_NAME = <span class="string">"a"</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Config</span>():</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name=<span class="string">"a"</span></span>):</span><br><span class="line">self.name = name</span><br><span class="line"><span class="keyword">pass</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getConfigName</span>(<span class="params">self</span>):</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"config name {}"</span>.<span class="built_in">format</span>(self.name))</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">getGlobalConfigName</span>(<span class="params">self</span>):</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"global config name {}"</span>.<span class="built_in">format</span>(GLOBAL_CONFIG_NAME))</span><br><span class="line">config = Config(<span class="string">"a"</span>)</span><br></pre></td></tr></table></figure><p>热更代码</p><figure class="highlight python"><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="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更代码</span></span><br><span class="line">code=<span class="string">"""</span></span><br><span class="line"><span class="string">import config</span></span><br><span class="line"><span class="string">config.GLOBAL_CONFIG_NAME = "c"</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span>(code)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line">config.config.getGlobalConfigName()</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="comment"># global config name a</span></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># global config name c</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 python"><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="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更代码</span></span><br><span class="line">code=<span class="string">"""</span></span><br><span class="line"><span class="string">import config</span></span><br><span class="line"><span class="string">def getGlobalConfigName(self):</span></span><br><span class="line"><span class="string">print("global config new name {}".format("d"))</span></span><br><span class="line"><span class="string">config.Config.getGlobalConfigName = getGlobalConfigName</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exec</span>(code)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line">config.config.getGlobalConfigName()</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="comment"># global config name a</span></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># global config new name d</span></span><br></pre></td></tr></table></figure><h3 id="函数有被其他地方引用"><a href="#函数有被其他地方引用" class="headerlink" title="函数有被其他地方引用"></a>函数有被其他地方引用</h3><figure class="highlight python"><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="keyword">class</span> <span class="title class_">ConfigMgr</span>():</span><br><span class="line"><span class="keyword">def</span><span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">self.cbs = <span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">register</span>(<span class="params">self,cb</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">注册回调</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line">self.cbs.add(cb)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">deregister</span>(<span class="params">self,cb</span>):</span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">解除注册</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">if</span> cb <span class="keyword">in</span> self.cbs:</span><br><span class="line">self.cbs.remove(cb)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">callAllcbs</span>(<span class="params">self</span>):</span><br><span class="line"><span class="keyword">for</span> cb <span class="keyword">in</span> self.cbs:</span><br><span class="line">cb()</span><br></pre></td></tr></table></figure><p>热更代码</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">mgr = config.ConfigMgr()</span><br><span class="line">mgr.register(config.config.getGlobalConfigName)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更代码</span></span><br><span class="line">code=<span class="string">"""</span></span><br><span class="line"><span class="string">import config</span></span><br><span class="line"><span class="string"># 取消注册</span></span><br><span class="line"><span class="string">mgr.deregister(config.config.getGlobalConfigName)</span></span><br><span class="line"><span class="string">def getGlobalConfigName(self):</span></span><br><span class="line"><span class="string">print("global config new name {}".format("d"))</span></span><br><span class="line"><span class="string">config.Config.getGlobalConfigName = getGlobalConfigName</span></span><br><span class="line"><span class="string"># 重新注册</span></span><br><span class="line"><span class="string">mgr.register(config.config.getGlobalConfigName)</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="built_in">exec</span>(code)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">mgr.callAllcbs()</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="comment"># global config name a</span></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># global config new name d</span></span><br><span class="line"><span class="comment"># global config new name d</span></span><br></pre></td></tr></table></figure><h2 id="模块层热更"><a href="#模块层热更" class="headerlink" title="模块层热更"></a>模块层热更</h2><p>使用 importlib.reload(module) 函数,可以重新加载指定的模块 module,并更新模块中的代码。这样,我们就可以在同一个 Python 会话中多次修改和调试一个模块,而不需要重新启动整个解释器。</p><p>请注意,importlib.reload 函数只会重新加载指定的模块本身,而不会重新加载该模块所依赖的其他模块。如果你的模块依赖其他模块的更新,你可能需要手动重新加载这些依赖模块。</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> config</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hotFix</span>():</span><br><span class="line"><span class="comment"># 中断程序</span></span><br><span class="line"><span class="built_in">input</span>()</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line">importlib.reload(config)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"><span class="comment"># 保存了旧的模块内的实例</span></span><br><span class="line">configOrigin = config.config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"><span class="comment"># 热更</span></span><br><span class="line"></span><br><span class="line">hotFix()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line"><span class="comment"># 判断模块内实例是否变了</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"config 对象实例是否一致:{}"</span>.<span class="built_in">format</span>(config.config <span class="keyword">is</span> configOrigin))</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">configOrigin.getGlobalConfigName()</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="comment"># 热更前</span></span><br><span class="line"><span class="comment"># global config name a</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># config 对象实例是否改变:False</span></span><br><span class="line"><span class="comment"># reload after global config name a</span></span><br><span class="line"><span class="comment"># global config name a</span></span><br></pre></td></tr></table></figure><p>reload之后:<br>1、原全局变量会被覆盖掉,如果该全局变量对象的状态会被重置,导致程序失常。<br>2、重载后实例的类仍然是指向旧类</p><h3 id="全局变量处理"><a href="#全局变量处理" class="headerlink" title="全局变量处理"></a>全局变量处理</h3><ol><li>数据复原</li></ol><ul><li>在模块内写一个onReload函数,当模块被重新加载时,调用这个函数初始化一些状态</li><li>全局对象的类都提供一个初始化函数,在重载的时候调用<br>这种处理约束对开发约束很大,必须提前指定好规则。</li></ul><ol start="2"><li>引用复原<br>重载前模块的全局变量替换重载后的全局变量</li></ol><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> config</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hotFix</span>():</span><br><span class="line"><span class="comment"># 中断程序</span></span><br><span class="line"><span class="built_in">input</span>()</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> types <span class="keyword">import</span> FunctionType</span><br><span class="line">oldMoudel = <span class="built_in">__import__</span>(<span class="string">"config"</span>)</span><br><span class="line">oldMoudelData = {}</span><br><span class="line">attrList = <span class="built_in">dir</span>(oldMoudel)</span><br><span class="line"><span class="comment"># 将旧模块的东西全部保存</span></span><br><span class="line"><span class="keyword">for</span> attrName <span class="keyword">in</span> attrList:</span><br><span class="line">oldMoudelData[attrName] = <span class="built_in">getattr</span>(oldMoudel, attrName)</span><br><span class="line"><span class="comment"># 重载模块</span></span><br><span class="line">importlib.reload(oldMoudel)</span><br><span class="line">newMoudle = <span class="built_in">__import__</span>(<span class="string">"config"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> attrName <span class="keyword">in</span> <span class="built_in">dir</span>(newMoudle):</span><br><span class="line"><span class="keyword">if</span> attrName <span class="keyword">in</span> oldMoudelData:</span><br><span class="line"><span class="comment"># 自定义那些东西需要替换</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">type</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], FunctionType) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">int</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">float</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">str</span>):</span><br><span class="line"><span class="keyword">pass</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="built_in">setattr</span>(newMoudle, attrName, oldMoudelData[attrName])</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"><span class="comment"># 保存了旧的模块内的实例</span></span><br><span class="line">configOrigin = config.config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"><span class="comment"># 热更</span></span><br><span class="line"></span><br><span class="line">hotFix()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line"><span class="comment"># 判断模块内实例是否变了</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"config 对象实例是否一致:{}"</span>.<span class="built_in">format</span>(config.config <span class="keyword">is</span> configOrigin))</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">configOrigin.getGlobalConfigName()</span><br></pre></td></tr></table></figure><h3 id="旧类的实例对象处理"><a href="#旧类的实例对象处理" class="headerlink" title="旧类的实例对象处理"></a>旧类的实例对象处理</h3><p>三种方式:</p><ul><li>重新生成实例</li><li>切换实例对象的类</li><li>对实例对象的类更新</li></ul><p>1、 重新生成实例对象,这个比较简单</p><p>2、切换实例对象的类</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> config</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hotFix</span>():</span><br><span class="line"><span class="comment"># 中断程序</span></span><br><span class="line"><span class="built_in">input</span>()</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"><span class="comment"># 重载模块</span></span><br><span class="line">importlib.reload(config)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"><span class="comment"># 保存了旧的模块内的实例</span></span><br><span class="line">configOrigin = config.config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"><span class="comment"># 热更</span></span><br><span class="line"></span><br><span class="line">hotFix()</span><br><span class="line"><span class="comment"># 换类</span></span><br><span class="line">configOrigin.__class__ = config.Config</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line"><span class="comment"># 判断模块内实例是否变了</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"config 对象实例是否一致:{}"</span>.<span class="built_in">format</span>(config.config <span class="keyword">is</span> configOrigin))</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">configOrigin.getGlobalConfigName()</span><br><span class="line"></span><br><span class="line"><span class="comment"># ---输出---</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更前</span></span><br><span class="line"><span class="comment"># reload global config name a</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># config 对象实例是否一致:False</span></span><br><span class="line"><span class="comment"># reload after global config name a</span></span><br><span class="line"><span class="comment"># reload after global config name a</span></span><br></pre></td></tr></table></figure><p>实现这种方法:</p><ul><li>必须额外写代码,这不是纯粹的模块层更新了</li><li>要找到所有该类的对象</li></ul><p>3、对实例对象的类更新<br>不改变实例对象对旧类的引用,将旧类内的类函数等引用修改指向新类的类函数,并且在新模块内的新类指向旧类,因为此时旧类的函数等引用都指向了新的函数,完成热更新。</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> config</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">hotFix</span>():</span><br><span class="line"><span class="comment"># 中断程序</span></span><br><span class="line"><span class="built_in">input</span>()</span><br><span class="line"><span class="keyword">import</span> importlib</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> types <span class="keyword">import</span> FunctionType</span><br><span class="line">oldMoudel = <span class="built_in">__import__</span>(<span class="string">"config"</span>)</span><br><span class="line">oldMoudelData = {}</span><br><span class="line">attrList = <span class="built_in">dir</span>(oldMoudel)</span><br><span class="line"><span class="comment"># 将旧模块的东西全部保存</span></span><br><span class="line"><span class="keyword">for</span> attrName <span class="keyword">in</span> attrList:</span><br><span class="line">oldMoudelData[attrName] = <span class="built_in">getattr</span>(oldMoudel, attrName)</span><br><span class="line"><span class="comment"># 重载模块</span></span><br><span class="line">importlib.reload(oldMoudel)</span><br><span class="line">newMoudle = <span class="built_in">__import__</span>(<span class="string">"config"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> attrName <span class="keyword">in</span> <span class="built_in">dir</span>(newMoudle):</span><br><span class="line"><span class="keyword">if</span> attrName <span class="keyword">in</span> oldMoudelData:</span><br><span class="line"><span class="comment"># 自定义那些东西需要替换</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">type</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], FunctionType) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">int</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">float</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">str</span>):</span><br><span class="line"><span class="keyword">pass</span></span><br><span class="line"><span class="keyword">elif</span> <span class="built_in">isinstance</span>(oldMoudelData[attrName], <span class="built_in">type</span>):</span><br><span class="line">ReplaceClassFunc(<span class="built_in">getattr</span>(newMoudle, attrName), oldMoudelData[attrName])</span><br><span class="line"><span class="built_in">setattr</span>(newMoudle, attrName, oldMoudelData[attrName])</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="built_in">setattr</span>(newMoudle, attrName, oldMoudelData[attrName])</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">ReplaceClassFunc</span>(<span class="params">srcClass, desClass</span>):</span><br><span class="line"><span class="keyword">for</span> attrName <span class="keyword">in</span> <span class="built_in">dir</span>(srcClass):</span><br><span class="line">attr = <span class="built_in">getattr</span>(srcClass, attrName)</span><br><span class="line"><span class="keyword">from</span> types <span class="keyword">import</span> FunctionType</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">isinstance</span>(attr, FunctionType) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(attr, <span class="built_in">int</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(attr, <span class="built_in">float</span>) \</span><br><span class="line"><span class="keyword">or</span> <span class="built_in">isinstance</span>(attr, <span class="built_in">str</span>):</span><br><span class="line"><span class="built_in">setattr</span>(desClass, attrName, attr)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更前"</span>)</span><br><span class="line"><span class="comment"># 保存了旧的模块内的实例</span></span><br><span class="line">configOrigin = config.config</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line"><span class="comment"># 热更</span></span><br><span class="line"></span><br><span class="line">hotFix()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"热更后"</span>)</span><br><span class="line"><span class="comment"># 判断模块内实例是否变了</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"config 对象实例是否一致:{}"</span>.<span class="built_in">format</span>(config.config <span class="keyword">is</span> configOrigin))</span><br><span class="line">config.config.getGlobalConfigName()</span><br><span class="line">configOrigin.getGlobalConfigName()</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="comment"># reload global config name a</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 热更后</span></span><br><span class="line"><span class="comment"># config 对象实例是否一致:False</span></span><br><span class="line"><span class="comment"># reload after global config name a</span></span><br><span class="line"><span class="comment"># reload after global config name a</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="python-热更思路"><a href="#python-热更思路" class="headerlink" title="python 热更思路"></a>python 热更思路</h1><h2 id="关于python类型的理解"><a</summary>
<category term="Python专栏" scheme="https://lixiang365.github.io/categories/Python%E4%B8%93%E6%A0%8F/"/>
<category term="Python" scheme="https://lixiang365.github.io/tags/Python/"/>
<category term="热更" scheme="https://lixiang365.github.io/tags/%E7%83%AD%E6%9B%B4/"/>
</entry>
<entry>
<title>Rust 数据结构的内存布局</title>
<link href="https://lixiang365.github.io/post/655fcddc.html"/>
<id>https://lixiang365.github.io/post/655fcddc.html</id>
<published>2023-08-31T12:47:00.000Z</published>
<updated>2023-09-13T12:47:00.000Z</updated>
<content type="html"><![CDATA[<h2 id="为什么要了解内存布局"><a href="#为什么要了解内存布局" class="headerlink" title="为什么要了解内存布局"></a>为什么要了解内存布局</h2><p>1、因为rust有自己的所有权规则,为了提高性能,有时候赋值数据并不是深拷贝。<br>2、rust类型可以从size方面划分为两种,一种是在编译器知道size大小,另一种是动态size大小(DST),在某些时候这些概念可能难以理解。从底层了解内存布局后,会让我们对rust的一些优秀实现思想有所了解。<br>3、学过C/C++的同学应该对内存会更熟悉一些</p><h2 id="存在栈中的类型"><a href="#存在栈中的类型" class="headerlink" title="存在栈中的类型"></a>存在栈中的类型</h2><p>只有在编译期确定大小的类型才会分配在栈上<br>在栈上为函数分配的内存称为栈帧栈上分配内存只需要移动栈指针,不需要任何系统调用,非常快</p><h3 id="i32"><a href="#i32" class="headerlink" title="i32"></a>i32</h3><p> i32类型需要4个字节</p><h2 id="存在堆上"><a href="#存在堆上" class="headerlink" title="存在堆上"></a>存在堆上</h2><h3 id="Box"><a href="#Box" class="headerlink" title="Box"></a>Box</h3><p><img src="https://s2.loli.net/2023/09/13/7Z6sC5qAavDINby.png" alt="box-type.png"></p><p>Box类型是智能指针,在栈上只存了一个指针,64为系统就是8个字节,指针指向的数据存在在堆中,如果是一个I32,则在堆上需要4个字节,在rust中堆内存分配的定义在GlobalAlloc trait中。(注:当使用Box时,内存分配器并不一定使用系统调用,内存分配器会以块的形式请求内存,减少系统调用次数,同时内存释放时也不一定就返回给了系统,内存分配器会管理记录使用和未使用的内存。)</p><p><img src="https://s2.loli.net/2023/09/13/RbMTDSahm3rz1XU.png" alt="rust堆内存分配定义.png"></p><figure class="highlight rust"><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="keyword">fn</span> <span class="title function_">main</span>(){</span><br><span class="line"> <span class="comment">// 在rust中赋值会发生堆内存拷贝</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">heap</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">heap</span>()<span class="punctuation">-></span><span class="type">Box</span><<span class="type">i32</span>>{</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">b</span> = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(<span class="number">23</span>);</span><br><span class="line"> b</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="有符号和无符号整型"><a href="#有符号和无符号整型" class="headerlink" title="有符号和无符号整型"></a>有符号和无符号整型</h3><p>rust的整数显示了整数的内存占用位数,例如i32、i8、u8、在内存中分别占4、1、1个字节。isize,usize类型在不同平台上占字节不同,在32位系统上占4个字节,64位系统占8个字节。</p><p><img src="https://s2.loli.net/2023/09/13/36F9lQZt4gmSP1N.png" alt="整数类型.png"></p><h3 id="char"><a href="#char" class="headerlink" title="char"></a>char</h3><p>char类型存储的是Unicode,在内存中占4个字节</p><p><img src="https://s2.loli.net/2023/09/13/HY1uwfUxkygCBiV.png" alt="char类型.png"></p><h3 id="元组类型"><a href="#元组类型" class="headerlink" title="元组类型"></a>元组类型</h3><p>元组类型如果成员都是存在<strong>栈</strong>上的,元组类型的数据也是在栈上顺序排列,但是因为有内存对齐的原因。所以元组所占的内存有时并不等于所有成员的和。(内存对齐是为了方便CPU快速访问,另一方面也是节省内存)</p><p><img src="https://s2.loli.net/2023/09/13/j4KaGWsz9iMRmYw.png" alt="元组类型.png"></p><figure class="highlight rust"><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="keyword">let</span> <span class="variable">a</span>:(<span class="type">char</span>,<span class="type">u8</span>,<span class="type">i32</span>) = (<span class="string">'a'</span>,<span class="number">7</span>,<span class="number">345</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查类型内存大小</span></span><br><span class="line">std::mem::size_of::<T>();</span><br><span class="line"><span class="comment">// 检查类型的对齐值,(该类型分配的总字节应该是对齐值的整倍)</span></span><br><span class="line">std::mem::align_of::<T>();</span><br><span class="line"></span><br><span class="line">std::mem::size_of::<(<span class="type">char</span>,<span class="type">u8</span>,<span class="type">i32</span>)>();<span class="comment">// 12</span></span><br><span class="line">std::mem::align_of::<(<span class="type">char</span>,<span class="type">u8</span>,<span class="type">i32</span>)>();<span class="comment">// 4</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="借用类型-T-和-mut-T"><a href="#借用类型-T-和-mut-T" class="headerlink" title="借用类型 &T 和&mut T"></a>借用类型 &T 和&mut T</h3><p>很多人对引用类型不是很理解。<br>引用类型存储在<strong>栈</strong>上,本身就是一个指针,值是地址,所以需要机器字长来存储,在64位系统上就是8个字节。<br>可变引用和引用在内存结构上是一样的,他们的区别在于他们的<code>使用方式</code>和<code>编译器对他们施加的约束,限制</code>。<br>所以,我们可以这样理解引用:<strong>带有约束的指针</strong>。</p><p><img src="https://s2.loli.net/2023/09/13/l1Tyc3fPdwEnVBQ.png" alt="引用类型.png"></p><h3 id="数组类型"><a href="#数组类型" class="headerlink" title="数组类型"></a>数组类型</h3><h4 id="定长数组"><a href="#定长数组" class="headerlink" title="定长数组"></a>定长数组</h4><p><code>let a:[i32;3] = [55,66,77]</code> 数组是大小固定的,和其他语言一样,数组是连续的内存。存储在栈上</p><p><img src="https://s2.loli.net/2023/09/13/PeLMBWi49qtN5nR.png" alt="数组类型.png"></p><h4 id="动态数组-Vector"><a href="#动态数组-Vector" class="headerlink" title="动态数组 Vector"></a>动态数组 Vector</h4><p><code>let v: Vec<i32> = vec![55,66,77];</code><br>动态数组大小可变,数据存在堆上,同时在栈上存储了堆数据的地址(指针)、容量、长度len,每一部分的大小都是一个机器字长,64位系统就是8个字节。注意,vec在栈上的大小始终是固定的,</p><h3 id="slice-切片-T-和-切片引用-T"><a href="#slice-切片-T-和-切片引用-T" class="headerlink" title="slice 切片[T] 和 切片引用&[T]"></a>slice 切片[T] 和 切片引用&[T]</h3><p>首先最重要的是我们需要搞清楚基本概念,切片是切片类型,切片引用是引用类型。是不同的类型。</p><figure class="highlight rust"><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="comment">// 切片</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s1</span>:[<span class="type">i32</span>] = a[<span class="number">0</span>..<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 切片引用</span></span><br><span class="line"> <span class="keyword">let</span> &s2:[<span class="type">i32</span>] = &a[<span class="number">0</span>..<span class="number">2</span>];</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>切片是数组底层数据元素的部分视图,切片没有指定元素的数量。所以在编译时,也不知道切片分配要分配多少字节,所以不能保存在变量中因为大小未知,不能分配在栈上。是动态大小类型(DST)。</p><p>切片引用是引用。我们知道引用是一个指针,存储数据地址,这里有个特殊情况,当rust对动态大小类型引用时(slice,str等),除了会存储数据地址外,还会额外使用一个机器字长存储数据的长度。这种引用也称为胖指针(因为存储了附加信息)。</p><p>切片引用使用了两个字长存储胖指针,大小确定,所以可以存在栈上。我们一般使用的都是切片引用</p><p><img src="https://s2.loli.net/2023/09/13/xgB2Wtr1uX5Hizf.png" alt="切片和切片引用.png"></p><h3 id="字符串-String-str-str"><a href="#字符串-String-str-str" class="headerlink" title="字符串 String str &str"></a>字符串 String str &str</h3><p>首先明确,字符串切片和字符串切片引用是不同的</p><p><img src="https://s2.loli.net/2023/09/13/jRKtEHO6uM9UoNg.png" alt="字符串和字符串切片.png"></p><h4 id="String"><a href="#String" class="headerlink" title="String"></a>String</h4><p><code>let s1 = String::from("hello");</code><br>String底层使用Vec实现的,所以和vec内存布局相同,和vec区别,字符串必须是UTF8编码。</p><h4 id="str-字符串切片"><a href="#str-字符串切片" class="headerlink" title="str 字符串切片"></a>str 字符串切片</h4><p><code>let s2: &str = "hello";</code><br>字符串切片是存在程序的只读区的(未指定具体在那个段,可能在代码段),这点和其他语言类似,有着’static生命周期</p><h4 id="str-字符串切片引用"><a href="#str-字符串切片引用" class="headerlink" title="&str 字符串切片引用"></a>&str 字符串切片引用</h4><p>跟切片引用类似,字符串切片的引用也是一个胖指针,存储数据的起始内存地址和数据长度</p><figure class="highlight rust"><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">let</span> <span class="variable">s1</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>);</span><br><span class="line"><span class="comment">// 切片操作,返回字符切片</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里会编译错误,因为切片类型是不知道大小的</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">s2</span>: Str = s1[<span class="number">1</span>..<span class="number">3</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以使用引用</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">s3</span>: &Str = &s1[<span class="number">1</span>..<span class="number">3</span>];</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="结构体-struct"><a href="#结构体-struct" class="headerlink" title="结构体 struct"></a>结构体 struct</h3><p>rust有多种结构体</p><h4 id="命名字段的结构体"><a href="#命名字段的结构体" class="headerlink" title="命名字段的结构体"></a>命名字段的结构体</h4><p>结构体在栈上的布局相当与将他的各个成员相邻排列,也会有内存对齐</p><p><img src="https://s2.loli.net/2023/09/13/Gbcu9rPxKLS52MW.png" alt="命名字段结构体.png"></p><h4 id="元组结构体"><a href="#元组结构体" class="headerlink" title="元组结构体"></a>元组结构体</h4><p>类似元组布局</p><h4 id="单元结构体"><a href="#单元结构体" class="headerlink" title="单元结构体"></a>单元结构体</h4><p>单元结构体没有任何数据,编译器不会为它分配任何数据</p><h3 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h3><p>rust有多种枚举表示方式</p><h4 id="c风格枚举"><a href="#c风格枚举" class="headerlink" title="c风格枚举"></a>c风格枚举</h4><figure class="highlight rust"><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="keyword">enum</span> <span class="title class_">HTTPStatus</span> {</span><br><span class="line"> OK,</span><br><span class="line"> NotFound,</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">enum</span> <span class="title class_">HTTPStatus</span> {</span><br><span class="line"> OK = <span class="number">200</span>,</span><br><span class="line"> NotFound = <span class="number">404</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2023/09/13/QWr1dtZKeV483hu.png" alt="C风格枚举.png"></p><p>在内存中它们被存储为从0开始的整数标记,rust会选择能够存储该枚举类型的最大枚举变体的最短整形。<br>在这里枚举最大值为1,所以只需要1个字节就可以,指定枚举整数值后,就至少需要2个字节来存</p><h4 id="rust风格"><a href="#rust风格" class="headerlink" title="rust风格"></a>rust风格</h4><figure class="highlight rust"><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="keyword">enum</span> <span class="title class_">Data</span>{</span><br><span class="line"> Empty,</span><br><span class="line"> <span class="title function_ invoke__">Number</span>(<span class="type">i32</span>),</span><br><span class="line"> <span class="title function_ invoke__">Array</span>(<span class="type">Vec</span><<span class="type">i32</span>>),</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2023/09/13/v9NGSkgpzEPuht8.png" alt="枚举占用内存.png"></p><p>我们首先分析<code>Array(Vec<i32>)</code>这个变体,这个变体的位置是第三个,所以枚举整数标记是2,需要1个字节,然后还包括了一个<code>Vec</code>,我们之前了解过Vec内存在栈上是3个usize,在64位系统上需要24个字节,加上内存对齐,整个<code>Array(Vec<i32>)</code>需要32个字节<br><code>Number(i32)</code>它存储一个整数,然后再加一个<code>i32</code>,一共5个字节,因为,所有枚举变体都具有相同的大小,编译器会自动填充到32字节。<br><code>Empty</code>同样,只需要一个整数标记1字节,但是也会填充到32字节。<br>枚举的大小由其最大的变体决定。</p><p>减少枚举占用内存的方法,减少最大的变体内存<br>例如将<code>Array(Vec<i32>)</code>变为<code>Array(Box<Vec<i32>>)</code>,新的内存只需要16个字节。枚举整体内存也减少一半</p><p><img src="https://s2.loli.net/2023/09/13/tQkGwz3mpfCiDqZ.png" alt="减少枚举内存.png"></p><h4 id="Option"><a href="#Option" class="headerlink" title="Option"></a>Option</h4><p><img src="https://s2.loli.net/2023/09/13/icE4xzFGY7nbSPB.png" alt="Option.png"><br>正常<code>Option</code>和其他枚举是同样的内存结构,当Option中的值是一个Box或者其他智能指针。因为任何智能指针的值都不能是0.所以rust编译器使用一个<code>usize</code>表示<code>Option<Box<i32>></code>,不再使用整数标记。使用指针为0来代表<code>None</code>,不为0就是<code>Some</code>.</p><p>更优化的内存<br><img src="https://s2.loli.net/2023/09/13/HArdztwq9GSYvi1.png" alt="优化Option.png"></p><h3 id="copy-和-move"><a href="#copy-和-move" class="headerlink" title="copy 和 move"></a>copy 和 move</h3><h4 id="copy"><a href="#copy" class="headerlink" title="copy"></a>copy</h4><p>对于只在栈上的数据,赋值操作rust使用copy,按位拷贝</p><h4 id="move"><a href="#move" class="headerlink" title="move"></a>move</h4><figure class="highlight rust"><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="keyword">let</span> <span class="variable">v</span>:<span class="type">Vec</span><<span class="type">String</span>> = <span class="built_in">vec!</span>[</span><br><span class="line"> <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> <span class="string">"world"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line">];</span><br><span class="line"><span class="keyword">let</span> <span class="variable">v2</span> = v;</span><br></pre></td></tr></table></figure><p>对于像String,Vec这种数据结构,一部分存在栈上,数据存在堆上的,赋值操作使用的是move,只会拷贝栈上的部分,原来变量内的指针,和新变量的指针都指向同一个地址,但是rust有所有权机制,如果同一内存释放两次会出问题,所以,move操作后此时变量的所有权发生了转移,原来的变量已经失效了,就不会存在重复释放问题。</p><p>我们也可以进行深拷贝在需要的时候,使用clone方法。</p><p>有时我们需要多个变量拥有同一份数据的,可以使用<code>Rc</code>引用计数</p><p><img src="https://s2.loli.net/2023/09/13/CHE4aVlAcponUxI.png" alt="move.png"></p><h3 id="特征对象"><a href="#特征对象" class="headerlink" title="特征对象"></a>特征对象</h3><p>可以有多种方法,把具体对象转换成特征对象,例如赋值,在内存中,特征对象<code>&mut dyn Write</code>是一个胖指针,它由两个普通指针组成。</p><figure class="highlight rust"><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="keyword">use</span> std::io::Write;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">buffer</span>: <span class="type">Vec</span><<span class="type">u8</span>> = <span class="built_in">vec!</span>[];</span><br><span class="line"><span class="comment">// 引用形式的特征对象</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">w</span>: &<span class="keyword">mut</span> <span class="keyword">dyn</span> Write = &<span class="keyword">mut</span> buffer</span><br><span class="line"><span class="comment">// 智能指针形式特征对象</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">w2</span>: <span class="type">Box</span><<span class="keyword">dyn</span> Write> = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(buffer);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>特征对象的第一个指针指向值,在这里就是Vec,第二个指针指向一个虚表vtable,这个表有值类型的信息,vtable在编译时生成,由相同类型的所有对象共享,虚表包含一系列函数指针,指向特征的方法,当调用特征方法时,会自动使用虚表。<br><code>dyn Write</code> 也是动态大小类型,和切片一样,所以我们总是用他的引用。<br>rust可以将普通引用变为trait对象,同时也能将智能指针(Box,Rc等)变为trait对象。这种情况下,内存结构就变成了胖指针。</p><p>其实无论是引用还是智能指针,rust将他们转为特征对象时,rust都知道他们的类型,只是给它们添加了一个vtable地址,就将常规指针转为胖指针</p><p><img src="https://s2.loli.net/2023/09/13/2WlVM5kzc3QsL1B.png" alt="特征对象.png"></p><h3 id="函数指针"><a href="#函数指针" class="headerlink" title="函数指针"></a>函数指针</h3><figure class="highlight rust"><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><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>(){</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">f</span>: <span class="title function_ invoke__">fn</span>()<span class="punctuation">-></span><span class="type">bool</span> = test_func; </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">test_func</span>()<span class="punctuation">-></span><span class="type">bool</span>{</span><br><span class="line"> <span class="literal">true</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个函数指针只需要一个usize来存储函数的机器码地址</p><h3 id="闭包-closure"><a href="#闭包-closure" class="headerlink" title="闭包 closure"></a>闭包 closure</h3><p>rust没有具体的闭包类型,通过定义了三个trait来实现闭包<br><code>Fn,FnOnce,FnMut</code>,rust把闭包转换成了结构体。所以内存布局就是闭包捕获的变量。</p><h4 id="fnOnce"><a href="#fnOnce" class="headerlink" title="fnOnce"></a>fnOnce</h4><figure class="highlight rust"><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="keyword">fn</span> <span class="title function_">main</span>(){</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">c</span> = <span class="title function_ invoke__">crate_closure</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">crate_closure</span>() <span class="punctuation">-></span> <span class="keyword">impl</span> <span class="title class_">FnOnce</span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">name</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>);</span><br><span class="line"> || {</span><br><span class="line"> <span class="title function_ invoke__">drop</span>(name);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>FnOnce</code> 只是一个特征,它定义了对象的行为和方法。<br>rust内部使用结构体来表示闭包,rust会根据使用的变量创建一个适当的结构体,并为该结构体实现最合适的特征</p><figure class="highlight rust"><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="comment">// rust 内部结构体表示闭包的简略代码</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyClosure</span>{</span><br><span class="line"> name: <span class="type">String</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">FnOnce</span> <span class="keyword">for</span> <span class="title class_">MyClosure</span> {</span><br><span class="line"> <span class="comment">// FnOnce 必须实现的方法,因为只调用一次,所以使用self作为参数</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">call_once</span>(<span class="keyword">self</span>){</span><br><span class="line"> <span class="title function_ invoke__">drop</span>(<span class="keyword">self</span>.name) </span><br><span class="line">} </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2023/09/13/dBOFmwTQJe6GXoA.png" alt="FnOnce.png"></p><h4 id="FnMut"><a href="#FnMut" class="headerlink" title="FnMut"></a>FnMut</h4><figure class="highlight rust"><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><br><span class="line"><span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">i</span>: <span class="type">i32</span> = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// f 必须时mut的,因为闭包的实现里需要&mut self作为参数</span></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">f</span> = ||{</span><br><span class="line"> i+=<span class="number">1</span>;</span><br><span class="line"> };</span><br><span class="line"><span class="title function_ invoke__">f</span>();</span><br><span class="line"><span class="title function_ invoke__">f</span>();</span><br><span class="line"></span><br><span class="line"><span class="built_in">println!</span>(<span class="string">"{}"</span>,i) <span class="comment">// 2</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight rust"><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="comment">// rust 内部结构体表示闭包的简略代码</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyClosure</span>{</span><br><span class="line"> i: &<span class="keyword">mut</span> <span class="type">i32</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">FnMut</span> <span class="keyword">for</span> <span class="title class_">MyClosure</span> {</span><br><span class="line"> <span class="comment">// FnMut 必须实现的方法,因为要修改值,所以使用&mut self作为参数</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">call_mut</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>){</span><br><span class="line"> *<span class="keyword">self</span>.i +=<span class="number">1</span>; </span><br><span class="line">} </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2023/09/13/7KWRaeYzCd8qcLS.png" alt="FnMut.png"></p><h4 id="Fn"><a href="#Fn" class="headerlink" title="Fn"></a>Fn</h4><p>与上边不同的是参数</p><figure class="highlight rust"><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">fn</span> <span class="title function_">crate_closure</span>() <span class="punctuation">-></span> <span class="keyword">impl</span> <span class="title class_">Fn</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">msg</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>);</span><br><span class="line"> <span class="keyword">move</span> || {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>,msg);</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 rust"><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="comment">// rust 内部结构体表示闭包的简略代码</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyClosure</span>{</span><br><span class="line"> i: <span class="type">String</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Fn</span> <span class="keyword">for</span> <span class="title class_">MyClosure</span> {</span><br><span class="line"> <span class="comment">// Fn 必须实现的方法,不可变借用,所以使用&self作为参数</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">call</span>(&<span class="keyword">self</span>){</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>,msg);</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><p><img src="https://s2.loli.net/2023/09/13/p18q5V9sOASrdvY.png" alt="Fn.png"></p>]]></content>
<summary type="html"><h2 id="为什么要了解内存布局"><a href="#为什么要了解内存布局" class="headerlink"</summary>
<category term="Rust基础" scheme="https://lixiang365.github.io/categories/Rust%E5%9F%BA%E7%A1%80/"/>
<category term="Rust专题" scheme="https://lixiang365.github.io/categories/Rust%E4%B8%93%E9%A2%98/"/>
<category term="Rust" scheme="https://lixiang365.github.io/tags/Rust/"/>
<category term="Rust内存布局" scheme="https://lixiang365.github.io/tags/Rust%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80/"/>
</entry>
</feed>