文章目录
- 代码案例
- 源代码
- 应用知识
- hostent结构体
- gethostbyname()函数
- inet_pton和inet_ntop
- inet_pton
- inet_ntop
- sockaddr_in
- in_addr结构
- htons 编辑
- socket()
- connect()
- sprintf函数
- format标签属性
- setsockopt
代码案例
爬到的HTML文件

输入终端的参数

源代码
/*************************************************************************> File Name: myphp.c> Author: 杨永利> Mail: 1795018360@qq.com > Created Time: 2020年07月28日 星期二 11时50分24秒************************************************************************/#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>int main(int argc, char* argv[]){if(argc!=3){printf("请输入正确的参数个数(运行文件,源网址,保存文件名)!\n");return -1;}// 存放源网址的字符串char *Char_Yuan=argv[1];printf("你给的源网址是:%s\n",Char_Yuan);// 定义hostent结构体/*struct hostenth_name – 地址的正式名称。h_aliases – 空字节-地址的预备名称的指针。h_addrtype –地址类型; 通常是AF_INET。h_length – 地址的比特长度。h_addr_list – 零字节-主机网络地址指针。网络字节顺序。h_addr - h_addr_list中的第一地址。*/struct hostent *host;// gethostbyname()函数用于获取主机名的包含主机名字和地址信息的hostent结构的指针if((host=gethostbyname(Char_Yuan))== NULL){printf("获取ip地址失败!\n");return -1;} // 定义字符串用于存放转换后的网络地址char buf[30];// inet_ntop是将网络二进制结构到ASCII类型的地址 /*第一个参数af是地址簇,第二个参数*src是来源地址,第三个参数* dst接收转换后的数据。第四个参数三接收数据的长度*/inet_ntop(host->h_addrtype,host->h_addr_list[0],buf,sizeof(buf));// sockaddr结构体/*参数说明:sin_family指代协议族,在socket编程中只能是AF_INETsin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。sin_addr存储IP地址,使用in_addr这个数据结构sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。*/struct sockaddr_in server;// 初始化结构体变量bzero(&server,sizeof(server));// 为结构体变量的每个成员赋值server.sin_family=AF_INET;server.sin_port=htons(80);server.sin_addr.s_addr=inet_addr(buf);// 根据目前数据,分配一个套接口的描述字及其所用的资源。int sockfd=socket(AF_INET,SOCK_STREAM,0);// connect函数进行连接套接字if(connect(sockfd, (struct sockaddr*)&server, sizeof(server))){printf("connect() error!\n");return -1;}// 定义套接字要进行发送的消息字符串char socket_send_message[1024];// 用sprintf函数将请求头写发送字符串sprintf(socket_send_message,"GET / HTTP/1.1\r\nHost:%s\r\nAccept:*/* \r\nUser-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64)\r\nconnection:Keep-Alive\r\n\r\n", Char_Yuan);// 将缓冲区设置为两秒int timeout=2000;/*setsockopt函数参数:sockfd:标识一个套接口的描述字。level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。optname:需设置的选项。optval:指针,指向存放选项待设置的新值的缓冲区。optlen:optval缓冲区长度。 */setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));// 发送套接字if(send(sockfd, socket_send_message, strlen(socket_send_message), 0) <= 0){printf("send() error!\n");return -1;}setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));// 定义套接字要进行接收的消息字符串char socket_get_message[1024];// 用于进行数据的接收int str_len;// 将获取的消息写入html文件FILE* file = fopen(argv[2], "a+");if(file == NULL){printf("fopen() error!\n");return -1;}// 执行循环直到接收消息的最后do{//给定循环缓冲区一直接受,直到接受至最后bzero(socket_get_message, sizeof(socket_get_message));str_len = recv(sockfd, socket_get_message, sizeof(socket_get_message), 0);// 将每次接收的数据追加到文件中fwrite(socket_get_message, 1, 1024, file);}while(str_len);return 0;
}
应用知识
hostent结构体
定义
struct hostent{
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
#define h_addr h_addr_list[0];
};
参数
struct hostent
h_name – 地址的正式名称。
h_aliases – 空字节-地址的预备名称的指针。
h_addrtype –地址类型; 通常是AF_INET。
h_length – 地址的比特长度。
h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
h_addr - h_addr_list中的第一地址。
gethostbyname()成功时返回一个指向结构体 hostent 的指针,或者是个空(NULL)指针。(但是和以前不同,不设置errno,h_errno 设置错误信息。请看下面的herror()。
gethostbyname()函数
gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构的指针。结构的声明与gethostbyaddr()中一致。
返回对应于给定主机名的主机信息。
#include <winsock2.h>
struct hostent *gethostbyname(const char *name);
name:指向主机名的指针。
返回类型
struct hostent
{
char *h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
};
Linux版
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);
gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针。结构的声明与gethostbyaddr()中一致。
返回的指针指向一个由Windows Sockets实现分配的结构。应用程序不应该试图修改这个结构或者释放它的任何部分。此外,所有线程共用一份这个结构的拷贝(这里应该是每个线程一份拷贝,原文是这样的Furthermore, only one copy of this structure is allocated per thread, so the application should copy any information it needs before issuing any other Windows Sockets function calls.),所以应用程序应该在发出其他Windows Scokets API调用前,把自己所需的信息拷贝下来。
gethostbyname()实现没有必要识别传送给它的IP地址串。对于这样的请求,应该把IP地址串当作一个未知主机名同样处理。如果应用程序有IP地址串需要处理,它应该使用inet_addr()函数把地址串转换为IP地址,然后调用gethostbyaddr()来得到hostent结构。
返回:非空指针——成功,空指针——出错,同时设置h_errno
inet_pton和inet_ntop
函数作用
- inet_pton:转换字符串到网络地址
- inet_ntop:转换网络二进制结构到ASCII类型的地址
inet_pton
inet_pton:将“点分十进制” -> “二进制整数”
函数原型:
int inet_pton(int af, const char *src, void *dst);
参数:
这个函数转换字符串到网络地址,
第一个参数af是地址簇,
第二个参数src是来源地址,
第三个参数 dst接收转换后的数据。
inet_pton 是inet_addr的扩展,支持的多地址族有下列:
af = AF_INET
src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在dst中。
af = AF_INET6
src为指向IPV6的地址,函数将该地址转换为in6_addr的结构体,并复制在dst中。
如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。
inet_ntop
inet_ntop函数原型如下[将“二进制整数” -> “点分十进制”]
Linux下inet_pton和inet_ntop这2个IP地址转换函数,可以在将IP地址在“点分十进制”和“二进制整数”之间转换。而且,这2个函数能够处理ipv4和ipv6。算是比较新的函数了。
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和inet_pton相同,只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。
sockaddr_in
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in{short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/};
参数说明:
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围065535,同时01024范围的端口号已经被系统使用或保留。
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
(s_addr按照网络字节顺序存储IP地址)
然后用memset函数初始化就可以了memset((char*)&mysock,0,sizeof(mysock));//初始化
sockaddr_in mysock;
memset((char*)&mysock,0,sizeof(mysock));
mysock.sin_family=AF_INET;
mysock.sin_port=htons(1234);//1234是端口号
mysock.sin_addr.s_addr=inet_addr("192.168.0.1");
in_addr结构
在linux下:
typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};
htons 编辑
htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。
u_shorthtons(u_short hostshort);
socket()
socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。
对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。
#include <sys/socket.h>
int socket( int af, int type, int protocol);
参数
- af:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式。
- type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
- protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
返回值:
若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。
connect()
connect()用于建立与指定socket的连接。
定义函数:int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
- sockfd:标识一个套接字。
- serv_addr:套接字s想要连接的主机地址和端口号。
- addrlen:name缓冲区的长度。
返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.
sprintf函数
函数声明
int sprintf(char *string, char *format [,argument,...]);
参数列表
- string-- 这是指向一个字符数组的指针,该数组存储了 C 字符串。
- format-- 这是字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是%[flags][width][.precision][length]specifier
- [argument]…:根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。
功能
把格式化的数据写入某个字符串缓冲区。
返回值
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5。
format标签属性
format 标签属性是%[flags][width][.precision][length]specifier,具体讲解如下: [3]
sprintf格式的规格如下所示。[]中的部分是可选的。
%[指定参数][标识符][宽度][.精度]指示符
- [指定参数] 处理字符方向。负号时表示从后向前处理。
- [标识符] 填空字元。 0 的话表示空格填 0;空格是内定值,表示空格就放着。
- [宽度]字符总宽度。为最小宽度。
- [精度] 精确度。指在小数点后的浮点数位数。
- 转换字符
%% 印出百分比符号,不转换。
%c 字符输出到缓冲区,不转换。
%d 整数转成十进位。
%f 倍精确度数字转成浮点数。
%o 整数转成八进位。
%s 字符串输出到缓冲区,不转换。
%x 整数转成小写十六进位。
%X 整数转成大写十六进位。
setsockopt
setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数说明
- sockfd:标识一个套接口的描述字。
- level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
- optname:需设置的选项。
- optval:指针,指向存放选项待设置的新值的缓冲区。
- optlen:optval缓冲区长度。



















