」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 用 C 語言建立一個簡單的 TCP 伺服器

用 C 語言建立一個簡單的 TCP 伺服器

發佈於2024-11-08
瀏覽:304

Building a Simple TCP Server in C

在这篇博文中,我们将探索如何用 C 语言创建一个可以提供 HTML 文件的简单 TCP 服务器。我们将分解代码,解释其工作原理,并讨论增强该项目的未来计划。这是一个很好的例子,说明了如何在 C 中“只做事情”而不会使过程过于复杂!

项目概况

该项目的目标是实现一个基本的 TCP 服务器,用于侦听客户端连接并根据请求提供 HTML 文件。服务器将处理客户端请求,读取指定的 HTML 文件,并将内容作为 HTTP 响应发送回客户端。

?在 Twitter(X) 上继续对话:@trish_07

? GitHub 存储库:探索 TCP 服务器项目存储库

项目结构

为了组织我们的代码,我们将按如下方式构建项目:

tcp_server_c/
├── CMakeLists.txt             # Build configuration
├── include/
│   ├── server.h               # Main server header file
│   ├── html_serve.h           # Header for serve_html function
│   ├── request_handler.h      # Header for handle_client function
│   └── socket_utils.h         # Header for socket utility functions
├── src/
│   ├── server.c               # Main server program
│   ├── html_serve.c           # serve_html function
│   ├── request_handler.c      # handle_client function
│   └── socket_utils.c         # Utility functions for socket operations
└── README.md                  # Project documentation

代码分解

1. 套接字实用程序

首先,让我们创建一个实用程序文件来处理套接字初始化。这将确保我们的主服务器代码保持干净和专注。

include/socket_utils.h

#ifndef SOCKET_UTILS_H
#define SOCKET_UTILS_H

#include 

int initialize_server(struct sockaddr_in* address);

#endif

src/socket_utils.c

#include "socket_utils.h"
#include 
#include 
#include 
#include 

#define PORT 8080

int initialize_server(struct sockaddr_in* address) {
    int server_fd;
    int opt = 1;

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed!");
        return -1;
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) != 0) {
        perror("setsockopt failed");
        close(server_fd);
        return -1;
    }

    address->sin_family = AF_INET;
    address->sin_addr.s_addr = INADDR_ANY;
    address->sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)address, sizeof(*address)) 



2. HTML 服务功能

接下来,我们将创建一个服务 HTML 文件的函数。此函数将读取 HTML 文件的内容并将其返回给调用者。

include/html_server.h

#ifndef HTML_SERVER_H
#define HTML_SERVER_H

char* serve_html(const char* filename);

#endif

src/html_server.c

#include "html_server.h"
#include 
#include 
#include 

char* serve_html(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (!file) {
        perror("Error opening file");
        return NULL;
    }

    fseek(file, 0, SEEK_END);
    long length = ftell(file);
    fseek(file, 0, SEEK_SET);

    char* buffer = malloc(length   1);
    if (!buffer) {
        perror("Error allocating memory");
        fclose(file);
        return NULL;
    }

    fread(buffer, 1, length, file);
    buffer[length] = '\0'; // Null-terminate the buffer

    fclose(file);
    return buffer;
}

3. 处理客户请求

现在,让我们实现处理传入客户端请求的逻辑。

include/request_handler.h

#ifndef REQUEST_HANDLER_H
#define REQUEST_HANDLER_H

#include 

void handle_client(int new_socket);

#endif

src/request_handler.c

#include "request_handler.h"
#include "html_server.h"
#include 
#include 
#include 

#define BUFFER_SIZE 1024

void handle_client(int new_socket) {
    char buffer[BUFFER_SIZE] = { 0 };
    read(new_socket, buffer, BUFFER_SIZE);

    // Serve the HTML file
    char* html_content = serve_html("../html/index.html");
    if (html_content) {
        write(new_socket, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n", 48);
        write(new_socket, html_content, strlen(html_content));
    } else {
        const char* not_found_response = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n

404 Not Found

"; write(new_socket, not_found_response, strlen(not_found_response)); } free(html_content); close(new_socket); // Close the connection with the current client }

4. 主服务器逻辑

最后,让我们将所有内容放在主文件中。

src/main.c

#include 
#include 
#include 
#include 
#include 
#include "socket_utils.h"
#include "request_handler.h"

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    server_fd = initialize_server(&address);
    if (server_fd == -1) {
        return EXIT_FAILURE;
    }

    printf("Server listening on port: 8080\n");

    while (1) {
        if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) 



未来计划

展望未来,我们计划实施多项增强功能和功能:

  1. 多线程支持:为了同时处理多个客户端连接,我们将引入线程功能来提高服务器的效率。
  2. 动态内容服务:通过与轻量级模板引擎集成来实现提供动态内容的功能。
  3. 日志记录:添加日志记录机制来跟踪请求、错误和服务器性能。
  4. 安全功能:探索添加 HTTPS 支持和输入验证以增强安全性。
  5. 改进的错误处理:针对各种场景实现更好的错误处理,例如文件未找到、服务器过载等

结论

这个简单的 TCP 服务器项目作为如何用 C 创建 Web 服务器的基础示例,展示了该语言的强大功能和简单性。通过在此基础上构建,我们可以开发更复杂的功能并提高性能,使其成为服务 Web 内容的强大解决方案。

您可以在 GitHub 上找到完整的源代码并为该项目做出贡献:GitHub 存储库链接。

请随时提供反馈、提出问题或为未来的改进贡献您的想法!


版本聲明 本文轉載於:https://dev.to/trish_07/building-a-simple-tcp-server-in-c-2h85如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何在Java字符串中有效替換多個子字符串?
    如何在Java字符串中有效替換多個子字符串?
    在java 中有效地替換多個substring,需要在需要替換一個字符串中的多個substring的情況下,很容易求助於重複應用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    程式設計 發佈於2025-03-12
  • 如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    postgresql:為每個唯一標識符在postgresql中提取最後一行,您可能需要遇到與數據集合中每個不同標識的信息相關的信息。考慮以下數據:[ 1 2014-02-01 kjkj 在數據集中的每個唯一ID中檢索最後一行的信息,您可以在操作員上使用Postgres的有效效率: id dat...
    程式設計 發佈於2025-03-12
  • PHP陣列鍵值異常:了解07和08的好奇情況
    PHP陣列鍵值異常:了解07和08的好奇情況
    PHP數組鍵值問題,使用07&08 在給定數月的數組中,鍵值07和08呈現令人困惑的行為時,就會出現一個不尋常的問題。運行print_r($月份)返回意外結果:鍵“ 07”丟失,而鍵“ 08”分配給了9月的值。 此問題源於PHP對領先零的解釋。當一個數字帶有0(例如07或08)的前綴時,PHP...
    程式設計 發佈於2025-03-12
  • Python命名元組詳解:對比普通元組的優勢
    Python命名元組詳解:對比普通元組的優勢
    命名的元組是輕量級且易於創建的對像類型,可通過提供命名屬性來增強該元素的可用性。讓我們深入研究其用法和與常規元素的比較。 的創建和命名元組的用法創建命名元組,我們使用collections.namedtuple Factory函數。例如,為要點定義一個命名元組:可以像常規元組一樣創建此名稱元組的實...
    程式設計 發佈於2025-03-12
  • 哪種方法更有效地用於點 - 填點檢測:射線跟踪或matplotlib \的路徑contains_points?
    哪種方法更有效地用於點 - 填點檢測:射線跟踪或matplotlib \的路徑contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    程式設計 發佈於2025-03-12
  • VS Code & Delve 調試Go代碼:Build Tags配置指南
    VS Code & Delve 調試Go代碼:Build Tags配置指南
    在Visual Studio Code中使用標籤進行調試,並在使用構建標籤來編譯GO程序的各種版本時,請在delve debugger 中使用,這對於配置DEBUGGER以配置最佳debugger以進行最佳利用。標籤:在Visual Studio Code的GO插件中指定構建標籤,您可以使用bui...
    程式設計 發佈於2025-03-12
  • 如何在JavaScript對像中動態設置鍵?
    如何在JavaScript對像中動態設置鍵?
    在嘗試為JavaScript對象創建動態鍵時,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正確的方法採用方括號: jsobj ['key''i] ='example'1; 在JavaScript中,數組是一...
    程式設計 發佈於2025-03-12
  • HTML格式標籤
    HTML格式標籤
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    程式設計 發佈於2025-03-12
  • 為什麼儘管有效代碼,為什麼在PHP中捕獲輸入?
    為什麼儘管有效代碼,為什麼在PHP中捕獲輸入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,輸出...
    程式設計 發佈於2025-03-12
  • 為什麼不使用CSS`content'屬性顯示圖像?
    為什麼不使用CSS`content'屬性顯示圖像?
    在Firefox extemers屬性為某些圖像很大,&& && && &&華倍華倍[華氏華倍華氏度]很少見,卻是某些瀏覽屬性很少,尤其是特定於Firefox的某些瀏覽器未能在使用內容屬性引用時未能顯示圖像的情況。這可以在提供的CSS類中看到:。 googlepic { 內容:url(&...
    程式設計 發佈於2025-03-12
  • 為什麼我會收到MySQL錯誤#1089:錯誤的前綴密鑰?
    為什麼我會收到MySQL錯誤#1089:錯誤的前綴密鑰?
    mySQL錯誤#1089:錯誤的前綴鍵錯誤descript [#1089-不正確的前綴鍵在嘗試在表中創建一個prefix鍵時會出現。前綴鍵旨在索引字符串列的特定前綴長度長度,可以更快地搜索這些前綴。 了解prefix keys `這將在整個Movie_ID列上創建標準主鍵。主密鑰對於唯一識...
    程式設計 發佈於2025-03-12
  • \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    答案: 在大多數現代編譯器中,while(1)和(1)和(;;)之間沒有性能差異。編譯器: perl: 1 輸入 - > 2 2 NextState(Main 2 -E:1)V-> 3 9 Leaveloop VK/2-> A 3 toterloop(next-> 8 last-> 9 ...
    程式設計 發佈於2025-03-12
  • 為什麼PYTZ最初顯示出意外的時區偏移?
    為什麼PYTZ最初顯示出意外的時區偏移?
    與pytz 最初從pytz獲得特定的偏移。例如,亞洲/hong_kong最初顯示一個七個小時37分鐘的偏移: 差異源利用本地化將時區分配給日期,使用了適當的時區名稱和偏移量。但是,直接使用DateTime構造器分配時區不允許進行正確的調整。 example pytz.timezone(&#...
    程式設計 發佈於2025-03-12
  • Laravel要去:我的旅程和纖維API樣板的創建
    Laravel要去:我的旅程和纖維API樣板的創建
    花費四年以上,我對MVC(Model-View-Controller)架構非常熟悉。它的簡單性和結構使與之合作變得很高興,而Laravel的有條理的文件夾可幫助開發人員保持正軌。您始終知道將代碼放置在哪裡,以及廣泛的內置工具 - 數據庫連接,redis,排隊,遷移,ORM等等 - 將設置無縫。只需...
    程式設計 發佈於2025-03-12
  • 如何使用替換指令在GO MOD中解析模塊路徑差異?
    如何使用替換指令在GO MOD中解析模塊路徑差異?
    在使用GO MOD時,在GO MOD 中克服模塊路徑差異時,可能會遇到衝突,其中可能會遇到一個衝突,其中3派對軟件包將另一個帶有導入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    程式設計 發佈於2025-03-12

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3