读书建议:如果某个地方看不懂,请将你到底不懂什么东西描述出来。你可能会发现作者在下一段就给予了你解释。

1.TCP/IP协议

下面是TCP/IP协议的四层结构:

ip协议编程 python tcp tcpip协议代码_TCP


从图中可知,应用层的应用程序一般使用的是传输层或网络层的协议,但不能使用数据链路层提供的服务。。每层协议都将在上层数据的基础上加上自己的头部信息(有时还包括尾部信息)。

  • 协议:规定发送过来的一堆二进制码的含义,这样通信双方就可以根据得到的二进码进行相应的操作。系统程序员会编写代码实现这些协议,即这些代码实现了对二进制的解析和相应的操作。TCP/IP协议的四层结构就是四套不同的代码。
  • 内核空间:就是系统程序员编写的代码,非系统程序员不会去修改这些代码,非系统程序员只调用这些代码向外提供的接口。

1.1 ARP高速缓存的查看和修改

ARP:广播问一下“你们谁的IP是A,请告诉我你的mac为多少”。ARP有一个高速缓存,保存经常(最近)访问的IP到mac的映射,Linux下可以使用arp命令来查看和修改ARP高速缓存。接下来我们通过抓包来观察ARP包的传输情况:

首先你需要知道常见的arp命令:

arp -a # 查看ARP缓存内容
sudo arp -d 192.168.1.109           # 删除192.168.1.109对应的ARP缓存项
$sudo arp -s 192.168.1.109 08:00:27:53:10:67  # 添加ARP缓存项

然后假设你有如下网络结构:

ip协议编程 python tcp tcpip协议代码_数据_02

首先你需要开启Kongming20的echo服务:开启echo服务

接下来,在ernest-laptop上执行如下命令:

$sudo arp -d 192.168.1.109#清除ARP缓存中Kongming20对应的项
$sudo tcpdump -i eth0 -ent '(dst 192.168.1.109 and src 192.168.1.108)or (dst 192.168.1.108 and src 192.168.1.109)' #如无特殊声明,抓包都在机器ernest-laptop上执行
$telnet 192.168.1.109 echo    #   开启另一个终端执行telnet命令进行远程登录
Trying 192.168.1.109...
Connected to 192.168.1.109.
Escape character is'^]'.
^]                 # (回车)#输入Ctrl+]并回车
telnet>quit
Connection closed.

tcpdump抓取到的众多数据包中,只有最靠前的两个和ARP通信有关系,现在将它们列出(数据包前面的编号是笔者加入的,后同):

1.00:16:d3:5c:b9:e3>ff:ff:ff:ff:ff:ff,ethertype ARP(0x0806),length 42:Request who-has 192.168.1.109 tell 192.168.1.108,length 28
2.08:00:27:53:10:67>00:16:d3:5c:b9:e3,ethertype ARP(0x0806),length 60:Reply 192.168.1.109 is-at 08:00:27:53:10:67,length 46

由tcpdump抓取的数据包本质上是以太网帧,数值0x806是以太网帧头部的类型字段的值。其他的内容就是简单的英语描述。

1.2 Linux下访问DNS服务

DNS:域名到IP的映射,/etc/resolv.conf文件来存放DNS服务器的IP地址,如下:

#Generated by Network Manager
nameserver 219.239.26.42
nameserver 124.207.160.106

其中的两个IP地址分别是首选DNS服务器地址和备选DNS服务器地址。

使用host访问DNS服务器:

$host-t A www.baidu.com
www.baidu.com is an alias for www.a.shifen.com.
www.a.shifen.com has address 119.75.217.56
www.a.shifen.com has address 119.75.218.77
  • 本命令向首选DNS服务器219.239.26.42查询机器www.baidu.com的IP地址。
  • host命令的输出告诉我们,机器名www.baidu.com是www.a.shifen.com.的别名,并且该机器名对应两个IP地址。-t选项告诉DNS协议使用哪种查询类型。我们这里使用的是A类型,即通过机器的域名获得其IP地址(但实际上返回的资源记录中还包含机器的别名)

下面抓包看看:

$sudo tcpdump -i eth0 -nt -s 500 port domain
$host -t A www.baidu.com

这一次执行tcpdump抓包时,我们使用“port domain”来过滤数据 包,表示只抓取使用domain(域名)服务的数据包,即DNS查询和应 答报文。tcpdump的输出如下:

1.IP 192.168.1.108.34319>219.239.26.42.53:57428+A?www.baidu.com.(31)
2.IP 219.239.26.42.53>192.168.1.108.34319:57428 3/4/4 CNAME www.a.shifen.com.,A 119.75.218.77,A 119.75.217.56(226)

1.3 IP协议

特点:
无状态:独立传输,接收到的数据可能是乱序和重复的。IP数据报的分片是需要重组来使得有序,这里的无状态是指数据报之间无序。
无连接:不存储表示连接的相关数据结构
不可靠:尽最大努力传输。发送端的IP模块一旦检测到IP数据报发送失败,就通知上层协议发送失败,而不会试图重传。通知上层协议以后,上层协议决定重传的策略。

IPv4头部结构的重要字段:

  • 4位的TOS字段分别表示:最小延时,最大吞吐量,最高可靠性和最小费用。最多有一个能置为1,设置其中的相应位就可以让数据传输产生对应的效果。
  • MTU指的是数据链路层帧的长度,以太网最长帧的长度为1500字节,数据部分最多是1480字节,所以IP数据报不能不能超过1480字节,如果超过就需要进行分片。
  • 可选信息:记录经过的路由、记录各个路由器转发时候的时间、traceroute指定需要经过哪些路由器

使用tcpdump观察IPv4头部结构:

$sudo tcpdump -ntx -i lo  #抓取本地回路上的数据包
$telnet 127.0.0.1        #开启另一个终端执行telnet命令登录本机
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is'^]'.
Ubuntu 9.10
ernest-laptop login:ernest    #输入用户名并回车
Password:      #输入密码并回车

IP分片及其相关实验(不重要):具体可以见原文

route:

route命令查看路由表:

ip协议编程 python tcp tcpip协议代码_ip协议编程 python tcp_03

route命令可以修改路由表:

$sudo route add-host 192.168.1.109 dev eth0   # 添加:去主机192.168.1.109时需要经过哪个端口
$sudo route del-net 192.168.1.0 netmask 255.255.255.0  # 删除网络192.168.1.0对应的路由项
$sudo route del default # 删除默认路由,这样做的后果是无法访问因特网。
$sudo route add default gw 192.168.1.109 dev eth0 # 重新设置默认路由为192.168.1.109

使主机具有数据转发功能:主机一般只发送和接收数据报,修改/proc/sys/net/ipv4/ip_forward文件为1(修改命令:echo 1>/proc/sys/net/ipv4/ip_forward),则主机将具有转发功能。

ICMP重定向报文:ICMP重定向报文的作用是告诉源主机更好的路由方案,即告诉主机,当目标的IP为A时,需要将数据转发到哪个路由器上。/proc/sys/net/ipv4/conf/all/send_redirects内核参数指定是否允许发送ICMP重定向报文,而/proc/sys/net/ipv4/conf/all/accept_redirects内核参数则指定是否允许接收ICMP重定向报文。一般来说,主机只能接收ICMP重定向报文,而路由器只能发送ICMP重定向报文。

IPv6协议并不是IPv4协议的简单扩展,而是完全独立的协议。

1.4 TCP

1.4.1 TCP与UDP的区别、TCP头部结构的重要字段

TCP与UDP:
TCP:可靠的、面向连接的和基于流(stream)

  • 基于流:基于流的数据没有边界(长度)限制,它源源不断地从通信的一端流入另一端。TCP在发送这些字节流时是通过分组传输来实现的,分组的大小(MSS)是在TCP三次握手时确定的。
  • 面向连接: 两个人打电话时,双方确认并建立连接后才能进行通信。
  • 可靠:每个发送都需要应答。超时重传:超过某时间未收到应答则重发
  • TCP是一对一的;

UDP:不可靠、无连接和基于数据报(每个报文有一定长度)

  • 面向无连接:在邮局寄信时,你只需要将信放在邮筒里,不需要给收件人通知,收件人也不知道你给他寄信了。
  • 不可靠:UDP协议则和IP协议一样,提供不可靠服务。它们都需要上层协议来处理数据确认和超时重传。 上层协议来处理的意思是
  • UDP则非常适合于广播和多播
  • 接收端必须及时针对每一个UDP数据报执行读操作(通过recvfrom系统调用),否则就会丢包(这经常发生在较慢的服务器上)。并且,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则UDP数据将被截断

TCP头部结构的重要字段:

  • 16位端口号:客户端的端口号由系统自动选取,服务器则使用知名服务端口号,所有知名服务使用的端口号都定义在/etc/services文件中
  • 32位序号(sequence number):报文段在字节流中的相对位置(偏移量)= 初始序号+偏移量
  • 32位确认号(acknowledgement number):用于确认已经收到对方的信息,可在发送信息时随便携带。
  • 6个标志位:
    URG(紧急指针(urgent pointer)是否有效)
    ACK(报文段是否为确认报文段)
    PSH(提示接收端应用程序应该立即从TCP接收缓冲区中读走数据)
    RST(要求对方重新建立连接)
    SYN(请求建立一个连接)
    FIN(通知对方本端要关闭连接了)
  • 16位紧急指针(urgent pointer):它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。

1.4.2 TCP连接的建立和释放、客户端和服务器状态的转移

三次握手建立连接和释放连接的过程如下:

ip协议编程 python tcp tcpip协议代码_TCP_04

进一步解释:

  • 三次握手为什么是三次的原因是每次发送的信息都要求得到一个确认
  • 同步报文段和结束报文段没有携带任何应用程序数据,但它们也要占用一个序号值
  • 确认号 = 序号+1
  • 实际上,仅用于确认目的的确认报文段5是可以省略的,因为结束报文段6也携带了该确认信息。确认报文段5是否出现在连接断开的过程中,取决于TCP的延迟确认特性。
  • 半关闭状态:我告诉你我的发送已经结束了,但是我还可以收数据的,即已接收到报文段4且报文段6未发送前的时间内,Kongming20还可以向ernest-laptop发送信息。
  • 超时重连:客户端没有收到对报文段1的确认,此时会重新发送报文段1,下面通过实际的例子来演示:
# ernest-laptop上丢弃所有收到的请求
$sudo iptables -F # 利用iptable命令过滤收到的数据包
$sudo iptables -I INPUT -p tcp --syn -i eth0 -j DROP

# Kongming20上执行telnet命令登录到ernest-laptop
$sudo tcpdump -n -i eth0 port 23 #仅抓取telnet客户端和服务器交换的数据包
$date;telnet 192.168.1.108;date#在telnet命令前后都执行date命令,以计算超时时间
Mon Jun 11 21:23:35 CST 2012
Trying 192.168.1.108...
telnet:connect to address 192.168.1.108:Connection timed out
Mon Jun 11 21:24:38 CST 2012

从tcpdump抓取的信息中可以发现客户端重发了多次相同序号的TCP报文段。/proc/sys/net/ipv4/tcp_syn_retries中可修改进行超时重连的次数

【问题】服务器没有收到报文段3,会进行什么处理?答:应该会重新发送报文段2。

客户端和服务器状态的转移:

TCP连接的任意一端在任一时刻都处于某种状态,当前状态可以通过netstat命令(见第17章)查看

ip协议编程 python tcp tcpip协议代码_客户端_05

粗虚线表示典型的服务器端连接的状态转移;粗实线表示典型的客户端连接的状态转移。我猜细实线应该表示服务器和客户端共同的状态转移。CLOSED是一个假想的起始点,并不是一个实际的状态。

  • 服务器是从LISTEN开始,经过三次握手变为ESTABLISHED。当客户端主动关闭连接时,服务器通过返回确认报文段使连接进入CLOSE_WAIT状态。
  • FIN_WAIT_1状态的三种转移方案:
    1.从FIN_WAIT_1状态——》FIN_WAIT_2状态 ——》TIME_WAIT状态
    2.从FIN_WAIT_1状态直接进入TIME_WAIT状态:这意味着结束报文段6也携带了对报文4的确认信息
    3.从FIN_WAIT_1状态——》CLOSING状态 ——》TIME_WAIT状态(同时关闭):应该是指没有处于FIN_WAIT_1状态,没有接收到ACK且接收到了FIN
  • 客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:/proc/sys/net/ipv4/tcp_max_orphans和/proc/sys/net/ipv4/tcp_fin_timeout。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中生存的时间。

TCP连接的建立与断开过程,客户端和服务器的状态转移如下图:

ip协议编程 python tcp tcpip协议代码_TCP_06

1.4.3 TIME_WAIT、复位报文段、对报文的确认、外带数据

TIME_WAIT:收到服务器的结束报文时客户端处于的状态,然后客户端将会发送确认报文。TIME_WAIT状态在持续2MSL后,会被转化成CLOSED(TCP报文段的最大生存时间是MSL)。

  • TIME_WAIT的作用:
    1)服务器发送的结束报文的确认报文丢失时,服务器会重复发送的结束报文,此时处于TIME_WAIT状态的客户端会对结束报文进行处理
    2)处理来迟的TCP报文:连接结束了,但是网络中可能还存在未到达客户端的报文
  • TIME_WAIT的副作用:如果程序使用的是固定的端口(如知名服务端口号),在程序结束后,端口会被占用一段时间,导致程序重启失败。有时为了避免这种情况,可执行如下两种操作:
    1)强制使用处于TIME_WAIT状态的socket地址:服务器程序可以通过设置socket选项SO_REUSEADDR来强制使用被处于TIME_WAIT状态的连接所占用的socket地址。
    2)不进入TIME_WAIT状态:我们也可以通过修改内核参数/proc/sys/net/ipv4/tcp_tw_recycle来快速回收被关闭的socket,从而使得TCP连接根本就不进入TIME_WAIT状态
    一般来说,客户端使用的是随机的端口,所以一般不会出现端口被占用的情况。但如果是服务器主动关闭连接后异常终止,则因为它总是使用同一个知名服务端口号,所以连接的TIME_WAIT状态将导致它不能立即重启

复位报文段:复位报文段就是携带RST标志的报文段,以通知对方关闭连接或重新建立连接。

  • 1)访问不存在端口或处于TIME_WAIT的端口时,对方将会返回复位报文段
  • 2)一旦发送了复位报文段,此发送端所有排队等待发送的数据都将被丢弃。这是异常终止连接的方法。
  • 3)A关闭连接或异常终止了连接,而B没有接收到结束报文段,此时B如果发送信息,A就会返回复位报文段。

TCP交互数据流(对报文的确认是否带有数据):如telnet登录后,输入各种命令与远程服务器进行交互,这些信息就是TCP交互数据流。

  • 客户端对服务器返回数据的确认都不带应用程序数据。这是因为要携带应用程序数据要等用户输入才行,而用户输入的速度是比较慢。这是一般是在局域网中采取的策略,广域网中可以采用Nagle算法。
    Nagle算法:每输入一个字符就用一个TCP报文进行传输,这样会导致网络拥塞。Nagle算法:报文未被确认之前不发送新的报文,但发送方在等待确认的同时收集本端需要发送的数据,并在确认到来时以一个TCP报文段将它们全部发出。
  • 服务器对客户端返回数据的确认一般都带应用程序数据。这里的确认是指对客户端发送请求的确认,而携带的应用程序数据是指客户端请求的数据。所以一般是将客户端请求的数据和对请求的确认一起发送回客户端。

TCP成块数据流:发送方会连续发送多个TCP报文段,接收方可以一次确认所有这些报文段。服务器每发送4个TCP报文段就传送一个PSH标志(tcpdump输出标志P)给客户端,以通知客户端的应用程序尽快读取数据
ubuntu上开启ftp服务,然后就可以使用ftp登录,使用get获取文件

外带数据:用于告诉对方本端发生的紧急事件,此种报文总是立刻发送。UDP没有实现带外数据传输,TCP也没有真正的带外数据。TCP头部中的紧急指针标志和紧急指针两个字段用于实现带外数据。紧急指针被设置为指向最后一个带外数据的下一字节。

  • 接收端只将紧急事件的报文的最后一个字节读入到一个特殊的缓存中。这个缓存只有1字节。如果上层应用程序没有及时将带外数据从带外缓存中读出,则后续的带外数据(如果有的话)将覆盖它。
  • TCP设置SO_OOBINLINE选项时,则带外数据将和普通数据一样被TCP模块存放在TCP接收缓冲区中。紧急指针可以用来指出带外数据的位置

1.4.4 超时重传、可靠传输与拥塞控制

TCP超时重传:TCP服务必须能够重传超时时间内未收到确认的TCP报文段。

  • 重传几次后,就尝试利用ARP重新获取目的地的mac地址,为什么启用ARP?答:因为超时重传失败了,此时主机认为缓存中记录的mac地址已经过期了。
  • 控制超时重传次数的文件::/proc/sys/net/ipv4/tcp_retries1和/proc/sys/net/ipv4/tcp_retries2,前者 指定在底层IP接管之前TCP最少执行的重传次数,默认值是3。后者指 定连接放弃前TCP最多可以执行的重传次数,默认值是15(一般对应 13~30 min)。
    故有这样一个认识,所有我们学习的网络传输协议都是用一个一个文件实现的。如果我们要修改某一个协议中的某一个值时候,只需要百度“linux修改xx”就可以了。所以说linux中万物都是文件。

可靠传输(参考自谢希仁《计算机网络》):

  • 以字节为单位的滑动窗口:
    1)A收到确认,根据确认号和接收方窗口值调整窗口
    2)B收到从头开始的连续字节,窗口才能向前移动并发出确认超时重传的时间选择SACK:告诉发送方,我那个部分没有收到,请你再发一下。
  • 流量控制:
    发太快,来不及接收:发送窗口不能比接收方给出的接收窗口大
    发太小:使用Nagle算法,即先将第一个数据字节发送出去,然后就等待对此数据报的确认并在等待期间缓存所有到达的数据。当收到确认或数据达到发送窗口大小的一半或数据达到最大长度,就立刻发送一个报文段。

糊涂窗口综合征:接收方发送的确认,它确认的数据太小,使得发送窗口增长缓慢。解决方法:

  • 1)等接收缓存可以装一个最长报文时,再发确认。
  • 或 2)等接收缓存一半是空闲的。【问题】这样老是等这等那的,不就不能实现实时的传输了????

SWND、RWND和CWND:

  • 发送窗口SWND:向网络一次连续写入的数据量。
  • 接收窗口RWND:接收方可通过其接收通告窗口(RWND)来控制发送端的SWND
  • 拥塞窗口CWND:发送端还引入了一个称为拥塞窗口(Congestion Window,CWND)来进行拥塞控制。SWND值是RWND和CWND中的较小者。

拥塞控制包括内容:
发送端在未检测到拥塞时使用慢开始和拥塞避免。

  • 慢开始:拥塞窗口cwnd初始化为2~4SMSS(最大报文段)。每收到一次确认,拥塞窗口就增加min(N,SMSS),其中N为此次被确认的字节数,SMSS为最大报文段的字节数。这样,每经过一个往返时间RTT,cwnd就几乎翻倍。
  • 拥塞避免:当cwnd超过门限值时,有如下两种方法使得CWND按照线性方式增加:
    1)不是每次收到确认都更新cwnd,而是经过一个往返时间RTT,才在拥塞窗口上一次增加min(N,SMSS)
    2)每收到一个对新数据的确认报文段,就按照CWND+=SMSS*SMSS/CWND来更新CWND。
    这样,每经过一个往返时间RTT,cwnd都只增加一个SMSS左右。

发送端检测到拥塞时需要采取其他策略,有两种情况发送端会检测到拥塞:
1)超时却未收到确认,此时会进行如下操作:

\[门限值ssthresh = max ( FlightSize/2,2*SMSS) ,FlightSize为未被确认的字节数\]

\[CWMD<=SMSS\]

这样调整之后,CWMD将小于SMSS,那么也必然小于新的慢启动门限值ssthresh,故而拥塞控制再次进入慢启动阶段

2)收到三个对同一报文的重复确认。重复的确认报文意味着接收方又告诉一次发送方,它想要的下一个东西是啥,也就是提醒发送方这个数据我接收方还没收到。此时会进行如下操作:

  • 快重传:立即重传丢失的报文段,并按进行如下操作:

\[门限值ssthresh = max ( FlightSize/2,2*SMSS) ,FlightSize为未被确认的字节数\]

\[CWMD = ssthresh + 3*SMSS\]

  • 快恢复:出现快重传时,使用快恢复。门限值=cwnd/2,cwnd=门限值,即立刻开启拥塞避免算法。

额外说明:
谢希仁书(链接)中使用报文段的个数作为拥塞窗口的大小举例说明,这里是以实际的字节为单位进行说明。
拥塞控制算法在Linux下有多种实现,比如reno算法、vegas算法和cubic算法等,/proc/sys/net/ipv4/tcp_congestion_control文件指示机器当前所使用的拥塞控制算法

1.5 TCP/IP通信案例:访问Internet上的Web服务器

在Kongming20上运行wget客户端程序,在ernest-laptop上运行squid代理服务器程序。客户端通过代理服务器的中转,获取Internet上的主机www.baidu.com的首页文档index.html。

在Kongming20上设置环境变量http_proxy:

$export http_proxy="ernest-laptop的ip:3128"   #在Kongming20上执行

3128是squid服务器默认使用的端口号(可以通过lsof命令查看服务器程序监听的端口号)。设置好环境变量之后,Kongming20访问任何Internet上的Web服务器时,其HTTP请求都将首先发送至ernest-laptop的3128端口。squid代理服务器接收到wget客户端的HTTP请求之后,将简单地修改这个请求,然后把它发送给最终的目标Web服务器。

三种代理类型:

  • 正向代理(请求从局域网中出去):将我们的请求送给正向代理服务器,让它替我们请求
  • 反向代理(请求进入局域网中):通过反向服务器将众多请求分散给多个服务器,实现负载均衡。大型网站通常将反向代理作为公网访问地址,阻止web攻击。
  • 透明代理:我理解的是:网关总是将请求发送给一个正向代理服务器,则称为透明代理。此时不需要对局域网内的所有主机进行设置,所有主机都通过网关使用正向代理服务器。应该就是通过路由器FQ的原理。

部署squid代理服务器:squid、varnish都是提供了缓存能力的代理服务器软件,其中squid支持所有代理方式,而varnish仅能用作反向代理。在服务器上配置squid程序就可以实现代理。在ernest-laptop上部署squid代理服务器:只需修改squid服务器的配置文件/etc/squid3/squid.conf,在其中加入如下两行代码(需要root权限,且应该加在合适的位置,详情可参考其他类似条目的设置):

acl localnet src 192.168.1.0/24
http_access allow localnet

这两行代码的含义是:允许网络192.168.1.0上的所有机器通过该代理服务器来访问Web服务器。我们通过上面的两行代码简单地配置了squid的访问控制。但实际应用中,squid提供更多、更安全的配置,比如用户验证等。
接下来在ernest-laptop上执行如下命令,以重启squid服务器:

$sudo service squid3 restart
*Restarting Squid HTTP Proxy 3.0 squid3[OK]

service是一个脚本程序(/usr/sbin/service),它为/etc/init.d/目录下的众多服务器程序(比如httpd、vsftpd、sshd和mysqld等)的启动(start)、停止(stop)和重启(restart)等动作提供了一个统一的管理。现在,Linux程序员已经越来越偏向于使用service脚本来管理服务器程序了。

目标主机名及其对应的IP地址:Linux将目标主机名及其对应的IP地址存储在/etc/hosts配置文件,如果程序在/etc/hosts文件中未找到目标机器名对应的IP地址,它将求助于DNS服务。如下:

127.0.0.1 localhost
192.168.1.109 Kongming20
192.168.1.108 ernest-laptop

用户可以通过修改/etc/host.conf文件来自定义系统解析主机名的方法和顺序(一般是先访问本地件/etc/hosts,再访问DNS服务)

1.6 其他

物理层与数据链路层:

  • 物理层负责将数据从比特流的形式转换为电流或光信号,并在发送端和接收端之间传递这些信号。
  • 数据链路层负责在两台计算机之间传递数据帧,并在数据帧中加入或删除适当的错误检测和控制信息。

网卡:
网卡(也称为网络接口卡,NIC)是一种硬件设备,它负责在计算机和网络之间传递数据。网卡通常实现了OSI参考模型的物理层和数据链路层的功能。但是,网卡并不实现网络协议中的其他层。【注】一般来说,数据链路层是由操作系统的内核实现的,而不是由网卡实现的。但是,有些网卡也可能包含数据链路层功能,这取决于网卡的设计。
网卡通过将网线中的电流转换为二进制信息来实现数据的传输(属于物理层)。网卡内部包含了许多电子器件,如晶体管、集成电路(IC)和微处理器等,这些器件可以将电流转换为二进制信息。网卡还需要安装网卡驱动软件,才能在计算机中正常工作。

网络协议中的除了物理层以外的其他层都是使用软件而非硬件实现的,所以都可以通过c语言调用这些已经被操作系统内核或网卡实现的协议。socket就是系统内核向应用层提供用于操作低层协议的api,socket一般有两个功能:1.应用层与内核收/发缓冲区的数据复制 2.修改内核中各层协议的某些头部信息或其他数据结构,从而精细地控制底层通信的行为:
在OSI参考模型中,物理层是最底层的一层,负责将数据从比特流的形式转换为电流或光信号,并在发送端和接收端之间传递这些信号。这是一个硬件过程,因此物理层是使用硬件实现的。
除了物理层以外,网络协议中的其他层都是使用软件实现的。

网卡驱动程序:
网卡驱动程序:操作系统要求网卡产商提供相应的接口,以便于操作系统调用。提供这些接口的程序称为驱动程序。进一步的解释如下:
网卡驱动程序是负责连接计算机的操作系统和网卡之间的接口的软件。它向操作系统提供了软件接口(API),使得操作系统能够直接与网卡通信。这些接口包括驱动程序和库函数等。

  • 驱动程序是网卡驱动程序的主要部分,负责管理网卡的底层操作,包括配置网卡、接收和发送数据、控制网卡的功能等。
  • 库函数是网卡驱动程序的辅助部分,提供了一组供程序员使用的函数。库函数常用于封装一些复杂的功能,

比如网卡驱动程序可能向操作系统提供ARP协议的实现接口,当然如果网卡驱动程序不能提供完整的接口,那么操作系统也可以使用其他方法来实现ARP协议。例如,操作系统可以通过使用其他的驱动程序或协议栈来实现ARP协议。

应用程序使用TCP发送数据时,一般使用会将数据拷贝到内核缓冲区,这样做的原因如下:
在内核程序中直接访问用户空间的数据存在一些风险。用户空间的数据可能被破坏,或者可能会导致安全漏洞的产生。为了避免这些风险,内核程序通常会将需要访问的用户空间的数据复制到内核空间,然后在内核空间中对数据进行操作。当然,数据拷贝这个过程也是有代价的。拷贝数据需要时间,而且会占用内存空间。

默认网关:默认网关指的是当前主机中的路由表无法找到对应的路由项,此时会将数据转发到默认网关。在家庭中,默认网关一般就是家里的路由器。

ICMP报文:

ip协议编程 python tcp tcpip协议代码_客户端_07

ICMP报文是放在IP报文的数据部分的。ICMP报文用于查询目标是否可达、告诉对方需要去访问哪个主机才能拿到数据(重定向)

问题:
【问题】有了mac地址为什么还要有IP地址,有了IP地址为什么还要有mac地址。
这是因为网络协议分层的原因,但是为什么要分成网络层和数据链路层,而且两层都有地址?
为什么需要分为数据链路层和网络层?

【问题】什么是网关?默认要发送的地址?

【问题】PPP不是基于Ethernet的吗
PPP协议与IP协议是什么关系,
一般linux中输入ifconfig就可以看见ppp和eth类型的端口