使用NDIS驱动监测以太网络活动

2023-10-27

 
本论文提供了NDIS的基本的理解,应用程序如何与驱动程序交互,发挥驱动程序最佳性能。本论文也说明了使用例子驱动(PACKET.SYS)监测以太网的应用程序。本论文不是帮助程序员开发网络驱动而是帮助他使用这样的驱动。
 
引言
       从计算机被发明以来,对大多数编程人员来说,编写设备驱动都是令人着迷的。开发驱动是为了满足特定应用的需要。这个目的导致大量不同种类的驱动被开发出来,例如打印机驱动,文件系统驱动等。此外,特定的应用与特定种类的驱动相对应。随着Internet的到来,编写网络驱动成为驱动开发的核心。 为帮助开发网络设备驱动,微软为Windows NT操作系统开发了网络设备接口规范(NDIS)库
1、简介
1、1 什么是设备驱动
设备驱动是操作系统和输入输出设备间的粘合剂。驱动负责将操作系统的请求传输,转化为特定物理设备控制器能够理解的命令。
1、2 什么是网络接口卡
       网络接口卡(NIC)是一个物理设备,类似于网关,通过它,网络中的任何设备都可以发送和接收数据帧。不同网络中,网络接口卡的名称是不同的。例如,以太网中称为以太网接口卡,令牌环网中称为令牌环网接口卡,等等。
 
 
1、3 什么是网络驱动接口规范(NDIS)
       网络驱动接口规范描述了一个接口,通过这个接口,一个或多个NIC驱动可以与一个或多个覆盖在其上的 协议驱动和操作系统通信。NDIS为网络驱动开发提供了完整的抽象。对所有外部功能来说,NIC驱动都依赖于NDIS,这些功能包括 与协议驱动的通信,注册,截获NIC硬件中断,与下层的NIC的通信
1、4 为什么需要NDIS
       NDIS库(NDIS.SYS)为NIC驱动的编写提供了完整的抽象接口。库输出所有可以在NIC驱动开发中使用的NT内核模式函数。这个库负责响应所有下层NIC驱动特定的任务,保持绑定和状态信息。
2、特点与使用
2、1基本特征
l        单NIC驱动
l        NDIS库
l        均衡处理器支持
l        多协议驱动支持
l        管理
l        发送类型
l        操作标志
l        全双工操作
l        ARCNET和WAN支持
2、2 驱动类型
       Windows NT支持三种类型的驱动:
l        网络接口卡驱动(NIC)
l        中间协议驱动
l        上层协议驱动
 
2、2、1 网络接口卡驱动
NIC驱动管理网络接口卡(NIC)。NIC驱动接口在下边界直接控制硬件(NIC),在上边界提供上层驱动访问的接口:
l        发送和接收包
l        重置NIC(Reset)
l        停止NIC
l        查询NIC
l        设置NIC操作特性
NIC驱动的两种类型
l         微端口驱动:微端口驱动应用于管理NIC硬件特殊操作,包括在NIC上发送和接收数据。微端口驱动不能直接呼叫系统例程,只能呼叫NDIS提供的函数。
l         完全NIC驱动:完全NIC驱动不仅管理硬件而且管理NDIS完成的的操作系统特定任务。完全NIC驱动必须保持接收数据的绑定信息。
2、2、2 中间协议驱动
       中间协议驱动接口位于上层协议驱动和微端口驱动之间。对于上层传输驱动程序来书,中间驱动看起来像是微端口驱动。对微端口驱动来说,看起来像是协议驱动。使用中间协议驱动的主要是为了传输媒质,存在于对于传输驱动未知和微端口管理之间的新的媒质类型。
2、2、3 上层协议驱动
       上层协议驱动应用于TDI接口或其它向用户提供服务的特定应用接口。例如 驱动调用NDIS分配包,向包中烤贝数据和向底层发送包。它也在它的底层提供协议接口,来接收下层驱动发送来的包
2、3 应用程序和驱动的交互
       在Windows NT下的所有驱动必须具有DriverEntry函数,作为驱动的进入点。驱动中其它的函数是通过DriverEntry函数声明的。应用程序调用函数如CreateFile,ReadFile等,会由NT I/O管理器生成相应IRP(输入/输出请求包)。
在NT下,几乎所有I/O操作都是包驱动的。每个I/O操作由工作顺序来描述,工作顺序告诉驱动做什么和通过I/O子系统追踪请求的过程。这些工作顺序通过一个称为I/O请求包(IRP)的数据结构的形式给出。 这个IRP为完成特定操作按顺序调用驱动中的进入点
 
3、在以太网中使用NDIS监测数据包 
              本节以一个NDIS驱动例子说明监测以太网。
              Packet 监测捕获在局域网中传输的的所有包。这是由以太网的广播特性决定的。通过以太网发送的包是向网络中所有计算机广播的。每台计算机上的以太网卡检查每个包的目的是否是它自己,如果是则接收,不是则拒绝。利用这个基本的功能来捕获网络中传输的所有数据包。 以太网卡可以被设置工作于好几种模式,可以设置于期望中的捕获数据包的模式。一个这样的可以工作于期望的例子驱动程序PACKET.SYS来自微软Windows NT 设备开发工具(DDK)。
3、1 以太网接口卡的模式
       以太网接口卡可以被设置的模式如下:
l        广播
l        多播
l        直接
l        混杂
       广播:数据帧可以发向网络中所有计算机, 这种帧的目的地址是0xffffff,也称为帧的广播地址。任何设置为广播模式的网卡都接收目的地址为广播地址的数据帧。通常所有的网卡被配置为接收广播帧。
       多播:发往一组计算机的帧称为多播帧, 使用特定的多播地址作为目的地址。这些计算机的组构成了多播组。这样,多播组里的任何一个成员计算机将会接收具有多播目的地址的帧。尽管网卡可以不是一个多播组里成员,但可以配置为多播模式,这样就可以接收所有多播帧。
       直接发往特定计算机的帧具有特定计算机的物理地址(以太网地址)的目的地址。具有特定物理地址的计算机将接收特定的帧,丢弃其它的帧。网卡可以设置为仅仅接收直接帧。
       混杂:设置为这个模式的网卡接收所有收到的数据包。这个模式具有的以太网的广播特性是包监测应用程序的关键。
       PACKET.SYS例子驱动可以将网卡设置为以上提到的模式。 应用可以利用PACKET.SYS将网卡设置为混杂模式,以便于捕获网络中传输的所有数据包
3、2 关于例子驱动PACKET.SYS
       例子PACKET.SYS来自NT DDK。这个驱动可以将网卡设置为任何期望的模式,并且允许应用程序通过网络发送和接收数据包。除驱动程序的sys文件之外,还提供了一个DLL(PACKET32.DLL),通过此DLL应用程序可以和驱动程序通信。
3、2、1应用程序如何同PACKET.SYS通信
应用程序调用DLL中的函数,这些函数依次调用PACKET.SYS驱动中的进入点。驱动程序利用NDIS.SYS输出函数与网络接口卡通信。
 
 
3、2、2 如何在应用程序中使用PACKET32.DLL
应用程序使用驱动读取所有到达NIC的数据包。以下示例说明了这个过程。
在上述过程中使用的结构体定义如下:
 
typedef struct _ADAPTER 
{ 
	HANDLE     hFile;								// 保存由CreateFile方法返回的句柄
	TCHAR      SymbolicLink[MAX_LINK_NAME_LENGTH];	// 保存驱动的符号链接名
} ADAPTER, *LPADAPTER;

typedef struct _PACKET 
{
	HANDLE       hEvent;	// 保存和适配器对象相应的事件句柄
	OVERLAPPED   OverLapped;// 包含异步输入输出信息的OVERLAPPED结构
	PVOID        Buffer;	// 包含发送和接收数据的缓冲区
	UINT         Length;	// 缓冲区长度
} PACKET, *LPPACKET;

typedef struct _CONTROL_BLOCK 
{
    LPADAPTER   hFile;				// 指向适配器对象的指针
    HANDLE      hEvent;				// 保存事件句柄   
    // Name of the driver as registered in the registry.
    TCHAR       AdapterName[64];	// 注册表中注册的驱动名         
    HANDLE      hMem;				// 保存接收数据包的缓冲区
    LPBYTE      lpMem;
    HGLOBAL     hMem2;				//  保存发送数据包的缓冲区
    LPBYTE      lpMem2;        
	ULONG       PacketLength;		// 包长度 
	ULONG       LastReadSize;		// 最后读取的包大小   
	UINT        BufferSize;			// 缓冲区长度
} CONTROL_BLOCK, *PCONTROL_BLOCK;

 应用程序开始
 
CONTROL_BLOCK cbAdapter; 
ULONG NameLength=64;

// 得到驱动在注册表中注册的名字
PacketGetAdapterNames(CbAdapter.AdapterName,&NameLength);

CbAdapter.BufferSize=1514; // 保留1514字节,最大帧长度
 
// 分配并锁定内存缓冲区用于发送和接收数据包
CbAdapter.hMem=GlobalAlloc(GPTR, 1514); 
CbAdapter.lpMem=(LPBYTE)GlobalLock(CbAdapter.hMem);
CbAdapter.hMem2=GlobalAlloc(GPTR,1514);
CbAdapter.lpMem2=(LPBYTE)GlobalLock(CbAdapter.hMem2);
 
// 打开优先的适配器用于接收数据,
// 函数依次调用CreateFile方法,调用驱动程序中相应的进入点。为随后的读写操作打开适配器
CbAdapter.hFile=(ADAPTER*)PacketOpenAdapter(CbAdapter.AdapterName);
 
// 打开适配器域
if (CbAdapter.hFile = = NULL) 
{
	AfxMessageBox("Open Adapter failed");
}
 

// 此Packet对象用于从网络上接收所有数据的包
PVOID Packet;
// 设置过滤条件为混杂(非选择)模式
// 此函数依次呼叫DeviceIoControl方法,用来设置网卡工作于期望的模式。
Filter = NDIS_PACKET_TYPE_PROMISCUOUS;
 
// 设置网卡为混杂模式
PacketSetFilter(CbAdapter.hFile, Filter);
 
// 分配缓冲区用于接收数据包
Packet=PacketAllocatePacket(CbAdapter.hFile);
 
// 初始化接收数据包缓冲区
if(Packet != NULL)
{
	PacketInitPacket((PACKET *)Packet,(char *)pdData[nCurrentWriteLocation].pData,1514);

	// 从驱动中读取数据包
	// 此函数依次调用ReadFile方法来读取通过EIC从网络上收到的数据
	PacketReceivePacket(CbAdapter.hFile,(PACKET *)Packet,TRUE,&pdData[nCurrentWriteLocation].nPacketLength ); 

上述插入代码描述了应用程序如何使用 PACKET.SYS驱动设置以太网接口卡为混杂模式,用来捕获所有网络上的数据包
 
以上代码清晰的描述了应用程序利用例子驱动PACKET.SYS将EIC设置为期望的模式,发送和接收数据包。
3、2、3 应用程序中使用的在Packet32.dll中定义的函数如下:
下面的函数PacketGetAdapterNames返回注册表中注册的驱动名
ULONG
PacketGetAdapterNames(
    PTSTR   pStr,
    PULONG BufferSize
   )
{
    HKEY       SystemKey;
    HKEY       ControlSetKey;
    HKEY       ServicesKey;
    HKEY       NdisPerfKey;
    HKEY       LinkageKey;
    LONG       Status;
    DWORD      RegType;
    // Open the Key HKEY_LOCAL_MACHINE,打开HKEY_LOCAL_MACHINE键值
    Status=RegOpenKeyEx(
               HKEY_LOCAL_MACHINE,
               TEXT("SYSTEM"),
               0,
               KEY_READ,
               &SystemKey
               );
    if (Status == ERROR_SUCCESS) {
       // Open the key currentcontrolset 打开currentcontrolset键值
        Status=RegOpenKeyEx(
                   SystemKey,
                   TEXT("CurrentControlSet"),
                   0,
                   KEY_READ,
                   &ControlSetKey
                   );
        if (Status == ERROR_SUCCESS) {
            // Open the key Services打开Services键值
            Status=RegOpenKeyEx(
                       ControlSetKey,
                       TEXT("Services"),
                       0,
                       KEY_READ,
                       &ServicesKey
                       );
            if (Status == ERROR_SUCCESS) {
        // Open the key Packet. 打开Packet键值
        Status=RegOpenKeyEx(
                           ServicesKey,
                           TEXT("Packet"),
                           0,
                           KEY_READ,
                           &NdisPerfKey
                           );
                if (Status == ERROR_SUCCESS) {
            // Open the key Linkage.打开Linkage键值
                    Status=RegOpenKeyEx(
                               NdisPerfKey,
                               TEXT("Linkage"),
                               0,
                               KEY_READ,
                               &LinkageKey
                               );
                    if (Status == ERROR_SUCCESS) {
                   // Open the key Export.      
                        Status=RegQueryValueEx(
                                   LinkageKey,
                                   TEXT("Export"),
                                   NULL,
                                   &RegType,
                                   (LPBYTE)pStr,
                                   BufferSize
                                   );
             // Close all the keys that have been opened so far.关闭已打开的所有键值
                        RegCloseKey(LinkageKey);
                    }
                    RegCloseKey(NdisPerfKey);
                }
                RegCloseKey(ServicesKey);
            }
            RegCloseKey(ControlSetKey);
        }
        RegCloseKey(SystemKey);
    }
    return Status;
}

 
PVOID PacketOpenAdapter(LPTSTR   AdapterName)
{
	LPADAPTER lpAdapter;
	BOOLEAN    Result;
	 
	ODS("Packet32: PacketOpenAdapter/n");

	// 为适配器对象分配全局内存
	lpAdapter=(LPADAPTER)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,sizeof(ADAPTER));
	if (lpAdapter==NULL) 
	{
		 ODS("Packet32: PacketOpenAdapter GlobalAlloc Failed/n");
		 return NULL;
	}
 
	//  将名字拷贝到符号链接名
	wsprintf(lpAdapter->SymbolicLink, TEXT(".//%s%s"), DOSNAMEPREFIX, &AdapterName[8] );
 
	// Defines an MS-DOS name for the device.
	Result=DefineDosDevice(DDD_RAW_TARGET_PATH,&lpAdapter->SymbolicLink[4], AdapterName);
 
	if (Result) 
	{
		// Creates and returns a file handle for the specified device. 为特定设备创建并返回文件句柄
		lpAdapter->hFile=CreateFile(lpAdapter->SymbolicLink,GENERIC_WRITE | GENERIC_READ,0,NULL,CREATE_ALWAYS,FILE_FLAG_OVERLAPPED,0);
 
		if (lpAdapter->hFile != INVALID_HANDLE_VALUE) 
		{
			return lpAdapter;
		}
	}
	ODS("Packet32: PacketOpenAdapter Could not open adapter /n");
 
	GlobalFreePtr(lpAdapter );
	return NULL;
 }   

函数PacketOpenAdapter为设备定义了一个新的DOS设备名,调用CreaetFile方法来创建并打开通信设备,得到指向设备的句柄。应用程序预先调用此函数来发送和接收数据包。CreateFile方法 调用驱动中指定为IRP_MJ_CREATE的进入点,此进入点调用NDIS库,输出NdisOpenAdapter函数打开适配器
 
以下的 函数PacketAllocatePacket为packet对象分配内存,调用CreateEvent函数来建立特定文件句柄的事件
PVOID PacketAllocatePacket(LPADAPTER   AdapterObject)
{
	LPPACKET    lpPacket;
    //  为Packet对象分配内存
    lpPacket=(LPPACKET)GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,sizeof(PACKET));
    if (lpPacket==NULL) 
	{
        ODS("Packet32: PacketAllocateSendPacket: GlobalAlloc Failed/n");
        return NULL;
    }
    // 操作结束时建立事件对象 
    lpPacket->OverLapped.hEvent=CreateEvent( NULL,FALSE,FALSE, NULL);
    if (lpPacket->OverLapped.hEvent==NULL)
	{
        ODS("Packet32: PacketAllocateSendPacket: CreateEvent Failed/n");
        GlobalFreePtr(lpPacket);
        return NULL;
    }
    return lpPacket;
}

PacketInitPacket函数设置packet对象缓冲区
VOID PacketInitPacket(LPPACKET    lpPacket,PVOID       Buffer,UINT        Length)
{
    // 设置packet对象缓冲区到缓冲区
    lpPacket->Buffer=Buffer;
	// 设置packet对象缓冲区长度到缓冲区长度
    lpPacket->Length=Length;
}

PacketReceivePacket函数调用驱动中适当的进入点来读取网络中获得的数据包,放入声明的缓冲区。ReadFile方法使用在CreateFile方法中获得的句柄来完成此操作。
BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync,PULONG BytesReceived)
{
    BOOLEAN      Result; 
	// 设置偏移量为0
    lpPacket->OverLapped.Offset=0;
    lpPacket->OverLapped.OffsetHigh=0;
    if (!ResetEvent(lpPacket->OverLapped.hEvent)) 
	{
        return FALSE;
    }
    // 调用ReadFile来读取数据包
    Result=ReadFile(AdapterObject->hFile,lpPacket->Buffer, lpPacket->Length, BytesReceived, &lpPacket->OverLapped);
    if (Sync) 
	{
        // They want to wait
        Result=GetOverlappedResult(AdapterObject->hFile,&lpPacket->OverLapped,BytesReceived, TRUE );
    }
    else
	{
        // They don't want to wait, they will call PacketWaitPacket to get
        // The real result
        //不等待,调用PacketWaitPacket得到真实值
        Result = TRUE;
    }
    return Result;
}

使用驱动PACKET.SYS监测有许多例程是有用的。注意,没有提供更多的应用细节。
本节仅仅说明了使用NDIS例子驱动PACKET.SYS监控所有网络动作的应用程序如何编写。
 
4、如何识别HTTP请求
       上节描述了怎样从以太网中捕获所有的数据包。我们的意图是不仅仅捕获所有数据包,并且监控网络上的Internet活动,这意味着 我们应该识别携带HTTP请求的数据包。这需要 以太网帧结构和IP本节描述了TCP/IP包的标识,HTTP请求的标识,帧中的URL信息的获取
 
4、1  网络数据流
       为了在分层的网络中传输数据,从应用程序传输数据到协议栈中中相应的协议。之后,此协议处理完数据之后,将数据传向栈中的下一个协议。在数据穿越每一层协议的同时,协议栈上相应协议为了栈中下一层协议,将数据封装起来。因此, 封装就是一个将数据存储成协议栈中更低层协议要求的格式的过程
因此,我们可以看出,应用程序模块封装从用户应用消息传来的数据;TCP模块封装应用数据,增加TCP头并且发往下一层;当数据传向网络栈中IP模块时,IP模块将TCP段格式化为IP报文或称为包;以太网驱动将IP模块传来的数据格式化并将数据放入以太网帧中。
这就解释了帧中如何封装IP报文,IP包如何封装TCP/UDP数据。为了识别HTTP请求, 首先,我们应该识别包为TCP/IP包,然后检查此包是否是TCP包,最后识别此包是否HTTP请求
 
4、2 识别TCP/IP包
为识别一个数据包是TCP/IP包,我们应该首先看看以太网的帧结构。
以太网的帧数据包含了14字节头
       以太网帧头的域是:
l        目的地址(6字节)
l        源地址(6字节)
l        帧类型(2字节)
       就像名字的含义,目的地址域说明了以太网帧的目的。类似的,源地址说明了帧的源。帧的类型域是我们关心的。这个域标识了帧的协议。
       如果包是有效的IP包,则帧类型域(第13和第14字节)将会是080016
 
 
 
4、3 识别TCP包
       识别了TCP/IP包之后,下一个任务是识别出包是否是TCP包或其他包。 因为HTTP请求仅仅通过TCP请求来传输,所以我们可以忽略其他包。为确定包是否是一个TCP包,我们 必须分析IP头。IP头如下所示:
IP头的重要域是:
l        头部长度(4位)和版本号(4位)
l        包总长度(2字节)
l        数据在传输层上使用的协议类型。下表说明了IP包的通用TCP/IP协议类型域
Protocol
Value(Decimal)
TCP
6
UDP
17
ICMP
1
IGMP
2
l        头部校验和(2字节)
l        源IP(4字节)
l        目的IP(4字节)
       IP头中协议域的值为06说明了数据包是TCP包。
4、4  识别HTTP请求
              识别出数据包是TCP包后,我们必须识别包是否是HTTP请求包。为找出包中是否包含HTTP请求,我们必须检查TCP头,TCP头如下所示:
       TCP头中重要的域是:
l        源端口(2字节)
l        目的端口(2字节)
l        序列号(4字节)
l        标识号(4字节)
l        Hlen,保留和代码位(2字节)
l        窗口(2字节)
l        校验和(2字节)
l        紧急点(2字节)
       如上所述,TCP头中,我们最关心的域是 源和目的端口域。这些域说明了连接建立的端口。不同的TCP服务比如 HTTP,FTP等等使用特定的端口号来提供他们的服务。
       对HTTP服务来说,端口号是008010或005016。 如果源端口域包含值是80(HTTP端口号),则包就是HTTP响应包。如果目的端口域是80,则包是HTTP请求包
4、5  获取URL
       现在我们已经发现包是一个HTTP请求包了,找到包中包含的URL地址就相对简单了。
       在浏览器中键入的请求被以GET或POST请求的形式被发HTTP服务器。浏览器添加其他的与浏览器相关的信息,发往相应的HTTP服务器。完整的信息包含了封装在数据包中的请求页的URL。因此,依靠分析数据包就可以得到URL。 URL出项在数据包中的通用格式为
       GET/HTTP/1.0
       以上的URL是我们键入浏览器的任何站点默认页。如果我们请求站点的其他页或点击页面中提供的链接,然后请求页也被放入GET请求中。例如,当我们请求的页面为sample.html,则URL将会是:
GET/sample.html HTTP/1.0
       因此,通过在数据包中查找GET串,就可以获取URL请求。
 
5、 驱动性能和操作系统兼容性
       在混杂模式下,驱动捕获到达网卡的所有数据包。如果网络流量很大,就有可能不能捕获而丢包。这必须考虑在内。 驱动中使用的是NDIS版本是3.0。驱动在Windows NT下工作良好,但不能在Windows95下工作,因为Windows95仅仅支持的是NDIS是2.1版本和NDIS 3.1版本。
WINDOWS CE中使用驱动的相关问题
       Windows CE中NDIS应用是Windows NT下NDIS4.0的一个子集。为Windows NT写的驱动代码可以在Windows CE下工作,但需要考虑到一些问题。才Windows CE下,驱动会被编译为DLL,而不是Windows NT下的.SYS文件。而且,CE不支持内建的DMA和分配连续的内存块。更进一步, 为CE编写NDIS驱动,程序员必须考虑到电源管理的问题。这就是说,必须提供附加的电源管理代码。
 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用NDIS驱动监测以太网络活动 的相关文章

随机推荐

  • 国产开源新标杆!20B大模型,性能媲美Llama2-70B,单卡可推理

    明敏 发自 凹非寺量子位 公众号 QbitAI 国产新标杆 免费可商用的200亿参数大模型 来了 书生 浦语大模型 InternLM 20B版本正式发布 一举刷新国内新一代大模型开源纪录 它由上海人工智能实验室 上海AI实验室 与商汤科技联
  • MySQL入门---超详细安装及基本使用教程

    数据库技术和数据库系统 数据库技术和数据库系统已经成为计算机信息系统的核心技术和重要基础 gt gt MySQL简介 MySQL是一款单进程多线程 支持多用户 基于客户机 服务器 Client Server C S 的关系数据库管理系统 是
  • 怎么使用blender

    Blender是一款开源3D建模和动画软件 可以用来制作三维图像 动画 游戏 音频和视频 要使用Blender 需要先下载并安装该软件 安装完成后 打开Blender 您会看到一个3D空间 包含一个默认的立方体 可以通过鼠标和键盘来对立方体
  • strdup函数的用法

    函数名 strdup 功 能 将串拷贝到新建的位置处 用 法 char strdup char str 这个函数在linux的man手册里解释为 The strdup function returns a pointer toa new s
  • 推荐系统实战5——EasyRec 在DSSM召回模型中添加负采样构建CTR点击平台

    推荐系统实战5 EasyRec 在DSSM召回模型中添加负采样构建CTR点击平台 学习前言 EasyRec仓库地址 DSSM实现思路 一 DSSM整体结构解析 二 网络结构解析 1 Embedding层的构建 2 网络层的构建 3 相似度计
  • 【毕业设计】深度学习验证码识别算法研究与实现 - python 机器视觉

    文章目录 0 简介 1 数据收集 2 识别过程 3 网络构建 4 数据读取 5 模型训练 6 加入Dropout层 7 数据增强 8 迁移学习 9 结果 9 最后 0 简介 Hi 大家好 这里是丹成学长的毕设系列文章 对毕设有任何疑问都可以
  • 测试老鸟总结,性能测试需求分析-性能必要性,一篇打通...

    目录 导读 前言 一 Python编程入门到精通 二 接口自动化项目实战 三 Web自动化项目实战 四 App自动化项目实战 五 一线大厂简历 六 测试开发DevOps体系 七 常用自动化测试工具 八 JMeter性能测试 九 总结 尾部小
  • java中Optional使用方法

    Optional是Java 8中提供的一个容器类 用来装载可能为空的引用 在使用Optional时 不需要检查null 可以避免NullPointerException 1 创建Optional实例 Optional可以通过工厂方法of o
  • Linux下PostgreSQL主备环境搭建和切换

    1 概念 数据库热备 数据库热备是指为主数据库的创建 维护和监控一个或多个备用数据库 它们时刻处于开机状态 同主机保持同步 当主机失灵时 可以随时启用热备数据库来代替 以保护数据不受故障 灾难 错误和崩溃的影响 流复制 streaming
  • ubuntu基于docker搭建hadoop集群【史上最详细】

    1 安装docker ubuntu 18 04安装 docker 2 拉取 ubuntu镜像 如果不指定版本号的话 默认拉取最新的ubuntu版本 docker pull ubuntu 3 创建容器 1 查看已拉取的镜像 docker im
  • 统信UOS桌面操作系统安装教程

    配置推荐 CPU 频率 2GHz 及更高的处理器 内存 推荐配置 4GB 以上 最低配置 2GB 硬盘 至少 64GB 的空闲硬盘 https www uniontech com next product desktop system ed
  • Linux系统:安装python3

    目录 一 python函数输出 1 print 函数输出 二 实验 1 安装python3 一 python函数输出 1 print 函数输出 1 概念 在Python中 使用内置的print 函数可以将结果输出到IDLE或者标准控制台上
  • 人工智能-知识图谱的进阶一

    第一部分 概论 本文主要分为三个部分 第一个部分介绍我们为什么需要知识图谱 第二个部分介绍知识图谱的相关概念及其形式化表示 最后 作一个简单的总结 并介绍该专栏后续文章会涉及的内容 一 看到的不仅仅是字符串 当你看见下面这一串文本你会联想到
  • config:fail,invalid signature

    微信开发调用JS SDK某些页面报config fail invalid signature 注意 是某些页面才会报这个错误 导致 wx config 失败 百思不得解 看了几遍文档 确认url是页面完整的url 请在当前页面alert l
  • ffprobe查看多媒体流音视频详情

    ffprobe查看多媒体流音视频详情 ffprobe是一个简单的多媒体流解析器 可以查看多媒体流音视频详情 1 ffprobe简介 ffprobe从多媒体流中收集信息 并以人机可读的方式进行打印 例如 它可以用来检查多媒体流使用的容器的格式
  • JavaScript实现简单文本编辑功能

    核心属性 方法 contentEditable html属性 用来设置 或 返回元素的内容是否可以被编辑 语法 p p HTMLElementObject contentEditable true false HTMLElementObje
  • MybatisPlus-条件查询方式及多条件构建查询(亲测)

    一 条件查询方式 取消Spring日志打印 取消SpringBoot和MyBatisPlus的log打印 二 多条件构建查询 格式一 常规格式 格式二 链式编程格式 格式三 lambda格式 推荐 格式四 lambda格式 推荐 并且 an
  • Android Jetpack介绍

    1 文档背景 本文是Jetpack介绍系列文档的开篇 是对Jetpack整体的介绍 后续会对其中的各个组件分别单独说明 2 官方简介 Jetpack 是一套库 工具和指南 可帮助开发者更轻松地编写优质应用 使用这些组件 可帮助开发者遵循最佳
  • Java学习(超详细)一

    目录 1 初识Java 1 1 Java简介 1 1 1 什么是程序 1 1 2 Java的产生 1 1 3 Java技术平台 1 1 4 生活中的程序 1 1 5 计算机中的程序 1 1 6 为什么学习Java 1 2 Java环境JDK
  • 使用NDIS驱动监测以太网络活动

    转载自 http blog csdn net ddtpower article details 656687 本论文提供了NDIS的基本的理解 应用程序如何与驱动程序交互 发挥驱动程序最佳性能 本论文也说明了使用例子驱动 PACKET SY