文章目录

| 概念 | 解释 |
|---|---|
| 传输层 TCP 复用 | 发送方的部分应用进程的报文在传输层使用 TCP 协议进行封装 |
| 传输层 UDP 复用 | 发送方的部分应用进程的报文在传输层使用 UDP 协议进行封装 |
| 网际层 IP 复用 | 发送方不同协议的数据都可以封装成 IP 数据报(IP 协议字段,TCP 为 6,UDP 为 17) |
| 网际层 IP 分用 | 接收方的网际层在去除首部后把数据交付给上层相应的协议(TCP 或 UDP) |
| 传输层 TCP 分用 | 接收方的传输层使用 TCP 协议去除首部后把数据交付给目的应用进程 |
| 传输层 UDP 分用 | 接收方的传输层使用 UDP 协议去除首部后把数据交付给目的应用进程 |
| 端口号 | 类型 | 解释 |
|---|---|---|
| 0 ~ 1023 | 熟知端口号 | 由 IANA 分配给 TCP/IP 体系结构应用层中最重要的一些应用协议 |
| 1024 ~ 49151 | 登记端口号 | 为没有熟知端口号的应用程序使用。要使用这类端口号,必须在 IANA 进行登记,以防止重复 |
| 49152 ~ 65535 | 短暂端口号 | 仅在客户端使用,由客户进程在运行时动态选择,通信结束后会被系统收回,以便给其他客户进程使用 |
| FTP | SMTP | DNS | DHCP | HTTP | BGP | HTTPS | RIP |
|---|---|---|---|---|---|---|---|
| 21/20 | 25 | 53 | 67/68 | 80 | 179 | 443 | 520 |
【注】OSPF 不使用传输层协议,所以没有对应的端口号。但 OSPF 的 IP 协议字段值为 89。

【注】重点关注 seq、ack、ACK、SYN、FIN 这几个位。
假设有客户机 A 和服务器 B 已经建立了 TCP 连接。
(1)现 A 向 B 发送 TCP 确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=201, ack=800, ACK=1 | 100B(seq=201~300) |
seq=201:指 A 的数据部分是从序号 201 开始的,数据部分总长 100B,因此数据部分的最后一个序号是 300。ack=800:指 A 期望 B 发来的下一个报文段的数据部分序号是从 800 开始的。ACK=1:连接建立后所有传送的报文段都必须置 1。(2)然后 B 向 A 发送 TCP 确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=800, ack=301, ACK=1 | 200B(seq=800~999) |
seq=800:指 B 的数据部分是从序号 800 开始的,数据部分总长 200B,因此数据部分的最后一个序号是 999。ack=301:指 B 期望 A 发来的下一个报文段的数据部分序号是从 301 开始的,同时确认 A 的报文段(seq=201~300)已收到。ACK=1:连接建立后所有传送的报文段都必须置 1。(3)若 A 向 B 发送 3 个 TCP 确认报文段,其中第 2 个报文段丢失:
| 首部 | 数据部分 |
|---|---|
| seq=201, ack=x, ACK=1 | 100B(seq=201~300) |
| seq=301, ack=x, ACK=1 | 100B(seq=301~400)(丢失) |
| seq=401, ack=x, ACK=1 | 100B(seq=401~500) |
则 B 仅正确接收第 1 个和第 3 个报文段。此时 B 向 A 发送的 TCP 报文段,应对已正确接收且按序到达的最后一个 TCP 报文段确认,这就是累积确认(更详细的解释见第 4 节内容):
| 首部 | 数据部分 |
|---|---|
| seq=x, ack=301, ACK=1 | 200B(seq=x~x+199) |
TCP 连接有三个阶段:连接建立、数据传送(上面已述)、连接释放。
假设有客户机 A 和服务器 B 准备建立 TCP 连接。

服务器 B 进程处于 LISTEN(监听)状态,等待客户机 A 的连接请求。
客户机 A 向服务器 B 发送 TCP 连接请求报文段:
| 首部 | 数据部分 |
|---|---|
| seq=x, ack=0, SYN=1, ACK=0 | SYN 报文段不能携带数据 |
seq=x:随机选取一个初始序号 x,作为客户机 A 的 TCP 报文段数据部分的初始序号。ack=0:由于 ACK=0,所以 ack 字段无效。SYN=1:TCP 连接请求报文段中的同步标志位 SYN 的值必须设置为 1。ACK=0:当 SYN=1,ACK=0 时,表明这是一个 TCP 连接请求报文段。此时,客户机 A 进入 SYN-SENT(同步已发送)状态。
【注】TCP 规定同步标志位 SYN 被设置为 1 的报文段(例如 TCP 连接请求报文段和 TCP 连接请求确认报文段)不能携带数据,但要消耗掉一个序号。
服务器 B 收到 TCP 连接请求报文段后,向客户机 A 发送 TCP 连接请求确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=y, ack=x+1, SYN=1, ACK=1 | SYN 报文段不能携带数据 |
seq=y:随机选取一个初始序号 y,作为服务器 B 的 TCP 报文段数据部分的初始序号。ack=x+1:服务器 B 期望收到客户机 A 发来的下一个报文段的数据部分序号是从 x+1 开始的,同时确认客户机 A 的报文段(seq=x)已收到。SYN=1:TCP 连接请求确认报文段中的同步标志位 SYN 的值必须设置为 1。ACK=1:当 SYN=1,ACK=1 时,表明这是一个 TCP 连接请求确认报文段。此时,服务器 B 进入 SYN-RCVD(同步已接收)状态。
客户机 A 收到 TCP 连接请求确认报文段后,向服务器 B 发送 TCP 确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=x+1, ack=y+1, SYN=0, ACK=1 | TCP 确认报文段可以携带数据 |
seq=x+1:客户机 A 的 TCP 报文段数据部分的序号为 x+1。ack=y+1:客户机 A 期望收到服务器 B 发来的下一个报文段的数据部分序号是从 y+1 开始的,同时确认服务器 B 的报文段(seq=y)已收到。SYN=0:TCP 确认报文段中的同步标志位 SYN 的值必须设置为 0。ACK=1:当 SYN=0,ACK=1 时,表明这是一个普通的 TCP 确认报文段。此时,客户机 A 进入 ESTABLISHED(连接已建立)状态。
【注】TCP 规定普通的 TCP 确认报文段可以携带数据,但如果不携带数据,则不消耗序号。如果该报文段不携带数据,则客户机 A 要发送的下一个数据报文段的序号仍为 x+1。
服务器 B 收到 TCP 确认报文段后,也进入 ESTABLISHED(连接已建立)状态。至此 TCP 连接已建立。
【总结】TCP 建立连接的过程:
客户机:“我有话要跟你讲,不知可不可以?”
服务器:“可以,你讲吧!”
客户机:“好的!blablabla”
假设有客户机 A 和服务器 B 准备释放 TCP 连接。

客户机 A 进程和服务器 B 进程处于 ESTABLISHED(连接已建立)状态。
客户机 A 向服务器 B 发送 TCP 连接释放报文段:
| 首部 | 数据部分 |
|---|---|
| seq=u, ack=v, FIN=1, ACK=1 | FIN 报文段可携带也可不携带数据 |
seq=u:客户机 A 的 TCP 报文段数据部分的序号为 u,它等于客户机 A 之前已传送数据部分的最后一个字节的序号加 1。ack=v:ack 字段的值为 v,它等于客户机 A 之前已收到数据的最后一个字节的序号加 1。FIN=1:TCP 连接释放报文段中的同步标志位 SYN 的值必须设置为 1。ACK=1:当 FIN=1,ACK=1 时,表明这是一个 TCP 连接释放报文段。【注】TCP 规定,终止标志位 FIN 等于 1 的 TCP 报文段即使不携带数据,也要消耗掉一个序号。
此时,客户机 A 进入 FIN-WAIT-1(终止等待 1)状态。
服务器 B 收到 TCP 连接释放报文段后,向客户机 A 发送 TCP 确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=v, ack=u+1, FIN=0, ACK=1 | TCP 确认报文段可以携带数据 |
seq=v:服务器 B 的 TCP 报文段数据部分的序号为 v。ack=u+1:服务器 B 期望收到客户机 A 发来的下一个报文段的数据部分序号是从 u+1 开始的,同时确认客户机 A 的连接释放报文段(seq=u)已收到。FIN=0:TCP 确认报文段中的同步标志位 SYN 的值必须设置为 0。ACK=1:当 FIN=0,ACK=1 时,表明这是一个普通的 TCP 确认报文段。此时,服务器 B 进入 CLOSE-WAIT(关闭等待)状态。客户机 A 已经没有数据要发送了。但服务器 B 如果还有数据要发送,客户机 A 仍要接收,即从服务器 B 到客户机 A 这个方向的连接并未关闭,这称为半关闭状态,该状态可能会持续一段时间。
服务器 B 向客户机 A 发送若干个 TCP 确认报文段,直到发送最后一次报文段,即 TCP 连接释放报文段:
| 首部 | 数据部分 |
|---|---|
| seq=w, ack=u+1, FIN=1, ACK=1 | FIN 报文段可携带也可不携带数据 |
seq=w:经过发送若干个 TCP 确认报文段,服务器 B 的 TCP 报文段数据部分的序号变为 w。ack=u+1:服务器 B 期望收到客户机 A 发来的下一个报文段的数据部分序号是从 u+1 开始的,同时重复确认客户机 A 的连接释放报文段(seq=u)已收到。FIN=1:TCP 连接释放报文段中的同步标志位 SYN 的值必须设置为 1。ACK=1:当 FIN=1,ACK=1 时,表明这是一个 TCP 连接释放报文段。此时,服务器 B 进入 LAST-ACK(最后确认)状态。
客户机 A 收到 TCP 连接释放报文段后,向服务器 B 发送 TCP 确认报文段:
| 首部 | 数据部分 |
|---|---|
| seq=u+1, ack=w+1, FIN=0, ACK=1 | TCP 确认报文段可以携带数据 |
seq=u+1:客户机 A 的 TCP 报文段数据部分的序号为 u+1。ack=w+1:客户机 A 期望收到服务器 B 发来的下一个报文段的数据部分序号是从 w+1 开始的,同时确认服务器 B 的连接释放报文段(seq=w)已收到。FIN=0:TCP 确认报文段中的同步标志位 SYN 的值必须设置为 0。ACK=1:当 FIN=0,ACK=1 时,表明这是一个普通的 TCP 确认报文段。此时,客户机 A 进入 TIME-WAIT(时间等待)状态,服务器 B 收到 TCP 确认报文段后进入CLOSED(连接关闭)状态。但是 TCP 连接仍未释放,必须经过 2MSL(最长报文段寿命,Maximum Segment Lifetime)的时间后,客户机 A 才能进入 CLOSED(连接关闭)状态。
【总结】TCP 释放连接的过程:
客户机:“我准备走了。”
服务器:“等一下,我还有一些话没说完。blablabla”
服务器:“blablabla”
服务器:“blablabla,我说完了,你可以走了。”
客户机:“好的!那我走了!”
TCP 协议提供一种基于滑动窗口协议的流量控制机制。TCP 使用了校验、序号、确认和重传等机制来达到可靠传输的目的,在下面的过程中将体现这一特点。
假设有发送方 A 和接收方 B 已建立 TCP 连接,不考虑 TCP 的拥塞控制。再假定 A 只给 B 发送数据,B 对 A 进行流量控制。
在 A 和 B 建立 TCP 连接后,B 告诉 A:“我的接收窗口 rwnd=500”,因此 A 将自己的发送窗口 swnd 也设置为 500。窗口大小是以最大报文段 MSS 为单位的,一般将 MSS 设置为 1B,所以 “swnd=500”意思是发送窗口的大小为 500B。
A 的发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | √ | √ | √ | √ | |||
| A 已发送 | ||||||||
| B 已确认 | ||||||||
| B 已收到 |
A 发送数据:seq=1,seq=101,seq=201,seq=301,发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | √ | √ | √ | √ | |||
| A 已发送 | √ | √ | √ | √ | ||||
| B 已确认 | ||||||||
| B 已收到 |
但是很不巧,seq=201 丢失了,B 只收到 seq=1,seq=101,seq=301。其中 seq=301 是失序报文段,但 B 不会丢弃它。TCP 作如下规定:每接收到一个失序报文段,就要发送一次冗余 ACK,指明下一个期待的报文数据序号。
很明显,现在 B 期望收到 seq=201 的报文,因此 seq=301 是不会被确认的,这被称为累积确认。现在 B 的接收窗口已经接受了 200B 的数据,还有 300B 未接收,于是发送 ACK=1,ack=201,rwnd=300 的冗余报文给 A。
此时发送方 A 的 swnd 调整为 300,swnd 滑动到首个未确认的数据位置。发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | √ | √ | |||||
| A 已发送 | √ | √ | √ | √ | ||||
| B 已确认 | √ | √ | ||||||
| B 已收到 | √ | √ | √ |
发送方 A 继续发送 seq=401,对于接收方 B 来说,seq=401 依然是失序报文段,继续发送 ACK=1,ack=201,rwnd=300 的冗余报文给 A。此时发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | √ | √ | |||||
| A 已发送 | √ | √ | √ | √ | √ | |||
| B 已确认 | √ | √ | ||||||
| B 已收到 | √ | √ | √ | √ |
发送方 A 也没有对 seq=201 坐视不理,实质上从发出 seq=201 开始,重传计时器也开始计数了,A 发现计时器超时但仍未收到来自 B 的确认,于是进行超时重传。
【注】其实,当发送方 A 连续收到三个冗余 ACK 后,就可以立即重新发送 seq=201,而不必等待计时器超时,这被称为快重传算法,在下一节“拥塞控制”将提到。
接收方 B 收到 seq=201 后,由于网络流量原因,接收窗口需减小到 100B,于是返回 ACK=1,ack=501,rwnd=100 的报文。此时发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | |||||||
| A 已发送 | √ | √ | √ | √ | √ | |||
| B 已确认 | √ | √ | √ | √ | √ | |||
| B 已收到 | √ | √ | √ | √ | √ |
发送方 A 发送 seq=501,此时发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | √ | |||||||
| A 已发送 | √ | √ | √ | √ | √ | √ | ||
| B 已确认 | √ | √ | √ | √ | √ | |||
| B 已收到 | √ | √ | √ | √ | √ |
接收方 B 收到 seq=501 后,返回 ACK=1,ack=601,rwnd=0 的报文,表示 B 不再接收任何数据。此时发送缓冲区情况如下:
| 数据 | 1~100B | 101~200B | 201~300B | 301~400B | 401~500B | 501~600B | 601~700B | 701~800B |
|---|---|---|---|---|---|---|---|---|
| swnd | ||||||||
| A 已发送 | √ | √ | √ | √ | √ | √ | ||
| B 已确认 | √ | √ | √ | √ | √ | √ | ||
| B 已收到 | √ | √ | √ | √ | √ | √ |
由于发送方 A 的 swnd=0,因此 A 不能发送任何数据了。接收方 B 必须发送一个非零窗口通知,以告知发送方 A:“你可以开始发送大小为 xxx 的窗口了。”所以,A 一直在等待这个通知,只要收到通知,将恢复传输过程。
然而可能会出现这样一种情况:B 发出的通知丢失,A 只能无限等待下去。为了打破由于非零窗口通知报文段丢失而引起的双方互相等待的死锁局面,TCP 为每一个连接都设有一个持续计时器:
【注】TCP 规定:即使接收窗口值为 0,也必须接受零窗口探测报文段、确认报文段以及携带有紧急数据的报文段。
设置一个慢开始门限阈值 ssthresh,初始值为 16。根据发送方的拥塞窗口 cwnd 的大小执行不同的算法:
慢开始算法:从 cwnd=1 开始,每经过一个传输轮次(即往返时延 RTT)指数规律增长,cwnd=2,cwnd=4,cwnd=8,当 cwnd = ssthresh = 16 时,改用拥塞避免算法。
拥塞避免算法:每经过一个传输轮次(即往返时延 RTT),cwnd 加 1,即线性规律增长。只要发送方判断网络出现拥塞,则令 ssthresh = cwnd / 2。然后令 cwnd=1,重新执行慢算法。
【注】传输轮次和往返时延的区别:
- 传输轮次:发送一批报文段并收到它们的确认的时间。
- 往返时延 RTT:开始发送一批报文段到开始发送下一批报文段的时间。
例如有以下传输过程:
| 传输轮次 | cwnd | 发送的 TCP 数据部分的序号 | 算法 | 备注 |
|---|---|---|---|---|
| 1 | 1 | 0 号 | 慢开始 | 初始时,cwnd = 1,ssthresh = 16 |
| 2 | 2 | 1 ~ 2 号 | 慢开始 | |
| 3 | 4 | 3 ~ 6 号 | 慢开始 | |
| 4 | 8 | 7 ~ 14 号 | 慢开始 | |
| 5 | 16 | 15 ~ 30 号 | 拥塞避免 | cwnd = ssthresh = 16 |
| 6 | 17 | 31 ~ 47 号 | 拥塞避免 | |
| 7 | 18 | 48 ~ 64 号 | 拥塞避免 | |
| … | … | … | ||
| 13 | 24 | 171 ~ 194 号 | 拥塞避免 | 重传计时器发生超时,说明网络拥塞,ssthresh = cwnd/2 = 12 |
| 14 | 1 | 195 号 | 慢开始 | cwnd 重新设置为 1 |
| 15 | 2 | 196 ~ 197 号 | 慢开始 | |
| 16 | 4 | 198 ~ 201 号 | 慢开始 | |
| 17 | 8 | 202 ~ 209 号 | 慢开始 | |
| 18 | 12 | 210 ~ 221 号 | 拥塞避免 | cwnd = 16 > ssthresh = 12,改用拥塞避免算法 |
| 19 | 13 | 222 ~ 234 号 | 拥塞避免 | |
| … | … | … |
慢开始和拥塞避免算法的实现过程如下图:

【注】慢开始和拥塞避免的含义:
- “慢开始”是指一开始向网络注入的报文段少,而并不是指拥塞窗口 cwnd 的值增长速度慢。
- “拥塞避免”也并非指完全能够避免拥塞,而是指在拥塞避免阶段将 cwnd 值控制为按线性规律增长,使网络比较不容易出现拥塞。
快重传和快恢复是对慢开始和拥塞避免算法的改进。根据发送方的拥塞窗口 cwnd 的大小执行不同的算法:
快重传算法:当发送方连续接收到三个冗余 ACK 报文时,直接重传对方尚未收到的报文段,而不必等待该报文段的重传计时器超时。
快恢复算法:当发送方连续接收到三个冗余 ACK 报文时,令 ssthresh = cwnd / 2,然后 cwnd 从该 ssthresh 开始线性增加。
例如有以下传输过程:
| 传输轮次 | cwnd | 发送的 TCP 数据部分的序号 | 算法 | 备注 |
|---|---|---|---|---|
| 1 | 1 | 0 号 | 慢开始 | 初始时,cwnd = 1,ssthresh = 16 |
| 2 | 2 | 1 ~ 2 号 | 慢开始 | |
| 3 | 4 | 3 ~ 6 号 | 慢开始 | |
| 4 | 8 | 7 ~ 14 号 | 慢开始 | |
| 5 | 16 | 15 ~ 30 号 | 拥塞避免 | cwnd = ssthresh = 16 |
| 6 | 17 | 31 ~ 47 号 | 拥塞避免 | |
| 7 | 18 | 48 ~ 64 号 | 拥塞避免 | |
| … | … | … | ||
| 13 | 24 | 171 ~ 194 号 | 拥塞避免 | 发送方连续收到三个冗余 ACK 报文,说明网络拥塞,ssthresh = cwnd/2 = 12 |
| 14 | 12 | 195 ~ 206 号 | 拥塞避免 | cwnd 设置为 ssthresh = 12 |
| 15 | 13 | 207 ~ 219 号 | 拥塞避免 | |
| 16 | 14 | 220 ~ 233 号 | 拥塞避免 | |
| 17 | 15 | 234 ~ 248 号 | 拥塞避免 | |
| 18 | 16 | 249 ~ 264 号 | 拥塞避免 | |
| 19 | 17 | 265 ~ 281 号 | 拥塞避免 | |
| … | … | … |
快重传和快恢复算法的实现过程如下图:

需要注意,发送方的发送窗口由接收方的接收窗口和发送方的拥塞窗口两者的最小值所决定,即swnd = min(rwnd, cwnd)。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in
给定两个大小相等的数组,如何找到不考虑位置的匹配元素的数量?例如:[0,0,5]和[0,5,5]将返回2的匹配项,因为有一个0和一个5共同;[1,0,0,3]和[0,0,1,4]将返回3的匹配项,因为0有两场,1有一场;[1,2,2,3]和[1,2,3,4]将返回3的匹配项。我尝试了很多想法,但它们都变得相当粗糙和令人费解。我猜想有一些不错的Ruby习惯用法,或者可能是一个正则表达式,可以很好地回答这个解决方案。 最佳答案 您可以使用count完成它:a.count{|e|index=b.index(e)andb.delete_at
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案
我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT