前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Bittorrent 协议浅析(七)uTorrent 传输、穿透拓展和 UDP Tracker

Bittorrent 协议浅析(七)uTorrent 传输、穿透拓展和 UDP Tracker

原创
作者头像
青橙.
修改于 2023-10-12 10:40:54
修改于 2023-10-12 10:40:54
1.5K0
举报
文章被收录于专栏:橙、橙、

0. 回顾

前序文章:

- Bittorrent 协议浅析(一)元数据文件 https://cloud.tencent.com/developer/article/2332701

- Bittorrent 协议浅析(二)Tracker 和 对等节点https://cloud.tencent.com/developer/article/2333043

- Bittorrent 协议浅析(三)对等数据传输实例https://cloud.tencent.com/developer/article/2333677

- Bittorrent 协议浅析(四)分布式哈希 https://cloud.tencent.com/developer/article/2334440

- Bittorrent 协议浅析(五)拓展协议 及 元数据传输拓展 https://cloud.tencent.com/developer/article/2334776

前文内容回顾:

BitTorrent 是一种用于分发文件的协议,元数据文件采用 bencode 编码,分片进行 SHA-1 哈希计算比对,并介绍元数据文件数据结构,通过 HTTP 请求由 Trakcer 交换节点信息,节点直接直接进行通讯。

拓展协议中的元数据传输拓展可在节点之间传输元数据,PEX 拓展允许节点交换节点信息,DHT 可通过 KRPC 根据信息哈希获取节点,本地服务发现基于组播,在私有种子中这些内容均须禁用。

截至目前,所阐述和涉及的内容几乎都是基于 TCP 的 BitTorrent 实现。在部分网络环境下,通过 TCP 建立连接具有一定的局限性,过多的 TCP 连接会不公平的消耗网络资源,基于 UDP 的 uTorrent 和 穿透拓展能很好的解决这方面的问题,同时为位于 NAT 或防火墙后的下载器提供连接可能。

uTorrent

结合Bittorrent 协议浅析(八)uTP 数据包分析、超级种子获得最佳阅读体验。

uTorrent 传输协议(uTorrent transport protocol,uTP) 是一种建立在 UDP 之上的传输协议。uTP 动态调整数据包大小,通常传输速越快,使用的数据包越大。通常最小到每个数据包 150 字节。小数据包不会堵塞缓慢的上行链路但标头的网络开销较大。

数据格式

uTorrent 的数据格式如下:

代码语言:txt
AI代码解释
复制
0       4       8               16              24              32
+-------+-------+---------------+---------------+---------------+
| type  | ver   | extension     | connection_id                 |
+-------+-------+---------------+---------------+---------------+
| timestamp_microseconds                                        |
+---------------+---------------+---------------+---------------+
| timestamp_difference_microseconds                             |
+---------------+---------------+---------------+---------------+
| wnd_size                                                      |
+---------------+---------------+---------------+---------------+
| seq_nr                        | ack_nr                        |
+---------------+---------------+---------------+---------------+

其中,

类型(type)

  • ST_DATA(0):ST_DATA数据包带有数据负载;
  • ST_FIN(1):完成连接。这是最后一个数据包,用于关闭连接,类似于TCP的FIN标志。连接应将此序列号记录为 eof_pkt,以继续等待可能丢失并无序到达的数据包;
  • ST_STATE(2):状态数据包。用于传输不带任何数据的ACK。不会增加seq_nr;
  • ST_RESET(3):强制终止连接。类似于TCP的RST标志。
  • ST_SYN(4):连接SYN。类似于TCP的SYN标志,此数据包启动连接。序列号初始化为1。所有后续数据包(除了重发 ST_SYN)都使用应使用连接 ID 和连接 ID+ 1发送。

版本号(version)

协议的版本号。当前为1。

连接 ID(connection_id):

用于标识属于同一连接的随机内容。连接发起方生成 ID,而响应数据使用 ID + 1。

时间戳(timestamp_microseconds):

该数据包发送时的时间戳。时间戳的分辨率越高越好。

时间戳差异(timestamp_difference_microseconds):

在上次接收数据包时,本地时间与上次接收数据包的时间戳之间的差异。

窗口大小(wnd_size):

已就绪的接收窗口大小,以字节为单位。窗口大小表示当前正在传输但尚未被确认的字节数。

扩展字段(extension):

扩展链中第一个扩展的类型。0表示没有扩展。(目前只有一个扩展,即选择性确认。)

选择性确认(Selective ACK):

选择性确认是一种可以选择性地确认非顺序接收的数据包的扩展。其有效载荷是一个至少32位的位掩码,以32位的倍数表示。每个位代表发送窗口中的一个数据包。多余位忽略。置 1 的位表示已接收,清 0 的位表示尚未接收。

序列号(seq_nr):

这是此数据包的序列号。与 TCP 不同,uTP 序列号不是指字节,而是指数据包。

确认号(ack_nr):

这是上一个连接收到的数据包的序列号。

连接流程

在 BEP 中给出了类似 C 语言的连接状态图来对连接流程进行描述,c.* 是连接的状态,pkt.*是数据包的字段,其连接过程与 TCP 的握手过程极为相似:

代码语言:txt
AI代码解释
复制
连接发起方                                               连接接收方

          | c.state = CS_SYN_SENT                         |
          | c.seq_nr = 1                                  |
          | c.conn_id_recv = rand()                       |
          | c.conn_id_send = c.conn_id_recv + 1           |
          |                                               |
          |                                               |
          | ST_SYN                                        |
          |   seq_nr=c.seq_nr++                           |
          |   ack_nr=*                                    |
          |   conn_id=c.rcv_conn_id                       |
          | >-------------------------------------------> |
          |             c.receive_conn_id = pkt.conn_id+1 |
          |             c.send_conn_id = pkt.conn_id      |
          |             c.seq_nr = rand()                 |
          |             c.ack_nr = pkt.seq_nr             |
          |             c.state = CS_SYN_RECV             |
          |                                               |
          |                                               |
          |                                               |
          |                                               |
          |                     ST_STATE                  |
          |                       seq_nr=c.seq_nr++       |
          |                       ack_nr=c.ack_nr         |
          |                       conn_id=c.send_conn_id  |
          | <------------------------------------------<  |
          | c.state = CS_CONNECTED                        |
          | c.ack_nr = pkt.seq_nr                         |
          |                                               |
          |                                               |
          |                                               |
          | ST_DATA                                       |
          |   seq_nr=c.seq_nr++                           |
          |   ack_nr=c.ack_nr                             |
          |   conn_id=c.conn_id_send                      |
          | >-------------------------------------------> |
          |                        c.ack_nr = pkt.seq_nr  |
          |                        c.state = CS_CONNECTED |
          |                                               |已建立连接
     .. ..|.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..|.. ..
          |                                               |
          |                     ST_DATA                   |
          |                       seq_nr=c.seq_nr++       |
          |                       ack_nr=c.ack_nr         |
          |                       conn_id=c.send_conn_id  |
          | <------------------------------------------<  |
          | c.ack_nr = pkt.seq_nr                         |
          |                                               |
          |                                               |
          V                                               V

其内容简单分析,指定连接发起方为 A,接收方为 B,将连接分为几个部分讨论:

初始化

A 设置为 CS_SYN_SENT 状态,发送连接请求。

A 的 seq_nr(序列号)初始化为1,conn_id_recv和conn_id_send分别初始化为随机数和conn_id_recv + 1。

握手

  • A 向 B 发送一个 SYN 数据包,建立连接请求。
  • B 从数据包中获取连接ID,并将其加1设置为 receive_conn_id,同时将 连接 ID 设置为send_conn_id,为接下来的数据包生成随机的seq_nr,将的状态切换为CS_SYN_RECV,接受连接请求。
  • B 向 A 发送一个状态数据包,其中包含自己的 seq_nr、ack_nr 和连接 ID,A 切换到CS_CONNECTED 状态,连接已建立。

数据传输

双方可以在连接上发送和接收数据。

超时和数据包丢失

超时(Timeouts)

初始超时时间为 1000 毫秒,之后会进行进行更新,对于每个超时的连续后续数据包的超时时间将加倍,更新逻辑:

当在只发送一次的数据包上进行通讯时,当数据包被确认,应更新连接时间(round trip time,RTT)和 RTT 方差(rtt_var),与此同时,与连接相关的数据包的默认超时时间也会在每次更新RTT和RTT方差时进行更新,超时时间设置为RTT和RTT方差的一定倍数,但不低于500毫秒,即:

代码语言:txt
AI代码解释
复制
timeout = max(rtt + rtt_var * 4, 500)

数据包丢失(Packet Loss)

  • 数据包丢失判定:如果具有序列号(seq_nr - cur_window)的数据包尚未被确认,但在它之后已经有 3 个或更多数据包被确认,则认为该数据包已经丢失。
  • 快速重传:当收到 3 个重复的确认时,即假定序列号为(ack_nr + 1)的数据包也已经丢失(在已发送该数据包的前提下)。

当数据包丢失时,最大窗口大小(max_window)减半,类似 TCP 的拥塞控制方法。

拥塞控制

TCP 有基于窗口的 拥塞控制,但 UDP 并没有类似的内容,所以需要自行实现拥塞控制,在 BEP 中给出了使用延时作为控制标准的拥塞控制方案,uTP 设置目标延迟为100毫秒,即拥塞控制的目标是每个连接不会在缓冲区内存在超过 100 毫秒的延迟。如果超过,则减缓发送速度来有效让步 TCP 流量。

通过uTP发送的数据包中包含高分辨率时间戳,接收端计算该时间戳与接收到数据时本地时间的差距,将这个差距反馈给发送方,通过最近 2 分钟的最低值作为基线(近似于最小延时)作为基准延时进行计算。

其计算由下决定

代码语言:txt
AI代码解释
复制
delay_factor = off_target / CCONTROL_TARGET; 
window_factor = outstanding_packet / max_window;
scaled_gain = MAX_CWND_INCREASE_PACKETS_PER_RTT * delay_factor * window_factor;

delay_factor 是一个表示延迟偏离目标延迟的因子,window_factor 是一个表示窗口大小与未确认数据包数量之间关系的因子,scaled_gain 是一个表示要应用于窗口大小的增益值,scaled_gain 的值用于调整窗口大小,窗口大小为零意味着套接字不能发送任何数据包,此时将触发超时,并重新设置窗口大小,以避免过度拥塞。

穿透(Holepunch)拓展

穿透拓展协议基于基础的拓展协议,它提供了一种利用中继节点帮助建立 uTorrent 连接的方式,拓展协议相关内容参考Bittorrent 协议浅析(五)拓展协议 及 元数据传输拓展进行了解,该拓展标识为ut_holepunch,下面是一个仅包括穿透拓展的握手实例,这里选择了 4 作为信息 ID,在实际过程中不同下载器会有不同选择:

代码语言:json
AI代码解释
复制
{
  m: {
    ut_holepunch: 4,
  }
}

穿透拓展数据除了数据长度、类型、消息 ID 外包括:

  • 1 字节类型
  • 1 字节地址类型,IPv4 为 0,IPv6 为 0x01
  • 4 或 16 字节 IP 地址(IPv4 或 IPv6)
  • 端口(2 字节);
  • 4 字节 错误代码,若无错误为 0

支持的消息类型包括:

类型编码

类型

描述

0x00

rendezvous

会面,向发起对等点和目标对等点发送连接消息

0x01

connect

连接,打开 uTP 连接

0x02

error

错误,无法完成

一个节点可以将连接的目标节点信息写入数据包发送给另一个节点(后称中继节点)一个会面信息,如果中继节点已连接到目标节点,且目标节点支持,中继节点会向这个节点和目标节点都发送一个连接消息,其中包含彼此的端口信息。在接收到连接消息后,每个节点都会启动与另一个节点的uTP连接,需要注意,有可能两次 uTP 连接尝试会同时成功,此时系统需处理多个连接。如果无法处理,中继节点应该向发起节点回复一个错误消息。

在实现过程当中,如果目标节点不希望连接,它应该在忽略连接消息或连接请求,不向中继节点或请求节点发送错误消息,如果请求节点没有在拓展握手阶段表明支持 ut_holepunch ,那么中继节点须忽略 ut_holepunch 消息。如果节点之间已经建立了连接,也应忽略连接信息。

常见的错误信息有:

编码

错误信息

描述

0x01

NoSuchPeer

目标节点无效

0x02

NotConnected

中继节点未连接到目标节点

0x03

NoSupport

目标节点不支持 holepunch 扩展

0x04

NoSelf

目标节点的端点信息被错误地设置为中继节点的信息

在 NoSuchPeer 的情况下,也可以选择发送 NotConnected 错误代码。

穿透拓展为位于阻止传入连接后的下载器提供了更多连接到外部节点的可能,但仍然存在非常多的局限性,需结合网络环境,下载器实际进行分析。

基于 UDP 的 Tracker

在 BitTorrent 标准协议中,节点使用 HTTP 的方式与 Tracker 服务器进行通讯获取节点列表,请求内容和响应内容相对较短,在此情况下,需要建立 TCP 并频繁打开关闭连接的 HTTP 存在较大网络开销,使用 UDP 进行 Tracker 请求可以减少数据流量,同时可以简化 Tracker 的实现,虽然对于节点而言几乎没有区别,但对于需要处理大量请求的 Tracker 显得较为重要。

UDP 是一种 “不可靠” 的协议,下载器需要在 15 * 2 ^ n 秒未收到响应后重新发送请求,n 为失败的请求次数,最高到 8 ,特别的,连接 ID(connection ID)过期也需要重新发送请求。连接 ID 是缓解 UDP 源地址伪造的手段,Tracker 在收到请求后生成一个连接 ID 发送给下载器,下载器需要将该 ID 再次发送至 Trakcer 以进行源地址校验。这个 ID 可以复用于多个请求中,通常 1 分钟有效期。

连接

Tracker 请求内容均按大端序进行发送,请求开始后,需要首先请求连接 ID,首先需要选择一个随机的消息传输 ID,构造并发送下述数据包:

代码语言:txt
AI代码解释
复制
0       4       8               16
+-------+-------+---------------+
| 0x41727101980 |   0   | tra_id|
+---------------+---------------+

偏移(字节)

大小、类型

描述

0

64位 整数

协议 ID

0x41727101980

8

32位 整数

操作

0

12

32位 整数

消息ID

将收到如下至少 16 字节的响应:

代码语言:txt
AI代码解释
复制
0       4       8               16
+-------+-------+---------------+
|   0   |tra_id |     con_id    |
+---------------+---------------+

偏移(字节)

大小、类型

描述

0

32位 整数

操作

0

4

32位 整数

消息ID

8

64位 整数

连接ID

收到响应后,应将连接 ID 进行存储,在超时(1分钟)之前均使用这一连接 ID 进行数据请求。

请求

可以同时获取约 74 个种子的信息,发送的请求如下:

代码语言:txt
AI代码解释
复制
0       4       8               16
+-------+-------+---------------+
|     con_id    |   2   |tra_id |
+---------------+---------------+
|     info_hash ...      
+---------------+---------------+

偏移(字节)

大小、类型

描述

0

64位 整数

连接ID

8

32位 整数

操作

2

12

32位 整数

消息ID

16+20*n

20字节 字符串

信息哈希

下载器公告

在 IPv4 环境下,请求:

代码语言:txt
AI代码解释
复制
0       4       8               16              24              32
+-------+-------+---------------+---------------+---------------+
|   con_id      |   1   |tra_id |     info_hash...              |
+-------+-------+---------------+---------------+---------------+
| ...   |                   peer_id             |    download   |
+---------------+---------------+---------------+---------------+
|      left     |     upload    | event |   ip address  |  key  |
+---------------+---------------+---------------+---------------+
|  want | port  |
+---------------+---------------+---------------+---------------+

偏移(字节)

大小、类型

描述

0

64位 整数

连接ID

8

32位 整数

操作

1

12

32位 整数

消息ID

16

20字节 字符串

信息哈希

36

20字节 字符串

节点ID

56

64位 整数

下载量

64

64位 整数

剩余量

72

64位 整数

上传量

80

32位 整数

事件

0:无;1:完成;2:开始;3:停止

84

32位 整数

IP地址

默认 0

88

32位 整数

key

92

32位 整数

期望返回数量

默认 -1

96

16位 整数

端口

虽然包括了 IP 和端口,但大部分 Tracker 很少会识别并根据内容进行响应。

响应:

代码语言:txt
AI代码解释
复制
0       4       8               16              24              32
+-------+-------+---------------+---------------+---------------+
|   1   |tra_id |inter_ |leech_ |seeders|   IP  |pot|IP...
+-------+-------+---------------+---------------+---------------+

偏移(字节)

大小、类型

描述

0

32位 整数

操作

1

4

32位 整数

消息ID

8

32位 整数

请求间隔

12

32位 整数

下载数

16

32位 整数

做种数

20 + 6 * n

32位 整数

IP 地址

24 + 6 * n

16位 整数

端口

对于 IPv6,返回的地址和端口从 6 字节变为 18 字节,其他均一致,此时请求中的 IP地址 无效,应保持为 0。

错误

一个错误信息如下:

偏移(字节)

大小、类型

描述

0

32位 整数

操作

3

4

32位 整数

消息ID

8

字符串

错误信息

该部分完

自此,BitTorrent 的最终提案和已接受的提案除了快速交换和 WebSeed 已经都进行了分析和阐述,后续还会继续进行上述内容的分析,同时会增加更多的 BitTorrent 草案的分享,敬请关注,如果更新,会在这里有链接:

最后,本文参加的征文活动广告:

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
发布于 2018-05-25 13:24 更新于 2018-06-02 01:26
walterlv
2018/09/18
1.5K0
Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
Java 中的屠龙之术:如何修改语法树?
来源:https://my.oschina.net/u/4030990/blog/3211858
程序员黄小斜
2021/11/24
1.2K0
[深入解析C#] 泛型
使用泛型(generic),可以编写在编译时类型安全的通用代码,无须事先知道要使用的具体类型,即可在不同位置表示相同类型。在引入之初,泛型主要用于集合。如今,泛型已经广泛应用于C#的各个领域,其中用得较多的有如下几项:
科控物联
2022/03/29
1.7K0
[深入解析C#] 泛型
C# 发展历史及版本新功能介绍
C# 1.0 版 回想起来,C# 1.0 版非常像 Java。 在 ECMA 制定的设计目标中,它旨在成为一种“简单、现代、面向对象的常规用途语言”。 当时,它和 Java 类似,说明已经实现了上述早
程序你好
2018/07/20
4.4K0
基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider
Roslyn 是 .NET 平台下十分强大的编译器,其提供的 API 也非常丰富好用。本文将基于 Roslyn 开发一个 C# 代码分析器,你不止可以将分析器作为 Visual Studio 代码分析和重构插件发布,还可以作为 NuGet 包发布。不管哪一种,都可以让我们编写的 C# 代码分析器工作起来并真正起到代码建议和重构的作用。
walterlv
2023/10/23
9300
基于 Roslyn 同时为 Visual Studio 插件和 NuGet 包开发 .NET/C# 源代码分析器 Analyzer 和修改器 CodeFixProvider
Roslyn 入门:使用 Roslyn 静态分析现有项目中的代码
发布于 2018-03-18 12:45 更新于 2018-06-02 01:26
walterlv
2018/09/18
1.9K0
Roslyn 入门:使用 Roslyn 静态分析现有项目中的代码
Roslyn 节点的 Span 和 FullSpan 有什么区别 准备创建语法树访问语法树访问方法访问表达式不同
本文告诉大家在使用 Roslyn 分析代码时,使用的 Span 和 FullSpan 有什么区别
林德熙
2018/09/19
9230
Roslyn 节点的 Span 和  FullSpan 有什么区别
            准备创建语法树访问语法树访问方法访问表达式不同
在 Roslyn 分析语法树时添加条件编译符号的支持
我们在代码中会写 #if DEBUG 或者 [Conditional("DEBUG")] 来使用已经定义好的条件编译符号。而定义条件编译符号可以在代码中使用 #define WALTERLV 来实现,也可以通过在项目属性中设置条件编译符号(Conditional Compilation Symbols)来实现。
walterlv
2020/01/21
1.1K0
在 Roslyn 分析语法树时添加条件编译符号的支持
抽象语法树(AST)全流程示例解析
以下以表达式 3 + 5 * 2 为例,贯穿从代码输入到最终应用的全流程,说明AST的核心概念和作用。
用户2755790
2025/05/12
3600
CSharp委托与匿名函数
面对事件处理,我们通常会通过定义某一个通用接口,在该接口中定义方法,然后在框架代码中,调用实现该接口的类实例的方法来实现函数的回调。可能这样来说有些抽象,那我们提供一个具体的情形来实现这一情形。
w4ngzhen
2023/10/16
2500
C++11——引入的新关键字
auto是旧关键字,在C++11之前,auto用来声明自动变量,表明变量存储在栈,很少使用。在C++11中被赋予了新的含义和作用,用于类型推断。
恋喵大鲤鱼
2018/08/03
1.5K0
生成代码,从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型
发布于 2018-01-31 05:38 更新于 2018-05-25 12:33
walterlv
2018/09/18
1.5K0
生成代码,从 T 到 T1, T2, Tn —— 自动生成多个类型的泛型
.Net 编译器平台 --- Roslyn
最近做一个功能想要动态执行C#脚本,就是预先写好代码片段,在程序运行时去执行代码段,比如像这样(以下代码为伪代码):
Niuery Diary
2023/10/22
4660
.Net 编译器平台 --- Roslyn
动手写编译器:手动构造语法树,驱动中间代码生成
在前面章节中我们给出了语法解析树对应节点的设计,这些节点能够针对其内容完成中间代码的输出,这一节我们继续完善必要节点的设计,然后手动构造语法树,并驱动语法树实现中间代码生成。
望月从良
2022/04/27
3820
动手写编译器:手动构造语法树,驱动中间代码生成
Go 泛型之泛型约束
虽然泛型是开发人员表达“通用代码”的一种重要方式,但这并不意味着所有泛型代码对所有类型都适用。更多的时候,我们需要对泛型函数的类型参数以及泛型函数中的实现代码设置限制。泛型函数调用者只能传递满足限制条件的类型实参,泛型函数内部也只能以类型参数允许的方式使用这些类型实参值。在 Go 泛型语法中,我们使用类型参数约束(type parameter constraint)(以下简称约束)来表达这种限制条件。
贾维斯Echo
2023/12/29
7270
Go 泛型之泛型约束
C#历代版本新特性——面试题常用
掌握一门语言,当然要掌握它的特性,而随着C#历代版本的迭代更替,C#语言也日趋完善,在C#2.0~C#4.0版本所带来的新的语法特性格外重要。C#的新特性,其本质都是“语法糖”,目的是提升开发效率,在编译时会被编译器转成原始语法。下面按照版本顺序依次介绍其中在日常开发中比较常用的部分。 C# 1.0 特性 第1个版本,编程语言最基础的特性。 Classes:面向对象特性,支持类类型 Structs:结构 Interfaces:接口 Events:事件 Properties:属性,类的成员,提供访问字段
李郑
2018/03/06
2K0
C++模板总结
模板(Template)指 C++ 程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如 STL 以及 IO Stream。模板是 C++ 支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
C语言与CPP编程
2021/12/10
1.3K0
C++模板总结
C#历来语法特性总结
C# 11已与.NET 7一起发布,本文按照.NET的发布顺序,根据微软官方文档整理C#中一些有趣的语法特性。
郑子铭
2023/09/19
6751
C#历来语法特性总结
【JDK1.8 新特性】方法引用,构造器引用
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
CODER-V
2023/03/08
3220
.net题库第1-9章
第一章 单项选择题 第1题 C#程序的执行过程是( ) 从程序的Main方法开始,到最后一个方法结束 (答案) 从程序的第一个方法开始,到最后一个方法结束 从程序的Main方法开始,到Main方法结束 从程序的第一个方法开始,到Main方法结束 得分: 0.0 /10.0 第2题 C#语言源代码文件的后缀名为( )。 .csP .cs (答案) .C .C# 得分: 10.0 /10.0 第3题 下面对Write()和WriteLine()方法的描述,( )是正确的。 WriteLine()方法在输出字符串的后面添加换行符 (答案) 使用Write()和WriteLine()方法输出数值变量时,必须要先把数值变量转换成字符串 使用不带参数的WriteLine()方法时,将不会产生任何输出 使用Write()输出字符串时,光标将会位于字符串的下一行 得分: 10.0 /10.0 第4题 C#语言经编译后得到的是( )。 机器指令 Microsoft中间语言指令 (答案) 本机指令 汇编指令 得分: 10.0 /10.0 第5题 C#中导入某一命名空间的关键字是( )。 include import using (答案) use 得分: 10.0 /10.0 第6题 在C#中不可作为注释的选项是( ) ‘ (答案) /// /和/ // 得分: 10.0 /10.0 第7题 Console标准的输入设备是( )。 打印机 屏幕 键盘 (答案) 鼠标 得分: 10.0 /10.0 第8题 下面对Read()和ReadLine()方法的描述,( )是错误的。 ReadLine()方法读取的字符不包含回车和换行符 使用Read()方法读取的字符包含回车和换行符 (答案) 只有当用户按下Enter键时,Read()和ReadLine()方法才会返回 Read()方法一次只能从输入流中读取一个字符 得分: 0.0 /10.0 第9题 CLR是一种( )。 API编程接口 开发环境 运行环境 (答案) 程序设计语言 得分: 10.0 /10.0 第10题 下列( )选项不是.NET框架可以创建的应用程序类型。 Windows应用 Web服务 控制台应用 MIS系统 (答案)
用户8224910
2022/08/30
1.1K0
相关推荐
Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码
更多 >
LV.3
这个人很懒,什么都没有留下~
目录
  • 0. 回顾
  • uTorrent
    • 数据格式
    • 连接流程
    • 超时和数据包丢失
      • 超时(Timeouts)
      • 数据包丢失(Packet Loss)
    • 拥塞控制
  • 穿透(Holepunch)拓展
  • 基于 UDP 的 Tracker
    • 连接
    • 请求
    • 下载器公告
    • 错误
  • 该部分完
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档