【C語言網絡編程】HTTP 客戶端請求(發送請求報文過程)

在 C 語言中,我們可以使用 socket 編程來手動實現一個簡單的 HTTP 客戶端,像瀏覽器一樣請求網頁數據。本文將結合實際代碼,重點講解如何通過 C 語言構造并發送一個 HTTP 請求報文,實現與服務器的基本通信。

文章目標

通過一個簡單的 http_send_request() 函數,我們將實現以下流程:

  1. 將域名(如 "www.baidu.com")解析成 IP 地址

  2. 與目標服務器建立 TCP 連接(80 端口)

  3. 構造 HTTP 請求報文并發送給服務器

一、代碼結構總覽

#define HTTP_VERSION        "HTTP/1.1"
#define CONNETION_TYPE      "Connection:close\r\n"
#define BUFFER_SIZE         4096

我們使用 HTTP/1.1 協議,連接類型為短連接(發送請求后關閉)。

二、域名解析函數:host_to_ip

char *host_to_ip(const char *hostname) {struct hostent *host_entry = gethostbyname(hostname);  // DNS 查詢if (host_entry) {return inet_ntoa(*(struct in_addr*)host_entry->h_addr_list[0]);  // 返回IP字符串}return NULL;
}
  • gethostbyname() 負責 DNS 解析

  • inet_ntoa() 將原始 IP 地址(二進制)轉換為點分十進制字符串,如 "14.215.177.39"

三、創建并連接 Socket:http_create_socket

int http_create_socket(char *ip) {int sockfd = socket(AF_INET, SOCK_STREAM, 0);  // 創建 TCP socketstruct sockaddr_in sin = {0};sin.sin_family = AF_INET;sin.sin_port = htons(80);                     // 設置端口:HTTP 默認 80sin.sin_addr.s_addr = inet_addr(ip);          // 將 IP 字符串轉換為網絡地址if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(sin))) {return -1;  // 連接失敗}fcntl(sockfd, F_SETFL, O_NONBLOCK);           // 設置非阻塞模式(可選)return sockfd;
}

四、發送 HTTP 請求:http_send_request

這是本文的重點,完整代碼如下:

char * http_send_request(const char *hostname, const char *resource) {char *ip = host_to_ip(hostname);               // 1. 域名轉 IPint sockfd = http_create_socket(ip);           // 2. 創建 TCP 連接char buffer[BUFFER_SIZE] = {0};                // 3. 準備請求報文緩沖區// 4. 構造 HTTP GET 請求報文sprintf(buffer,"GET %s %s\r\n""Host: %s\r\n""%s\r\n",resource, HTTP_VERSION, hostname, CONNETION_TYPE);// 5. 發送請求數據send(sockfd, buffer, strlen(buffer), 0);return NULL;  // 當前版本未實現接收部分
}

五、HTTP 報文解析說明

通過 sprintf() 構造的請求報文如下所示(舉例):

GET /index.html HTTP/1.1
Host: www.baidu.com
Connection: close

它由以下部分組成:

行數內容說明
第1行請求行指定方法、資源路徑、協議版本
第2行Host 頭告訴服務器你訪問的是哪個域名
第3行Connection 頭表示用完連接后立即關閉
空行必須表示請求頭結束,開始正文(此處沒有正文)

\r\n 是 HTTP 標準要求的換行符,不能用 \n 替代。

六、http_send_request() 函數流程圖

開始││ 輸入參數:hostname 和 resource│├─? 1. 通過 host_to_ip(hostname)│     └─ DNS 查詢 → 獲取 IP 地址(如 "14.215.177.39")│├─? 2. 調用 http_create_socket(ip)│     └─ 創建 TCP socket 并連接服務器 80 端口│├─? 3. 構造 HTTP 請求報文│     └─ 格式如下:│         GET /resource HTTP/1.1│         Host: hostname│         Connection: close│├─? 4. 使用 send() 發送請求數據到 socket│└─? 5. 當前版本未實現 recv(),結束函數

域名 → IP → TCP連接 → 構造請求 → 發送數據

七、完整代碼

#define HTTP_VERSION        "HTTP/1.1"              // 指定使用的 HTTP 協議版本
#define CONNETION_TYPE      "Connection:close\r\n"  // 設置連接類型為關閉連接(短連接)#define BUFFER_SIZE 4096                             // 定義請求緩沖區大小// 將主機名(域名)轉換為 IP 地址字符串
char *host_to_ip(const char *hostname) {struct hostent *host_entry = gethostbyname(hostname);   // 調用 DNS 查詢函數// 如果查詢成功,返回對應 IP 地址(點分十進制字符串)// h_addr_list 是 IP 地址列表,取第一個并轉換為字符串if (host_entry) {return inet_ntoa((struct in_addr*)*host_entry->h_addr_list);}// 查詢失敗返回 NULLreturn NULL;
}// 創建一個 TCP socket 并連接到指定 IP 地址的 80 端口
int http_create_socket(char *ip) {int sockfd = socket(AF_INET, SOCK_STREAM, 0);     // 創建 TCP socketstruct sockaddr_in sin = {0};                     // 初始化服務器地址結構sin.sin_family = AF_INET;                         // 使用 IPv4 協議sin.sin_port = htons(80);                         // 設置端口為 80,使用 htons 轉換為網絡字節序sin.sin_addr.s_addr = inet_addr(ip);              // 將 IP 字符串轉換為網絡字節序// 嘗試連接服務器if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {return -1;                                     // 連接失敗則返回 -1}fcntl(sockfd, F_SETFL, O_NONBLOCK);               // 設置 socket 為非阻塞模式(可選)return sockfd;                                    // 返回連接成功的 socket 文件描述符
}// 構造并發送一個 HTTP GET 請求
char * http_send_request(const char *hostname, const char *resource) {char *ip = host_to_ip(hostname);                  // 第一步:通過域名獲取 IP 地址int sockfd = http_create_socket(ip);              // 第二步:創建并連接 socket 到服務器char buffer[BUFFER_SIZE] = {0};                   // 初始化發送緩沖區// 第三步:構造 HTTP 請求報文// 組成部分包括請求行、Host 頭部、Connection 頭部sprintf(buffer,"GET %s %s\r\n"           // 請求行:GET /path HTTP/1.1"Host: %s\r\n"            // Host 頭:指定服務器域名"%s\r\n",                 // Connection: close(關閉連接)resource, HTTP_VERSION,hostname,CONNETION_TYPE);// 第四步:通過 socket 發送請求報文send(sockfd, buffer, strlen(buffer), 0);return NULL; // 當前函數版本沒有實現響應接收,暫時返回 NULL
}

https://github.com/0voice

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89309.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89309.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89309.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

oracle2kingbase的字段長度問題

實驗一: oracle中: create table testlen(c1 varchar2(2)); insert into testlen values(山); --成功 insert into testlen values(山西); --失敗 ORA-12899: 列 "TESTK"."TESTLEN"."C1" 的值太大 (實際值: 4, 最大值: 2…

單鏈表的題目,咕咕咕

1.咕 203. 移除鏈表元素 - 力扣(LeetCode) 給你一個鏈表的頭節點 head 和一個整數 val ,請你刪除鏈表中所有滿足 Node.val val 的節點,并返回 新的頭節點 struct ListNode* removeElements(struct ListNode* head, int val) …

關于程序=數據結構+算法這句話最近的一些思考

最近看了很多單片機STM32的的相關程序,尤其是設計到ringbuff、buffer_manage、os_memory預計mem_manage等程序中間層的用法,我對這句話有了一些更深的思考,現在記錄下來,希望對處于相同階段的程序一些思想啟迪。首先“數據結構”也…

Rust 錯誤處理

Rust 錯誤處理 引言 Rust 是一種系統編程語言,以其安全、并發和性能著稱。在 Rust 中,錯誤處理是一個核心概念,它確保了程序在遇到異常情況時能夠優雅地處理。本文將深入探討 Rust 中的錯誤處理機制,包括錯誤類型、錯誤傳播、錯誤…

17. 什么是 webSocket ?

總結 WebSocket 是 HTML5 引入的一種新協議,允許客戶端和服務器之間進行雙向實時通信。建立在 TCP 協議之上,默認端口是 80(ws) 和 443(wss),沒有同源限制,客戶端可以與任意服務器通…

從零開始跑通3DGS教程:(五)3DGS訓練

寫在前面 本文內容 所屬《從零開始跑通3DGS教程》系列文章; 本文介紹在docker中訓練3dgs的方法 平臺/環境 linux, nvidia GPU, docker 轉載請注明出處: https://blog.csdn.net/qq_41102371/article/details/146535874 目錄 寫在前面系列文章準備docker創建環境參考完系列文章…

日記_7.14_實際開發的進步

1、快速定位后端2、會定位前端啦啦啦!3、前端沒有意義的塊叫div和span。而不是script4、所有 JavaScript 標識符均 區分大小寫5、JS中$和_下劃線和doller符均被視為字母。6、var、let區別:1 var全局。let局部。2 var可以重新聲明格式,let之恩…

AI Agent 開發

Agent開發常用框架: LangChainLlamaIndexVercel AI SDK LangChain:一站式 LLM 應用開發框架一句話總結 LangChain 把「模型調用 外部數據 工具 記憶 流程編排」全部標準化,讓你像搭積木一樣快速組合出聊天機器人、RAG、Agent 等大模型應用…

【水動力學】04 二維洪水淹沒模型Pypims安裝

模型介紹 HiPIMS(High-Performance Integrated hydrodynamic Modelling System)使用最先進的數值方案(Godunov型有限體積法)來求解二維淺水方程以進行洪水模擬。為了支持高分辨率洪水模擬,使用CUDA/C 語言在多個GPU上…

ARC 03 從Github Action job 到 runner pod

Github Action job 分配到集群 背景 job 是 Github Action 的基本單位,每個 job 單獨分配一個 runner。workflow 由一個或者多個 job 組成。如果用戶觸發runs-on字段為arc-runner-set的 job,那么 Github Action 服務器將 job 分配給 listener pod。 源碼…

ubuntu 22.04 anaconda comfyui安裝

背景: 戴爾R740服務器,安裝了proxmox操作系統,配置了顯卡直通。創建了一個ubuntu 22.04 VM虛擬機實例,并安裝了顯卡驅動與cuda等相關配置: 接下來準備搭建一套comfyui的環境,前段時間B站,抖音各…

每日面試題04:volatile字段的原理

在之前面試題02ConcurrentHashMap的底層原理中提到了volatile修飾符,在多線程編程的世界里,數據同步是一道繞不開的坎。當多個線程同時操作共享變量時,“看不見對方的修改”或“代碼順序錯亂”往往會導致程序行為異常。而 volatile作為 Java …

【云原生網絡】Istio基礎篇

文章目錄概述基礎知識技術架構概述數據平面核心組件網絡代理Envoy控制平面核心組件xDS協議Pilot組件其他概述參考博客😊點此到文末驚喜?? 概述 基礎知識 背景知識 服務網格(Service Mesh):獨立于應用程序的基礎設施層&#x…

PySpark Standalone 集群

一、PySpark Standalone 集群概述PySpark Standalone 集群是 Apache Spark 的一種部署模式,它不依賴于其他資源管理系統(如 YARN 或 Mesos),而是使用 Spark 自身的集群管理器。這種模式適合快速部署和測試,尤其在開發和…

圖像質量評價(Image Quality Assessment,IQA)

文章目錄圖像質量評價(Image Quality Assessment,IQA)一、評估方式:主觀評估 客觀評估1.1、主觀評估方式1.2、客觀評估方式:全參考 半參考 無參考(1)全參考的方法對比(Full-Refer…

【跟我學YOLO】(2)YOLO12 環境配置與基本應用

歡迎關注『跟我學 YOLO』系列 【跟我學YOLO】(1)YOLO12:以注意力為中心的物體檢測 【跟我學YOLO】(2)YOLO12 環境配置與基本應用 【跟我學YOLO】(3)YOLO12 用于診斷視網膜病變 【跟我學YOLO】&a…

Python爬蟲實戰:研究openpyxl庫相關技術

1. 引言 在當今數字化時代,互聯網上蘊含著海量有價值的數據。如何高效地獲取這些數據并進行分析處理,成為數據科學領域的重要研究方向。網絡爬蟲作為一種自動化的數據采集工具,可以幫助我們從網頁中提取所需的信息。而 openpyxl 作為 Python 中處理 Excel 文件的優秀庫,能…

Redis學習其一

文章目錄1.NoSQL概述1.1概述1.2Nosql的四大分類2.Redis入門2.1概述2.2基礎知識2.2.1基礎命令/語法2.2.2Redis為什么單線程還這么快2.3性能測試3.五大數據類型3.1Redis-key3.2String(字符串)3.3List(列表)3.4Set(集合)3.5Hash(哈希)3.6Zset(有…

高性能架構模式——高性能緩存架構

目錄 一、引入前提二、緩存架構的設計要點2.1、緩存穿透2.1.1、緩存穿透第一種情況:存儲數據不存在2.1.2、緩存穿透第二種情況:緩存數據生成耗費大量時間或者資源2.2、緩存雪崩2.2.1、解決緩存雪崩的第一種方法:更新鎖機制2.2.2、解決緩存雪崩的第二種方法:后臺更新機制2.3…

ubuntu+windows雙系統恢復

文章目錄前言一、恢復windows1.直接在grub命令行輸入exit退出2.手動查找windows引導文件先ls列出所有磁盤和分區查找各個分區是否包含引導文件設置引導分區以及引導文件路徑啟動windows二、在windows系統下刪除Ubuntu殘留引導文件三、準備ubuntu系統引導盤四、安裝ubuntu系統五…