- 运行环境
- 一、HTTP协议详解
- 二、包含必要头文件
- 三、域名转IP地址
- 四、创建套接字
- 五、发送request
- 六、处理response
- 七、完整代码
运行环境
Ubuntu20.04 虚拟机
gcc 9.4.0
请移步我的另一篇博客HTTP协议详解
二、包含必要头文件#include三、域名转IP地址#include #include #include #include #include #include #include #include
// 地址转IP
char *host_to_ip(const char *hostname)
{
// hostname 为主机名,也就是域名
// 使用该函数时,只要传递域名字符串,就会返回域名对应的 IP 地址
// 返回的地址信息会装入 hostent 结构体
struct hostent *host = gethostbyname(hostname);
if(host != NULL)
{
// h_addr_list : List of addresses from name server.
// haddr_list 里面存有多个目标主机的地址 是网络字节序,需要通过inet_ntop函数转换。
// inet_ntoa 功能是将网络地址转换成“.”点隔的字符串格式。
return inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
}
}
四、创建套接字
//创建套接字
int creat_socket(const chat *ip)
{
//初始化套接字
int sockfd = socket(ip, SOCK_STREAM, 0);
//套接字赋值
struct sockaddr_in sin = {0};
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr.s_addr = inet_addr(ip);
//套接字和信息值绑定并连接服务器 connect成功返回0
if(0 != connect(sockfd,(struct sockaddr*)&sin, strlen(struct sockaddr_in)))
{
return -1;
}
//设置sockfd为非阻塞的
fcntl(sockfd, F_SETFL, O_NONBLOCK);
return sockfd;
}
五、发送request
//发送请求
int send_request(const char *hostname, const char *resource)
{
// 地址转IP
char *ip = host_to_ip(hostname);
// 创建套接字
int sockfd = creat_socket(ip);
// 发送格式化输出到 buffer 所指向的字符串
char buffer[4096] = {0};
// sprintf 发送格式化输出到 str 所指向的字符串。
sprintf(buffer,"GET %s %srnHost: %srn%srnrn",
resource, "HTTP/1.1", hostname, "Connection:closern");
//发送请求
//如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR(-1)。
send(sockfd, buffer, strlen(buffer), 0);
return sockfd;
}
六、处理response
//处理响应
char *do_response(const int sockfd)
{
//fd_set 每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系
fd_set set;
FD_ZERO(&set); //将set的所有位置0,如set在内存中占8位则将set置为00000000
FD_SET(sockfd, &set); //将set的第sockfd(3)位置1,如set原来是00000000,则现在变为000100000
struct timeval tv;
tv.tv_sec = 5; //5ms轮循一次
tv.tv_usec = 0;
char *result = malloc(sizeof(int));
memset(result, 0, sizeof(int));
while(1)
{
int selection = select(sockfd + 1, &set, NULL, NULL, &tv);
// FD_ISSET 测试指定的文件描述符是否在该文件描述符集合中
if(!selection || !FD_ISSET(sockfd, &set))
{
break;
}
else
{
//接收数据的buffer
char buffer[4096] = {0};
//接收数据
int len = recv(sockfd, buffer, 4096, 0);
if (len == 0) //说明没连接上
{
break;
}
//realloc(*ptr, size)ptr 为需要重新分配的内存空间指针,size 为新的内存空间的大小。
result = realloc(result, (strlen(result) + len + 1) * sizeof(char));
//buffer --> result
strncat(result, buffer, len);
}
}
return result;
}
七、完整代码
#include#include #include #include #include #include #include #include #include #define BUFFER_SIZE 4096 #define HTTP_VERSION "HTTP/1.1" #define CONNETION_TYPE "Connection:closern" // 地址转IP char *host_to_ip(const char *hostname) { // hostname 为主机名,也就是域名 // 使用该函数时,只要传递域名字符串,就会返回域名对应的 IP 地址 // 返回的地址信息会装入 hostent 结构体 struct hostent *host = gethostbyname(hostname); if(host != NULL) { // h_addr_list : List of addresses from name server. // haddr_list 里面存有多个目标主机的地址 是网络字节序,需要通过inet_ntop函数转换。 // inet_ntoa 功能是将网络地址转换成“.”点隔的字符串格式。 return inet_ntoa(*(struct in_addr*)host->h_addr_list[0]); } } //创建套接字 int creat_socket(char *ip) { //初始化套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); //套接字赋值 struct sockaddr_in sin = {0}; sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr.s_addr = inet_addr(ip); //套接字和信息值绑定并连接服务器 connect成功返回0 if(0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) { return -1; } //设置sockfd为非阻塞的 fcntl(sockfd, F_SETFL, O_NONBLOCK); return sockfd; } //发送请求 int send_request(const char *hostname, const char *resource) { // 地址转IP char *ip = host_to_ip(hostname); // 创建套接字 int sockfd = creat_socket(ip); // 发送格式化输出到 buffer 所指向的字符串 char buffer[4096] = {0}; // sprintf 发送格式化输出到 str 所指向的字符串。 sprintf(buffer,"GET %s %srnHost: %srn%srnrn", resource, "HTTP/1.1", hostname, "Connection:closern"); //发送请求 //如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR(-1)。 send(sockfd, buffer, strlen(buffer), 0); return sockfd; } //处理响应 char *do_response(const int sockfd) { //fd_set 每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系 fd_set set; FD_ZERO(&set); //将set的所有位置0,如set在内存中占8位则将set置为00000000 FD_SET(sockfd, &set); //将set的第sockfd(3)位置1,如set原来是00000000,则现在变为000100000 struct timeval tv; tv.tv_sec = 5; //5ms轮循一次 tv.tv_usec = 0; char *result = malloc(sizeof(int)); memset(result, 0, sizeof(int)); while(1) { int selection = select(sockfd + 1, &set, NULL, NULL, &tv); // FD_ISSET 测试指定的文件描述符是否在该文件描述符集合中 if(!selection || !FD_ISSET(sockfd, &set)) { break; } else { //接收数据的buffer char buffer[4096] = {0}; //接收数据 int len = recv(sockfd, buffer, 4096, 0); if (len == 0) //说明没连接上 { break; } //realloc(*ptr, size)ptr 为需要重新分配的内存空间指针,size 为新的内存空间的大小。 result = realloc(result, (strlen(result) + len + 1) * sizeof(char)); //buffer --> result strncat(result, buffer, len); } } return result; } int main(int argc, char *argv[]) { if (argc < 3) return -1; char *response = do_response(send_request(argv[1], argv[2])); printf("response : %sn", response); free(response); }



