Http协议封装

server/2025/1/15 7:39:04/
http://www.w3.org/2000/svg" style="display: none;">

httphttp_0">Myhttp封装http协议

源代码

#include <iostream>
#include <cstring>
#include <string>
#include <thread>
#include <atomic>
#include <fstream> // 添加文件操作头文件#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
typedef int socklen_t;
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h> // 包含 inet_pton 函数
#define closesocket close
#endifstruct hostent *AutoGetIp(std::string domain, std::string &target_ip)
{struct hostent *host = gethostbyname(domain.c_str());if (host == nullptr){std::cerr << "Get IP address error for domain: " << domain << std::endl;return nullptr;}std::cout << "Initialized domain to IP: " << host->h_name;if (host->h_addr_list[0]){target_ip = inet_ntoa(*(struct in_addr *)host->h_addr_list[0]); // 将域名返回的可用ip列表第一个赋值给ipstd::cout << " --> " << target_ip << std::endl;}return host;
}class HttpRequest
{
private:int client_fd = -1;std::atomic<bool> stopListening{false};public:struct hostent *host; // 主机结构体std::string domain;std::string ip;int ip_version = AF_INET; // 默认使用ipv4int port = 80;            // 默认http协议是80端口HttpRequest() : host(nullptr) {}HttpRequest(std::string domain_) : HttpRequest(domain_, 80) {}HttpRequest(std::string domain_, int port_) : domain(domain_), port(port_){std::string target_ip;host = AutoGetIp(domain, target_ip);if (host == nullptr){std::cerr << "Failed to get IP address for domain: " << domain << std::endl;return;}ip = target_ip;ip_version = host->h_addrtype;std::string ip_str = ip_version == AF_INET ? "IPv4" : "IPv6";std::cout << "IP version: " << ip_str << std::endl;}HttpRequest(std::string ip_, int ip_v, int port_) : ip(ip_), ip_version(ip_v), port(port_) // 如果只知道ip和ip协议的版本{struct in_addr addr; // ipv4地址结构体if (inet_pton(ip_version, ip.c_str(), &addr) <= 0){std::cerr << "Invalid address/ Address not supported." << std::endl;return;}host = gethostbyaddr((const char *)&addr, sizeof(addr), ip_version);if (host == nullptr){std::cerr << "Get host by address error for IP: " << ip << std::endl;return;}domain = host->h_name;printf("Hostname: %s\n", host->h_name);}~HttpRequest(){stopListening = true;if (client_fd != -1){closesocket(client_fd);}
#ifdef _WIN32WSACleanup();
#endif}void setIP(std::string ip_) { ip = ip_; }void setPort(int port_) { port = port_; }int connect_to(){
#ifdef _WIN32WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){std::cerr << "Failed to initialize Winsock." << std::endl;return -1;}
#endifclient_fd = socket(ip_version, SOCK_STREAM, 0);if (client_fd == INVALID_SOCKET){std::cerr << "Socket creation failed." << std::endl;
#ifdef _WIN32WSACleanup();
#endifreturn -1;}struct sockaddr_in server_address;server_address.sin_family = AF_INET; // 使用 IPv4 协议server_address.sin_port = htons(port);if (inet_pton(ip_version, ip.c_str(), &server_address.sin_addr) <= 0){std::cerr << "Invalid address/ Address not supported." << std::endl;closesocket(client_fd);
#ifdef _WIN32WSACleanup();
#endifreturn -1;}if (connect(client_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0){std::cerr << "Connection failed." << std::endl;closesocket(client_fd);
#ifdef _WIN32WSACleanup();
#endifreturn -1;}std::cout << "Connected to server." << std::endl;sendContent();std::ofstream outFile("server_response.txt", std::ios::app); // 创建或打开文件以追加内容if (!outFile.is_open()){std::cerr << "Failed to open file for writing." << std::endl;return -1;}std::cout << "File opened successfully: server_response.txt" << std::endl;std::thread t([this, &outFile](){char buffer[1024] = {0};std::cout << "Listening for msg" << std::endl;while (!stopListening){int valread = recv(client_fd, buffer, sizeof(buffer) - 1, 0);if (valread < 0){std::cerr << "Receive failed." << std::endl;break;}else if (valread == 0){std::cout << "Connection closed by server." << std::endl;break;}else{buffer[valread] = '\0';std::cout << "Received message from server: " << buffer << std::endl;outFile << buffer; // 将接收到的数据写入文件}memset(buffer, 0, sizeof(buffer));}outFile.close(); // 关闭文件std::cout << "File closed successfully: server_response.txt" << std::endl;closesocket(client_fd); });t.join();#ifdef _WIN32WSACleanup();
#endifreturn 0;}void sendContent(){std::string msg = "GET / HTTP/1.1\r\nHost: " + domain + "\r\nConnection: Close\r\n\r\n";int result = send(client_fd, msg.c_str(), msg.length(), 0);if (result < 0){std::cerr << "Send failed." << std::endl;}else{std::cout << "Successfully sent " << result << " bytes\n";}}
};int main()
{
#ifdef _WIN32WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){std::cerr << "Failed to initialize Winsock." << std::endl;return -1;}
#endifHttpRequest httprequest("www.baidu.com", 80);httprequest.connect_to();#ifdef _WIN32WSACleanup();
#endifreturn 0;
}

API函数

gethostbyname()

gethostbyname函数用于通过域名或主机名获取其对应的IP地址信息。它返回一个指向hostent结构体的指针,该结构体包含了主机的相关信息。

gethostbyname函数原型
#include <netdb.h>
struct hostent *gethostbyname(const char *name);
hostent结构体内容

hostent结构体定义如下:

struct hostent {char *h_name;         // 官方主机名char **h_aliases;     // 主机别名列表int h_addrtype;       // 地址类型(如AF_INET表示IPv4)int h_length;         // 地址长度char **h_addr_list;   // 地址列表
};
  • h_name:官方主机名。例如,www.baidu.com的官方主机名可能是www.a.shifen.com
  • h_aliases:主机的别名列表。一个主机可以有多个别名,这些别名也用于访问该主机。
  • h_addrtype:地址类型,通常为AF_INET表示IPv4地址。
  • h_length:地址长度,IPv4地址长度为4字节。
  • h_addr_list:地址列表,包含了主机的所有IP地址。对于IPv4地址,可以使用inet_ntoa函数将其转换为点分十进制格式。
工作原理
  • gethostbyname首先检查本地的/etc/hosts文件,如果找到匹配的主机名,则返回相应的IP地址。
  • 如果未在hosts文件中找到匹配项,它会向DNS服务器发送查询请求,以获取主机名对应的IP地址。
注意事项
  • gethostbyname只能返回IPv4地址。如果需要获取IPv6地址或同时获取IPv4和IPv6地址,应使用getaddrinfo函数。
  • 返回的hostent结构体的内存由系统管理,因此在使用完毕后不需要手动释放。
gethostbyaddr()

在Linux系统中,将IP地址转换成域名(即反向DNS解析)可以使用gethostbyaddr函数。这个函数是POSIX标准的一部分,用于根据IP地址查找对应的主机名。以下是关于gethostbyaddr函数的详细说明和使用示例:

函数原型
#include <netdb.h>
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
参数说明
  • addr:指向IP地址的指针。对于IPv4地址,应该是一个指向struct in_addr的指针;对于IPv6地址,应该是一个指向struct in6_addr的指针。
  • len:地址的长度。对于IPv4地址,长度为sizeof(struct in_addr);对于IPv6地址,长度为sizeof(struct in6_addr)
  • type:地址类型,可以是AF_INET(IPv4)或AF_INET6(IPv6)。
返回值
  • 成功时返回指向hostent结构体的指针,该结构体包含了主机的相关信息。
  • 失败时返回NULL,可以通过h_errno获取错误原因(例如使用herror函数)。
示例代码

以下是一个使用gethostbyaddr函数进行反向DNS解析的示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>int main() {struct in_addr ip_addr;struct hostent *host;// 示例IP地址inet_pton(AF_INET, "8.8.8.8", &ip_addr);// 执行反向DNS解析host = gethostbyaddr(&ip_addr, sizeof(ip_addr), AF_INET);if (host == NULL) {herror("gethostbyaddr failed");return EXIT_FAILURE;}// 输出主机名printf("Hostname: %s\n", host->h_name);return EXIT_SUCCESS;
}
注意事项
  • 在使用gethostbyaddr之前,确保系统中已经配置了DNS服务器,并且DNS服务器能够响应反向查询请求。
  • gethostbyaddr函数可能不是线程安全的,如果需要在多线程环境中使用,可以考虑使用getaddrinfo函数的反向解析功能。
inet_ntoa()
  • 功能:将IPv4地址的网络字节序二进制形式转换为点分十进制的字符串表示形式。

  • 参数:

    • struct in_addr in:包含IPv4地址的in_addr结构体。
  • 返回值:

    • 返回指向转换后的字符串的指针。该字符串是静态分配的,因此每次调用inet_ntoa都会覆盖上一次的结果
  • 注意事项

    • inet_ntoa只支持IPv4地址,且返回的字符串是静态分配的,因此在多线程环境中使用时需要特别注意。
    • 对于IPv6地址或需要线程安全的场景,推荐使用inet_ptoninet_ntop
struct in_addr ip_addr;
inet_pton(AF_INET, "192.168.1.1", &ip_addr);
char *ip_str = inet_ntoa(ip_addr);
printf("IP address: %s\n", ip_str);
inet_ntop()
  • 功能:将IP地址的网络字节序二进制形式转换为字符串表示形式。
  • 参数:
    • int af:地址族,可以是AF_INET(IPv4)或AF_INET6(IPv6)。
    • const void *src:指向二进制IP地址的指针。
    • char *dst:指向用于存储转换后的字符串的缓冲区的指针。
    • socklen_t size:缓冲区的大小。
  • 返回值:
    • 成功时返回指向dst的指针。
    • 失败时返回NULL
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>int main() {struct in_addr ip_addr;inet_pton(AF_INET, "192.168.1.1", &ip_addr);char ip_str[INET_ADDRSTRLEN];const char *result = inet_ntop(AF_INET, &ip_addr, ip_str, INET_ADDRSTRLEN);if (result == NULL) {perror("inet_ntop");exit(EXIT_FAILURE);}printf("IP address in string format: %s\n", ip_str);return 0;
}
inet_pton()
  • 功能:将IP地址的字符串表示形式转换为网络字节序的二进制形式。
  • 参数:
    • int af:地址族,可以是AF_INET(IPv4)或AF_INET6(IPv6)。
    • const char *src:指向IP地址字符串的指针。
    • void *dst:指向存储转换后的二进制地址的缓冲区的指针。
  • 返回值:
    • 成功时返回1。
    • 输入字符串无效时返回0。
    • 出错时返回-1。
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>int main() {struct in_addr ip_addr;int result = inet_pton(AF_INET, "192.168.1.1", &ip_addr);if (result != 1) {perror("inet_pton");exit(EXIT_FAILURE);}printf("IP address in binary format: %u.%u.%u.%u\n",(unsigned char)ip_addr.s_addr,(unsigned char)(ip_addr.s_addr >> 8),(unsigned char)(ip_addr.s_addr >> 16),(unsigned char)(ip_addr.s_addr >> 24));return 0;
}

http://www.ppmy.cn/server/158191.html

相关文章

8 事件等待

临界区&自旋锁 这两个章节在”多核同步“篇已经学习过了,需要了解的可以自行查看对应章节。 线程等待与唤醒 我们在之前的课程里面了解了如何自己实现临界区以及什么是Windows自旋锁,这两种同步方案在线程无法进入临界区时都会让当前线程进入等待状态。 一种是通过Sl…

MySQL程序之:简要概述

MySQL安装中有许多不同的程序。本节简要概述了它们。后面的部分提供了每个程序的更详细描述&#xff0c;但NDB集群程序除外。每个程序的描述表明了它的调用语法和它支持的选项。&#xff0c;“NDB集群程序”&#xff0c;描述了特定于NDB集群的程序。 大多数MySQL发行版包括所有…

SQL HAVING 子句深入解析

SQL HAVING 子句深入解析 介绍 SQL&#xff08;Structured Query Language&#xff09;是一种用于管理关系数据库管理系统的标准编程语言。在SQL中&#xff0c;HAVING子句是与GROUP BY子句一起使用的&#xff0c;用于筛选分组后的数据。它根据聚合函数的结果对组进行条件过滤…

springboot和vue配置https请求

项目场景&#xff1a; 代码发布到线上使用https请求需要配置ssl证书&#xff0c;前后端都需要修改。 问题描述 如图&#xff0c;我们在调用接口时报如下错误&#xff0c;这就是未配置ssl但是用https请求产生的问题。 解决方案&#xff1a; 前端&#xff1a;在vite.config.js文…

MATLAB安装Robotics Toolbox(机器人工具箱)插件

一、下载工具箱安装包http://petercorke.com/wordpress/toolboxes/robotics-toolbox 二、将文件夹放到MATLAB安装文件夹指定目录下 三、打开MATLAB&#xff0c;主页------设置路径-----选添加并包含子文件夹-------选择这个rvctools文件夹save&#xff08;保存&#xff09;-clo…

牛客网刷题 ——C语言初阶(6指针)——BC105 矩阵相等判定

1. 题目描述&#xff1a;BC105 矩阵相等判定 牛客网OJ题链接 描述&#xff1a; KiKi得到了两个n行m列的矩阵&#xff0c;他想知道两个矩阵是否相等&#xff0c;请你回答他。(当两个矩阵对应数组元素都相等时两个矩阵相等)。 示例1 输入&#xff1a; 2 2 1 2 3 4 1 2 3 4 输出…

25/1/12 嵌入式笔记 学习esp32

了解了一下位选线和段选线的知识&#xff1a; 位选线&#xff1a; 作用&#xff1a;用于选择数码管的某一位&#xff0c;例如4位数码管的第1位&#xff0c;第2位&#xff09; 通过控制位选线的电平&#xff08;高低电平&#xff09;&#xff0c;决定当前哪一位数码管处于激活状…

二、BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…