利用C语言实现URL解析的基本方法之优秀

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
.cn 域名,1个 12个月
全局流量管理 GTM,标准版 1个月
简介: 今天主要来学习一下,如何利用URL,实现对应的解析过程。

第一:URL是什么  

 URL代表着是统一资源定位符(UniformResourceLocator)。

作用是为了告诉使用者 某个资源在 Web 上的地址。


这个资源可以是一个 HTML 页面,一个 CSS 文档,一幅图像或一个猫片等等。


比如:


用HTTP协议访问Web服务器:1fd8e9644dd0cecc99a8d66f08052cf1.png用FTP协议下载和上传文件时

3e1b67b38332749840cee6e00ba69b6b.png

读取客户端计算机本地文件时

aee77a80a311afa312fd88d08050352c.png这里面细分,又可以分为好几个部分。

1、协议

尽管 URL 有各种不同的写法, 但它们有一个共同点, 开头部分的内容必须是协议类型,

可以是http、ftp、mailto或者https,这部分文字都表示浏览器应当使用的访问方法。会用//为分隔符。决定了后面部分的写法, 因此并不会造成混乱。

2、用户名/密码

用户名密码通常可以省略。

3、域名

域名是www.gitee.com,在发送请求前,会向DNS服务器解析IP。如果已经知道ip,还可以跳过DNS解析那一步,直接把IP当做域名部分使用。

4、端口

域名后面有些时候会带有端口,和域名之间用:分隔,端口不是一个URL的必须的部分。当网址为http://时,默认端口为80, https://时,默认端口是443, ftp://时,默认端口是21。

5、文件路径/文件名

从域名的第一个/开始到最后一个/为止,是虚拟目录的部分。虚拟目录也不是URL必须的部分,上述实例http协议url中的虚拟目录是/yikoulinux/chat/blob/master/


从域名最后一个/开始到?为止,是文件名部分;如果没有?,则是从域名最后一个/开始到#为止,是文件名部分;如果没有?和#,那么就从域名的最后一个/从开始到结束,都是文件名部分。


比如前面的http url实例,其中文件chat.h在gitee服务器/yikoulinux/chat/blob/master/下:


c38b51463af68e6185fe7560a8c3f747.png

文件名也不是一个URL的必须部分。


文件名省略情况如下:


1.http://www.gitee.com/dir/


我们可以这样理解, 以“/” 结尾代表 /dir/ 后面本来应该有的文件名被省略了。根据 URL 的规则, 文件名可以像前面这样省略。不过, 没有文件名, 服务器怎么知道要访问哪个文件呢?其实, 我们会在服务器上事先设置好文件名省略时要访问的默认文件名。这个设置根据服务器不同而不同, 大多数情况下是 index.html 或者 default.htm 之类的文件名。


因此, 像前面这样省略文件名时, 服务器就会访问 /dir/index.html或者 /dir/default.htm[由web服务器配置]。


2.http://www.gitee.com/ 这个 URL 也是以“/” 结尾的, 也就是说它表示访问一个名叫“/” 的目录 。而且, 由于省略了文件名, 所以结果就是访问 /index.html 或者/default.htm 这样的文件了。


3.http://www.gitee.com 这次连结尾的“/” 都省略了。像这样连目录名都省略时, 真不知道到底在请求哪个文件了, 实在有些过分。不过, 这种写法也是允许的。当没有路径名时, 就代表访问根目录下事先设置的默认文件 , 也就是 /index.html 或者 /default.htm 这些文件, 这样就不会发生混乱了。


4.http://www.gitee.com/yikoupeng


一般来说, 这种情况会按照下面的惯例进行处理:如果Web 服务器上存在名为 yikoupeng的文件, 则将 yikoupeng作为文件名来处 理;如果存在名为 yikoupeng的目录, 则将 yikoupeng作为目录名来处理 。


第二:通过一个例子实现解析

编写一个简单的用于解析url的小例子,最终目标是解析出URL中所有的数据信息。

99134956128e40df9bb5bd5218d63128.png

第三:库函数

用到的几个库函数如下:

1. strncasecmp

头文件

#include<string.h>

函数定义

int strncasecmp(const char *s1,const char *s2,size_t n);

函数说明

用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异。

返回值

若参数s1和s2 字符串相同则返回0。
s1 若大于s2则返回大于0的值,
s1若小于s2则返回小于0 的值。

2. strstr

头文件

#include<string.h>

函数定义

char *strstr( const char* str, const char* substr );

函数说明

查找 substr 所指的空终止字节字符串在 str 所指的空终止字节字符串中的首次出现。不比较空终止字符。
若 str 或 substr 不是指向空终止字节字符串的指针,则行为未定义。

参数

1. str :指向要检验的空终止字节字符串的指针
2. substr :指向要查找的空终止字节字符串的指针

返回值

指向于 str 中找到的子串首字符的指针,或若找不到该子串则为空指针。若 substr 指向空字符串,则返回 str 。

3. strtok

函数定义

char *strtok(char *str, const char *delim)

功能

分解字符串 str 为一组字符串,delim 为分隔符

参数

1. str -- 要被分解成一组小字符串的字符串。
2. delim -- 包含分隔符的 C 字符串。

返回值

该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

4. strncpy

函数说明

1. char *strncpy(char *dest, const char *src, size_t n)
2.

功能

将src指向的字符串拷贝到dest执行的内存中,最多拷贝n个字符

参数

1. dest -- 指向用于存储复制内容的目标数组。
2. src -- 要复制的字符串。
3. n -- 要从源中复制的字符数。

返回值

该函数返回最终复制的字符串。

5. inet_pton/inet_ntop

头文件

1.        #include <sys/socket.h>
2.        #include <netinet/in.h>
3.        #include <arpa/inet.h>

函数声明

1. #include <arpe/inet.h>
2. int inet_pton(int family, const char *strptr, void *addrptr);

功能:

1. 将点分十进制的ip地址转化为用于网络传输的数值格式
2.对于IPv4地址和IPv6地址都适用

参数

family:协议类型既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
strptr:指向点分十进制的IP地址字符串,比如"192.168.1.1"
addrptr:转换结果存放在addrptr中,比如"192.168.1.1"转换为:0xC0A80101
addrptr类型为:struct in_addr 
typedef uint32_t in_addr_t;
struct in_addr {
    in_addr_t s_addr;
};

返回值


1. 若成功则为1,若输入不是有效的表达式则为0,
2. 若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

功能

将数值格式转化为点分十进制的ip地址格式,从数值格式(addrptr)转换到表达式(strptr),

返回值

若成功则为指向结构的指针,若出错则为NULL

6. gethostbyname

函数的定义

1. #include <netdb.h>
2. struct hostent * gethostbyname(const char * hostname);

功能

解析hostname指向的域名,该函数会将该域名封装到DNS协议包中,发送给DNS服务器,DNS服务器会将该域名对应的地址返回,存储在struct hostent中

参数

hostname :存储域名对应的字符串。

返回值

若成功则为非空指针,若出错则为NULL且设置h_errno
返回的指针类型为:
struct hostent{
    char *h_name;  //official name
    char **h_aliases;  //alias list
    int  h_addrtype;  //host address type
    int  h_length;  //address lenght
    char **h_addr_list;  //address list
}
DNS服务器返回的地址就存储在该结构体中

第四:自定义结构

结构体用于存放需要解析的协议和端口号

1. struct pro_port{
2.  char pro_s[32];
3.  unsigned short port;
4. };

目前本例子只解析以下集中协议,读者需要支持其他协议可以按照该格式增加对应信息即可

#define HEAD_FTP_P "ftp://"
#define HEAD_FTPS_P "ftps://"   
#define HEAD_FTPES_P "ftpes://"
#define HEAD_HTTP_P "http://"
#define HEAD_HTTPS_P "https://"
#define PORT_FTP  21
#define PORT_FTPS_I  990    //implicit
#define PORT_FTPS_E  21     //explicit
#define PORT_HTTP 80
#define PORT_HTTPS 443
struct pro_port g_pro_port[]={
 {HEAD_FTP_P,PORT_FTP},
 {HEAD_FTPS_P,PORT_FTPS_I}, 
 {HEAD_FTPES_P,PORT_FTPS_E}, 
 {HEAD_HTTP_P,PORT_HTTP}, 
 {HEAD_HTTPS_P,PORT_HTTPS},
};

第五:程序流程图

5fbebbe8e44cda2843c5e6992f262544.png

程序流程相对来说,比较简单,主函数功能说明如下:

1. parse_url()

int parse_url(char *raw_url,URL_RESULT_T *result)

参数:

raw_url:指向一个url字符串,比如:ftp://peng:pass@baidu.com/dir/index.html
result :url解析后的结果存放在该结构体中
结构体类型定义如下:
typedef struct
{
 char user[MAX_USER_LEN];
 char pass[MAX_PASS_LEN];
 char domain[INET_DOMAINSTRLEN];//域名
 char svr_dir[MAX_PATH_FILE_LEN]; //文件路径
 char svr_ip[MAX_IP_STR_LEN];
 int port;
}URL_RESULT_T;

功能:

解析url字符串,并将解析结果存放在result中

返回值;

1. 成功返回 URL_OK
2. 失败返回 URL_ERROR

2. void remove_quotation_mark()

void remove_quotation_mark(char *input)

参数

input:字符串

功能

去掉字符串中的双引号 \"

返回值

3. parse_domain_dir

int  parse_domain_dir(char *url,URL_RESULT_T *result)

参数

1. url:执行去掉协议头的url字符串,比如:peng:pass@baidu.com/dir/index.html
2. result :url解析后的结果存放在该结构体中

功能

解析出url中用户名、密码、域名/ip、文件路径等信息

返回值

1. 成功:URL_OK
2. 失败:URL_ERROR

4. check_is_ipv4()

int check_is_ipv4(char *domain)

参数

domain:指向一个域名或者IP地址点分十进制字符串,最大长度为:MAX_URL_LEN

功能

判断domain中存放的是不是合法的IP地址

返回值

1. 1:是IP地址
2. -1:不是IP地址

5、dns_resoulve()

int dns_resoulve(char *svr_ip,const char *domain)

参数

1. svr_ip:存放DNS协议解析过的域名对应的IP地址点分十进制字符串
2. domain:域名字符串

功能

将domain中的域名,通过DNS协议解析成对应的IP地址

返回值

1. 成功:URL_OK
2. 失败:URL_ERROR

第六:运行

测试程序

void main(void)
{
 int ret;
 char url_str[256]="ftp://peng:pass@baidu.com/dir/index.html";
 parse_url(url_str,&url_result_t);
 ret = check_is_ipv4(url_result_t.domain); 
 if(ret != 1)
 { 
  //dns
  dns_resoulve(url_result_t.svr_ip,url_result_t.domain);
 }
 printf("\n-------------result---------------\n");
 printf("user:%s\n",url_result_t.user);
 printf("pass:%s\n",url_result_t.pass);
 printf("port:%d\n",url_result_t.port);
 printf("domain:%s\n",url_result_t.domain);
 printf("svr_dir:%s\n",url_result_t.svr_dir);
 printf("svr_ip:%s\n",url_result_t.svr_ip);
 printf("-------------end---------------\n");
}

执行结果94f05a114d7a388e9c8fcfe427095150.png

第七:代码获取

完整代码可以进入我的仓库获取

目录
相关文章
|
24天前
|
安全 Ubuntu Shell
深入解析 vsftpd 2.3.4 的笑脸漏洞及其检测方法
本文详细解析了 vsftpd 2.3.4 版本中的“笑脸漏洞”,该漏洞允许攻击者通过特定用户名和密码触发后门,获取远程代码执行权限。文章提供了漏洞概述、影响范围及一个 Python 脚本,用于检测目标服务器是否受此漏洞影响。通过连接至目标服务器并尝试登录特定用户名,脚本能够判断服务器是否存在该漏洞,并给出相应的警告信息。
146 84
|
5天前
|
数据可视化 项目管理
个人和团队都好用的年度复盘工具:看板与KPT方法解析
本文带你了解高效方法KPT复盘法(Keep、Problem、Try),结合看板工具,帮助你理清头绪,快速完成年度复盘。
38 7
个人和团队都好用的年度复盘工具:看板与KPT方法解析
|
23天前
|
存储 Java 开发者
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
|
29天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
133 14
|
1月前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
47 8
|
1月前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
443 6
|
1月前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
41 5
|
1月前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
42 5
|
1月前
|
安全 搜索推荐 Unix
【C语言】《回调函数》详细解析
回调函数是指一个通过函数指针调用的函数。它允许将一个函数作为参数传递给另一个函数,并在特定事件发生时执行。这种技术使得编程更加灵活,可以动态决定在何时调用哪个函数。
43 1
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
87 2

推荐镜像

更多