Windows 实现TCP/IP 协议也是建立在上一篇博客的OSI 基础之上的。

用户态是由ws2_32.dll 和一些其他服务提供者的 dll 共同实现,当中ws2_32.dll 是一个框架。能够容纳非常多的服务提供者,这些服务提供者事实上就是各种协议的实现者,如比較常见的有 TCP/IP 协议,IPX 协议。而 TCP/IP 协议的服务实现是由 msafd.dll 和 mswsock.dll 来完毕。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2,该注冊表下记录了协议(服务)及其一些其他的信息。

就 TCP/IP 而言。我们普遍会使用 posix标准的 socket 接口来完毕我们应用程序的功能,这样要想完毕跨平台的代码就会比較方便。

在上一篇文章中,我们知道,tcp/ip 协议的用户态部分由msafd.dll 完毕,它与内核部分的 afd.sys 交互来实现 socket 接口的系统调用。然后 afd.sys 完毕 socket 的一些机制。而且和 tcpip.sys 驱动程序交互,总结一下例如以下。

1.      Msafd.dll : socket 接口的用户态部分。与afd.sys 通信。

2.      Afd.sys  : socket 接口的内核态部分。满足 msafd.dll的调用,向下与 tcpip.sys 通信。

3.      Tcpip.sys : tcp/ip 协议的主要实现部分,满足afd.sys 的调用,向下与小port网卡驱动通过 IRP通信。

4.      socket 的概念是在 msafd.dll和 afd.sys 中才有的,它们两个实现了 socket 的用户态和内核态部分。它们的下层是传输层(TDI)层,TDI 层完毕了 TCP, UDP, RawIp的机制。在 TDI 层中。仅仅有地址对象,连接对象,控制通道的概念。TDI 的下层是网络层(IP 层),在 IP 层中,仅仅有 Packet 的概念,收到数据时,通过 IP 包中的标识。知道要提交给 TCP 或 UDP 等处理。

TDI 层和 IP 层都由 tcpip.sys
来实现。

知道上面的概念后。就有了比較清晰的结构,当然驱动和设备的管理由 IO 管理器来管理,但tcpip 协议族却没实用常规设备栈的方式来处理数据包,afd.sys 与 tcpip.sys 以及 tcpip.sys 与 miniport 驱动之间都是由发送 IRP 来实现。这也使中间过滤层驱动的实现稍微复杂,这里且不谈。

那么我们把上面零散的概念串起来,看看从普通的 socket 接口到数据终于由网卡发出的整个过程。

Socket :

Ws2_32.dll 载入时会依据注冊表初始化服务提供者,服务者会告知自己支持的地址族,socket 类型,和协议类型。当我们调用socket(AF_INET, SOCK_DGRAM, IPPROT_UDP) 来创建一个 UDP 类型的套接字的时候,依据传入的參数,会定位到 msafd.dll 这个服务提供者,并会调用对应的 socket 创建接口,它会打开设备 \Device\Afd\EndPoint ,因为 afd.sys 创建了一个 \Device\Afd 设备,所以一个 IRP_MJ_CREATE
的 IRP 便会发送到 afd.sys 驱动的创建函数,它会创建一个FAD_FCB 结构体来表示这个套接字,而且记录下 FileObject,并返回。

Bind :

要想接收数据包。我们会把 socket 绑定到本地的一个IP-Port 对,就是调用 Bind 接口,msafd.dll 会通过一个控制消息。次功能号为 IOCTL_AFD_BIND。此时afd.sys 会接着依据上面 FCB 记录的设备名打开对应的 \Device\UDP 设备,并把输入參数标识为是一个传输层的地址,那么 tcpip.sys 会创建接口就会创建一个地址对象来表示这次绑定。当然还会分配对应的port信息。

Connect:

假设是 TCP,还须要连接到对方的socket。与 Bind 类似。它也会依据 FCB 记录的设备名打开对应的设备,并把输入參数标识为是一个连接对象,tcpip.sys 会创建一个连接对象来表示这次连接。

事实上在 TDI 层,另一种叫做控制通道。当其他驱动想得到 TDI 层的一些信息,如当前的 TCP或 UDP 连接有哪些,那么它会直接打开 \Device\TCP 等设备,因为没有传入參数,那么 tcpip.sys 则会创建一个控制通道。TDI 层这些对象的标识都会保存在与之相应的 FileObject->FsContext2 里,以便后来区分。

当前面准备工作做好后。我们就来看数据的接收和发送。

SendTo:

由 msafd.dll 发送一个 IOCTL_AFD_SEND_DATAGRAM到 afd.sys 。afd.sys 创建一个主功能号为 IRP_MJ_INTERNAL_DEVICE_CONTROL ,次功能号为 TDI_SEND_DATAGRAM的 IRP 到 tcpip.sys,tcpip.sys 调用相就的 UDPSendDatagram。组装一个 UDP 包,最后通过 IpSendDatagram 到协议层,然后由对应的小port驱动发送出去。

RecvFrom:

接收数据略微复杂一点,接收数据都是由afd.sys 驱动发送一个次功能号为 TDI_RECEIVE_DATAGRAM (afd.sys 与 tcpip.sys 的传输层都是以 IRP_MJ_INTERNAL_DEVICE_CONTROL 为主功能号)的 IRP 到 TDI 层。而 TDI 层都是以接收请求的形式来挂在地址对象的接收请求(DATAGRAM_RECEIVE_REQUEST)队列中,在地址对象创建的时候会创建这个队列。那么什么时候这个请求会被满足呢,这要从网卡接到数据说起。当网卡接收到数据时。协议驱动也会收到这个数据,普通情况下仅仅有能处理这个协议的驱动才会去处理这个包。此时就会进行到
tcpip.sys 的协议部分,即 IP 协议,tcpip.sys 依据对应的标识,确定是 IP 包,由于 tcpip.sys 还完毕了 ARP 包的处理,最后会上交到 Ipv4 的处理流程。它会调用ProcessFragment ->IpDispatchProtocol ,IpDispatchProtocl 会区分出是什么包,假设是 UDP 包,由会调用UDPReceive ,并进一步依据地址对象链表来找到匹配的地址对象。DGDeliverData 来交付数据。它会查看对址对象的接收请求队列中是否有请求。假设没有,则查看是否注冊了接收数据的处理过程,假设也没有注冊。那么就会丢掉这个包。这就是
UDP 不可靠的一个原因。

那么有人就会有疑问,我们假设调用完 Bind 之后,还没来得及调用 RecvFrom ,那么。接收到的包不就丢了么。事实上,在调用 Bind 之后。就会立即发送一个接收请求到队列中。也就避免了这样的情况的发生。

这仅仅是整个过程的导火索。在 Bind 里面它是通过调用TdiReceiveDatagram 来投递一个接收请求的,它会创建一个TDI_RECEIVE_DATAGRAM 的 IRP。并为这个 IRP 设置一个完毕例程PacketSocketRecvComplete,
tcpip.sys 会响应这个 IRP,并在对应的地址对象的接收请求队列中插入一个请求,并设置这个请求的完毕函数为DGReceiveComplete。用户完毕函数为DispDataRequestComplete。 当通过 DGDeliverData 交付数据时,假设队列中有请求,那么就去满足这个请求,拷贝数据到与这个请求对应的缓冲中,当调用请求的完毕函数 DGReceiveComplete,它会调用用户完毕函数 DispDataRequestComplete,DiapDataRequestComplete会完毕这个
IRP,那么 IRP 的完毕例程PacketSocketRecvComplete 就会得到调用了。 在 PacketSocketRecvComplete中(该函数在 afd.sys 中)要做的工作先暂停一下。回到 RecvFrom 的调用。在 RecvFrom 向下直到 afd.sys 层,它并不会直接发送 IRP 到 tcpip.sys 中去请求接收数据,假设 FCB->DatagramList 中没有数据。它会把 msafd.dll 下发的这个 IRP 放到 FCB->PendingIrpList 中,并挂起,所以到
tcpip.sys 的请求都是由在 Bind 最后发送的那个导火索引起。回到 PacketSocketRecvComplete 中。它会从FCB->PendingIrpList 中摘掉一个 IRP 并插入一个数据包到 FCB->DatagramList 中,最后完毕这个 IRP。那么 RecvFrom 下发的这个 IRP 就完毕了。

最后它又调用TdiReceiveDatagram 来投递一个接收请求,然后周而复始。

一个 UDP Socket的大致过程就到此为止了。

TCP/IP 在 Windows 下的实现的更多相关文章

  1. CentOS7设置静态IP以及windows下ping不通虚拟机、虚拟机ping不通外网解决方案

    问题:CentOS7安装完成后默认使用的是动态IP,当你每次重新启动CentOS7后,它的IP地址都不一样.一般我们都是使用远程连接工具连接CentOS7进行操作,如果每次IP都不一样,系统启动后,每 ...

  2. 同一个目标ip在windows下使用tracert正常但是在linux下使用traceroute中间节点不显示?tracert与traceroute原理与抓包分析

    针对第一个问题先说结论 windows的tracert是使用icmp来探路,linux的traceroute是使用udp探测,如果想达到和windows下一个效果,建议使用-I参数或mtr 下面是原理 ...

  3. Windows下主机名和IP映射设置

    如果需要添加域名和IP的对应关系可以在以下地方进行修改. 打开系统目录:c:/windows/system32/drivers/etc找到hosts文件,打开hosts文件并在最后面添加一条记录 例如 ...

  4. Windows计算机重置TCP / IP

    传输控制协议 (TCP / IP)是Internet上使用的通信协议. 在Windows的早期版本中,TCP / IP是一个单独的可选组件,可以像其他任何协议一样删除或添加. 早期版本中,从Windo ...

  5. Windows 下单机最大TCP连接数

    在做Socket 编程时,我们经常会要问,单机最多可以建立多少个 TCP 连接,本文将介绍如何调整系统参数来调整单机的最大TCP连接数. Windows 下单机的TCP连接数有多个参数共同决定,下面一 ...

  6. TCP/IP 某些最常见的错误原因码 (errno)列表

    对于在基于 UNIX 的环境中的 TCP/IP 用户,下表列出了某些最常见的错误原因码 (errno).它不是完整的错误列表.可以在文件 /usr/include/sys/errno.h 中找到 Er ...

  7. TCP/IP,http,socket,长连接,短连接

    TCP/IP TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在传输层中有TCP协议与UDP协议. 在应 ...

  8. windows 下odoo 不同版本安装运行问题

    在开发测试不同版本odoo时,总会遇到在同一浏览器下运行出错的状况.虽然可以把不同版本分属不同的端口,但是登录标识会入写用户本地浏览器cookie,由于cookie只匹配域名及路径但是不区分端口, 所 ...

  9. TCP/IP 最常见的错误原因码 (errno)列表

    对于在基于 UNIX 的环境中的 TCP/IP 用户,下表列出了某些最常见的错误原因码 (errno).它不是完整的错误列表.可以在文件 /usr/include/sys/errno.h 中找到 Er ...

随机推荐

  1. MySQL-安全对调两个表名

    我们想要的是同时完成表名对调,如果是先后的对掉,用RENAME的话可能会导致有些数据写入失败,那怎么办? 其实也不难,从MySQL手册里就能找到方法,那就是:同时锁定2个表,不允许写入,然后对调表名. ...

  2. 【转帖】云平台发现服务构建:为什么不使用ZooKeeper

    http://www.chinacloud.cn/show.aspx?id=19979&cid=16 [日期:2015-04-29] 来源:dockerone   作者: [字体:大 中 小] ...

  3. Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们

    转载自:http://www.open-open.com/lib/view/open1436360508098.html

  4. CXF学习笔记 之 “注解”

    @WebService 1.serviceName: 对外发布的服务名,指定 Web Service 的服务名称:wsdl:service.缺省值为 Java 类的简单名称 + Service.(字符 ...

  5. Android 资源保护问题——探索

    apk文件使用解压工具就能看到drawable等资源,但是有些游戏中的图片资源却是无法看到的. 这个问题探索了许久…… [1]图片资源不放置在drawable文件下,放在assets中(但是解压apk ...

  6. 06、Windows 10 技术预览

    随着 Windows 10 发布的,未来 Windows 平台都是统一开发模型,可以只写一个 Appx 包,就可以同时部署到 Windows/ Windowsw Phone/ Tablet /xbox ...

  7. tomcat开启https协议

    1.在tomcat的conf/server.xml 中配置 <Connector port="443" protocol="org.apache.coyote.ht ...

  8. GPIO 输入—按键检测

    这里要用到一定的模电知识.电容两端电压不能突变,电感两端电流不能突变.这里利用了电容的放电延时实现硬件消抖.按键按下会有抖动,波形有毛刺,使得高低电平显现不明显,而按键按下时,电容放电一下,马上又被充 ...

  9. javac编译出来的程序运行报错“错误: 找不到或无法加载主类”

    使用javac编译java文件生成class文件 >javac HelloWorld.java执行class文件>java HelloWorld 原因: 含有包名 解决办法: 按照包的结构 ...

  10. 悦铃文件必须是CCITT A_Law格式的,且没有被压缩

    最近在给公司弄来电彩铃,用的是电信的“悦铃”业务,办理过程不想多说了..给了我个网址和账号让我登录,登录界面惨不忍睹,感觉电信根本没有要宣传这项业务的意思,像是粗制滥造外包赶工做出来的.. 当然这不是 ...