返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++代码实现网络Ping功能
  • 926
分享到

C++代码实现网络Ping功能

2024-04-02 19:04:59 926人浏览 薄情痞子
摘要

目录(一)main.cpp文件(二)ping.h文件(1)IP头结构体:(2)ICMP头结构体:(3)ICMP响应报文结构体:(4)Ping类及相关变量的定义:(三)ping.cpp

ping 127.0.0.1: 这个Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机。

localhost是个操作系统网络保留名,是127.0.0.1的别名。

ping www.baidu.com——对这个域名执行Ping命令,你的计算机必须先将域名转换成IP地址,通常是通过DNS服务器

(一)main.cpp文件


// ConsoleApplication3.cpp : 定义控制台应用程序的入口点。
//
//程序应用:  ping命令是向目的主机发送ICMP报文,检验本地主机和远程的目的主机是否连接
 
#include <winsock2.h>
#include <stdio.h>
#include "ping.h"
 
int main(void)
{
	CPing objPing;                 //CPing类与对象
 
	char *szDestIP = "127.0.0.1";  //字符IP地址  //127.0.0.1 这个Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机。localhost是个操作系统的网络保留名,是127.0.0.1的别名。//ping www.baidu.com——对这个域名执行Ping命令,你的计算机必须先将域名转换成IP地址,通常是通过DNS服务器。
	PingReply reply;	           //PingReply类与对象
 
	printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE);	//ping 远端IP地址,32字节的数据
	
	while (TRUE)
	{
		objPing.Ping(szDestIP, &reply);//远端IP地址不为空(NULL),就返回true表示需要响应报文。远端IP空时不需要响应报文false。
		printf("Reply from %s: bytes=%d time=%ldms TTL=%ld\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);  //字节数,时间,TTL生存时间
		Sleep(500);
	}
 
	return 0;
}

(二)ping.h文件

(1)IP头结构体:

IHL:首部长度。因为IP的头部不是定长的,所以需要这个信息进行IP包的解析,从而找到Data字段的起始点。   

另外注意这个IHL是以4个字节为单位的,所以首部实际长度是IHL*4字节。

Time to Live:生存时间,这个就是TTL了。

Data:这部分是IP包的数据,也就是ICMP的报文内容。


//1.IP头结构体:20字节
struct IPHeader
{
	BYTE m_byVerHLen;		//4位版本Version+4位首部长度IHL	1B
	BYTE m_byTOS;			//服务类型						1B=16b
	USHORT m_usTotalLen;	//总长度						2B=16b
	USHORT m_usID;			//标识							2B=16b
	USHORT m_usFlagFraGoffset; //3位标志+13位片偏移=16位	2B=16b
	BYTE m_byTTL;			//TTL	生存时间    			1B=8b
	BYTE m_byProtocol;		//协议							1B=8b   为1时表示是ICMP报文
	USHORT m_usHChecksum;	//首部检验和					2B=16b
	ULONG m_ulSrcIP;		//源IP地址						4B=32b
	ULONG m_ulDestIP;		//目的IP地址					4B=32b
};

(2)ICMP头结构体:

类型Type、代码Code、校验和、标识符、序列号、ICMP数据


//ICMP报文由首部8B和数据段组成。
//首部为定长的8个字节,前4个字节是通用部分(类型1B/代码1B/校验和2B),后4个字节随报文类型的不同有所差异。
 
//2.ICMP头结构体 (标准ICMP头为8字节)
struct ICMPHeader
{
	BYTE m_byType;		 //类型   1B   type=8表示响应请求报文,type=0表示响应应答报文。
	BYTE m_byCode;		 //代码   1B   与type组合,表示具体的信息
	USHORT m_usChecksum; //检验和 2B   整个ICMP报文的检验和,包括Type、Code、...、Data。
	USHORT m_usID;		 //标识符 2B=16bits	用于标识本进程
	USHORT m_usSeq;		 //序列号 2B=16bits	用于判断回显应答数据报。
	ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)4B   //统计ping的往返时间的做法是,在ICMP报文的Data区域写入4个字节的时间戳。在收到应答报文时,取出这个时间戳与当前的时间对比即可。
};

(3)ICMP响应报文结构体:


//3.ICMP回答报文结构体
struct PingReply
{
	USHORT m_usSeq;         //ICMP包的序列号  2B
	DWORD m_dwRoundTripTime;//时间差		  4B  (word是2字节)
	DWORD m_dwBytes;        //数据所占字节数  4B
	DWORD m_dwTTL;          //TTL生存时间     4B
};

(4)Ping类及相关变量的定义:


//类
class CPing
{
//公共变量
public:
	CPing();  //构造函数
	~CPing(); //析构函数
 
	BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);
	BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);
 
//私有变量
private:
	BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);
	USHORT CalCheckSum(USHORT *pBuffer, int nSize);//计算检验和
	ULONG GetTickCountCalibrate();  //计算毫秒级别的时间差
 
private:
	Socket m_sockRaw;			//需要监听的socket
	WSAEVENT m_event;           //网络事件对象
	USHORT m_usCurrentProcID;   //当前进程发出的报文
	char *m_szICMPData;			//ICMP(Internet Control Message Protocol,网际控制报文协议)
	BOOL m_bIsInitSucc;			//初始化成功
 
private:
	static USHORT s_usPacketSeq;  //序列号++(16位=2字节)
};

(三)ping.cpp文件


#include "ping.h"
#include <iOStream>
 
USHORT CPing::s_usPacketSeq = 0;

(1)char *m_szICMPData; BOOL m_bIsInitSucc;


//::表示类作用域。为避免不同的类有名称相同的成员而采用作用域的方式进行区分。
CPing::CPing() :m_szICMPData(NULL), m_bIsInitSucc(FALSE)
{
	WSADATA WSAData;
	//WSAStartup(MAKEWORD(2, 2), &WSAData);
	if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
	{
		printf("WSAStartup() failed: %d\n", GetLastError());  
		return;
	}
 
	m_event = WSACreateEvent();  //创建一个网络事件对象(HANDLE m_event)。   //返回一个手工重置的事件对象句柄 (HANDLE hEventObject)
	m_usCurrentProcID = (USHORT)GetCurrentProcessId();  //当前进程ID
 
	
	m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);  //创建一个监听的socket (SOCKET m_sockRaw)  //当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。
 
	if (m_sockRaw == INVALID_SOCKET) //无效套接字
	{
		std::cerr << "WSASocket() failed:" << WSAGetLastError() << std::endl;  //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。
	}
	else  //是ICMP报文,令初始化成功,为ICMP数据分配内存
	{
		WSAEventSelect(m_sockRaw, m_event, FD_READ);  //调用WSAEventSelect将监听的socket(m_sockRaw)与该事件(m_event)进行关联。WSAEventSelect(套接字,网络事件对象,需要关注的事件)
		m_bIsInitSucc = TRUE;
 
		m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));  //为ICMPData分配内存
 
		//ping命令的工作原理是:向网络上的另一个主机系统发送ICMP报文,如果指定系统得到了报文,它将把报文一模一样地传回给发送者
 
		if (m_szICMPData == NULL)
		{
			m_bIsInitSucc = FALSE;
		}
	}
}
 
 
CPing::~CPing()
{
	WSACleanup();
 
	if (NULL != m_szICMPData)
	{
		free(m_szICMPData);
		m_szICMPData = NULL;
	}
}

(2)BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);


BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
	if (NULL != szDestIP)	//远端IP非空
	{
		return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);  //项目 -> 属性 -> C/C++ > SDL检查:否。——修改VS配置,告诉它我就要用旧函数。inet_pton() or InetPton()
	}
	return FALSE;           //远端IP为空,false
}

(3)BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);

1.//判断初始化是否成功

2.//配置套接字SOCKET

3.//构建ICMP包

4.//填补ICMP首部

5.//发送ICMP请求报文(ping请求)

6.//判断是否需要接收响应报文

7.//等待网络事件接收响应报文


BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
	//判断初始化是否成功
	if (!m_bIsInitSucc)
	{
		return FALSE;   //初始化没成功
	}
 
	//配置套接字SOCKET
	sockaddr_in sockaddrDest;                    //sockaddr_in是internet环境下的套接字地址。定义在ws2def.h中的结构体
	sockaddrDest.sin_family = AF_INET;           //地址族(Address Family):网络类型
	sockaddrDest.sin_addr.s_addr = dwDestIP;     //32位IP地址(4字节)
	int nSockaddrDestSize = sizeof(sockaddrDest);//大小
 
	//构建ICMP包
	int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);  //ICMP包长度:32+ICMP头
	ULONG ulSendTimestamp = GetTickCountCalibrate();    //发送时间戳(毫秒级)
	USHORT usSeq = ++s_usPacketSeq;						//2字节,++0
	memset(m_szICMPData, 0, nICMPDataSize);				//数据,长度。memset()初始化内存:
	//memset(* Dst, int Val, size_t Size):将指针变量Dst所指向的前Size字节的内存单元用一个“整数”Val替换。
 
	//填补ICMP首部
	ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;
	pICMPHeader->m_byType = ECHO_REQUEST;   //类型。Type:8,Code:0:表示回显请求报文(ping请求)。   Type:0,Code:0:表示回显回答报文(ping应答)
	pICMPHeader->m_byCode = 0;              //代码
	pICMPHeader->m_usID = m_usCurrentProcID;//标识符
	pICMPHeader->m_usSeq = usSeq;           //本报的序列号
	pICMPHeader->m_ulTimeStamp = ulSendTimestamp;//发送时间戳(非标准ICMP头部)
	pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);//计算检验和    //tcp/IP协议栈使用的校验算法:对16位的数据进行累加计算,并返回计算结果
	
	//————————————————————————————————————————————————————————————————————————————————————————————
	//发送ICMP请求报文(ping请求)
	if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR)
	{
		return FALSE;  //套接字错误
	}
 
	//判断是否需要接收响应报文
	if (pPingReply == NULL)
	{
		return TRUE;
	}
 
	char recvbuf[256] = { "\0" };  //初始化
 
	while (TRUE)    //接收响应报文
	{
		//等待网络事件
		if (WSAWaitFORMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT)  //等待事件(m_event)。
			//WSAWaitForMultipleEvents(事件对象数组里边的个数为1,事件对象数组,等待类型为FALSE表示事件数组里至少有一个信号就返回,等待的超时时间为100,当系统的执行队列有I/O例程要执行时不返回)
		{
			WSANETWORKEVENTS netEvent;
			WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);   //m_event = WSACreateEvent();
			//事件发生时,调用WSAEnumNetworkEvents,检测指定的socket上的网络事件,并将关联信息保存在netEvent中。
			//WSAEnumNetworkEvents(SOCKET s,WSAEVENT hEventObject,LPWSANETWORKEVENTS lpNetworkEvents)
			//当调用WSAEnumNetworkEvents函数成功后,它会将我们指定的socket和事件对象所关联的网络事件的信息保存到LPWSANETWORKEVENTS这个结构体里去,根据这个结构体我们就可以判断是否是我们所关注的网络事件已经发生了。
 
			//如果是FD_READ,读的网络事件发生了,那就调用recv函数进行操作。
			//若是FD_CLOSE,关闭的网络事件发生了,就调用closesocket将socket关掉,在数组里将其置零等操作。
			if (netEvent.lNetworkEvents & FD_READ)  //有网络事件,且可读,那就recv收数啊
			{
				ULONG nRecvTimestamp = GetTickCountCalibrate();   //计算开始发送的时间
				int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);  //从远端IP处收数。
				if (nPacketSize != SOCKET_ERROR)
				{
					//ICMP协议是IP层的一个协议,但是由于差错报告在发送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以ICMP报文需通过IP协议来发送。
					
					//ICMP数据报的数据发送前需要两级封装:首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报。
					//拆解封装:IP数据报去掉IP报头,剩下ICMP报文,再去掉ICMP报头,就是ICMP数据报了。
					IPHeader *pIPHeader = (IPHeader*)recvbuf;
					USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);     //(4位版本+4位首部长度)&0xf,然后乘4  //IP头部20字节
					ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);
 
					if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文
						&& pICMPHeader->m_byType == ECHO_REPLY   //是响应报文类型(Type=0)
						&& pICMPHeader->m_usSeq == usSeq         //是本次请求报文的响应报文
						)
					{
						pPingReply->m_usSeq = usSeq;   //ICMP包的序列号
						pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp; //当应答返回时,用当前时间减去存放在ICMP报文中的时间值,即是往返时间。
						pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);//数据大小:整包数据-IP头-ICMP头
						pPingReply->m_dwTTL = pIPHeader->m_byTTL;  //TTL生存时间
						return TRUE;
					}
				}
			}
		}
		//超时
		if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout)//时间戳(非标准ICMP头部)
		{
			return FALSE;
		}
	}
}

WSA连网事件流程图

(4)计算检验和


//计算检验和    // TCP/IP协议栈使用的校验算法是比较经典的,对16位的数据进行累加计算,并返回计算结果
USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)       //CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);
{
	unsigned long ulCheckSum = 0;   //4B=32b    //(1)将检验和字段置为0
 
	while (nSize > 1)
	{
		ulCheckSum += *pBuffer++;   //数值相加	//(2)把需校验的数据看成以16位为单位的数字组成,依次进行求和,并存到32位的整型中
		nSize -= sizeof(USHORT);    //长度相减
	}
	if (nSize)
	{
		ulCheckSum += *(UCHAR*)pBuffer; 
	}
 
	ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);  //高位相加  //(3)把求和结果中的高16位(进位)加到低16位上,如果还有进位,重复
	ulCheckSum += (ulCheckSum >> 16);                         //将溢出位加入
 
	return (USHORT)(~ulCheckSum);    //返回值:取反                       //(4)将这个32位的整型按位取反,并强制转换为16位整型(截断)后返回
}
 
 

(5)计算毫秒级别的时间差


//计算毫秒级别的时间差。返回值是unsigned long级别的
ULONG CPing::GetTickCountCalibrate()
{
	static ULONG s_ulFirstCallTick = 0;
	static LONGLONG s_ullFirstCallTickMS = 0;
 
	SYSTEMTIME systemtime;		//系统时间SYSTEMTIME与tm类似,不过多了一项wMilliseconds。
	FILETIME filetime;		    //文件时间FILETIME与time_t类似,是64位整型,不过FILETIME是以100纳秒(ns)为单位。
	GetLocalTime(&systemtime);  //GetLocalTime获得当前的本地时间,GetSystemTime函数获得当前的UTC时间,两个时间存在着时差。
	SystemTimeToFileTime(&systemtime, &filetime);//UTC的SYSTEMTIME时间 转换为对应的 本地的FILETIME时间
	
	LARGE_INTEGER liCurrentTime;
	liCurrentTime.HighPart = filetime.dwHighDateTime;
	liCurrentTime.LowPart = filetime.dwLowDateTime;
	LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;
 
	if (s_ulFirstCallTick == 0)
	{
		s_ulFirstCallTick = GetTickCount();  //返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数。用GetTickCount()计算毫秒级的时间差是不靠谱的!
	}
 
	if (s_ullFirstCallTickMS == 0)
	{
		s_ullFirstCallTickMS = llCurrentTimeMS;
	}
 
	return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);//   当前时间ms - 第一次回应时间ms
}

到此这篇关于c++代码实现网络Ping功能的文章就介绍到这了,更多相关C++网络Ping内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++代码实现网络Ping功能

本文链接: https://lsjlt.com/news/132332.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++代码实现网络Ping功能
    目录(一)main.cpp文件(二)ping.h文件(1)IP头结构体:(2)ICMP头结构体:(3)ICMP响应报文结构体:(4)Ping类及相关变量的定义:(三)ping.cpp...
    99+
    2024-04-02
  • FTP客户端c代码功能实现
    现在市面上有很多免费的FTP软件:如FileZilla ,那如果想自己在代码中实现与ftp服务器的上传下载文件该如何实现那?  本质上ftp协议就是TCP基础上建立的一种协议,具体如下。 FTP 概述 文件传输协议(FTP)作为网络共享文件...
    99+
    2023-09-24
    服务器 网络 linux
  • C++实现关机功能详细代码
    目录前言:功能实现:总结前言: 可以写出来后发给你的室友或者好朋友,可以增进你们之间的友谊 功能实现: 输入关机命令语句,shutdown -s -t 60,电脑就会在60秒之后关机...
    99+
    2024-04-02
  • python实现的ping的代码
    把开发过程中较好的代码段做个记录,如下资料是关于python实现的ping的代码,希望对各位朋友有用。 #!/usr/bin/env python #coding:utf-8 import os, sys, socket, struct, ...
    99+
    2023-01-31
    代码 python ping
  • C++实现Go的defer功能(示例代码)
    在Go语言中有一个关键字:defer,它的作用就是延迟执行后面的函数,在资源释放方面特别有用,比如下面一段C/C++的示例代码: void test() { FILE* fp ...
    99+
    2024-04-02
  • C#实现文件上传功能代码分享
    本篇内容介绍了“C#实现文件上传功能代码分享”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!实现按日期命名C#文件上传代码如下:private...
    99+
    2023-06-18
  • 使用代理ip怎么实现网络防御功能
    这篇文章给大家介绍使用代理ip怎么实现网络防御功能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。在不同地区部署代理ip节点,使访问者能够快速连接到附近的节点,使访问者能够更快地访问站点,CDN缓存能够进一步提高站点的访...
    99+
    2023-06-15
  • C#实现验证码功能
    本文实例为大家分享了C#实现验证码功能的具体代码,供大家参考,具体内容如下 分析 需要四个字符(字母(大小写)+数字) 将四个字符连接成字符串 将连接的字符...
    99+
    2024-04-02
  • vbs如何实现网络PING包查询
    这篇文章主要介绍了vbs如何实现网络PING包查询,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。代码如下:@echo off color f2 echo. set for=于...
    99+
    2023-06-09
  • Android 验证码功能实现代码
    先给大家展示下效果图,如果大家感觉还不错,请参考实现代码 很简单的一个例子,点击刷新验证码,刷新当前显示的验证码,点击确定,如果输入的和显示的匹配,就会跳转到下一个界面中,这...
    99+
    2022-06-06
    验证码 Android
  • C++实现Go的defer功能(示例代码)
    在Go语言中有一个关键字:defer,它的作用就是延迟执行后面的函数,在资源释放方面特别有用,比如下面一段C/C++的示例代码: void test() { FILE* fp...
    99+
    2022-06-07
    c+ GO defer 示例 C++
  • C#实现MP3播放器功能的示例代码
    目录实践过程效果代码实践过程 效果 代码 public partial class Form1 : Form { public Form1() ...
    99+
    2022-12-09
    C# MP3播放器 C# 播放器
  • C#实现图片轮播功能的示例代码
    目录实践过程效果代码实践过程 效果 代码 public partial class Form1 : Form { public Form1() { ...
    99+
    2022-12-19
    C#图片轮播功能 C#图片轮播 C# 轮播
  • 企业微信扫码登录网页功能实现代码
    企业微信扫码登录网页功能,代码如下所示: //jq写法完善版 <!DOCTYPE html> <html lang="en"> <head> ...
    99+
    2024-04-02
  • android截屏功能实现代码
    android开发中通过View的getDrawingCache方法可以达到截屏的目的,只是缺少状态栏! 原始界面 截屏得到的图片 代码实现 1. 添加权限(Android...
    99+
    2022-06-06
    Android
  • pytorch简单实现神经网络功能
    目录一、基本(1)利用pytorch建好的层进行搭建(2)使用网络二、进阶一、基本 (1)利用pytorch建好的层进行搭建 import torch from torch impo...
    99+
    2024-04-02
  • Android实现网络图片浏览功能
    我们在上网的过程中经常看到各种图片,那你知道它是如何实现的吗?接下来就让我们一块探讨一下。 网络图片的浏览可以分为俩部分,基本的页面布局与界面交互,让我们一步步的来编写。 基本布局很简单,只需要有一个输入图片链接的EditText,一个浏览...
    99+
    2023-05-31
    android 图片浏览 roi
  • C#实现商城购物功能的代码怎么写
    这篇文章主要介绍“C#实现商城购物功能的代码怎么写”,在日常操作中,相信很多人在C#实现商城购物功能的代码怎么写问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#实现商城购物功能的代码怎么写”的疑惑有所帮助!...
    99+
    2023-06-29
  • C#实现身份证验证功能的示例代码
    目录实践过程效果代码实践过程 效果 代码 public partial class Form1 : Form { public Form1() { ...
    99+
    2022-12-20
    C#身份证验证 C# 验证 C#身份证
  • python 网络爬虫初级实现代码
    首先,我们来看一个Python抓取网页的库:urllib或urllib2。 那么urllib与urllib2有什么区别呢? 可以把urllib2当作urllib的扩增,比较明显的优势是urllib2.url...
    99+
    2022-06-04
    爬虫 代码 网络
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作