Linux C語言網絡編程詳細入門教程:如何一步步實現TCP服務端與客戶端通信

文章目錄

  • Linux C語言網絡編程詳細入門教程:如何一步步實現TCP服務端與客戶端通信
    • 前言
    • 一、網絡通信基礎概念
    • 二、服務端與客戶端的完整流程圖解
    • 三、每一步的詳細講解和代碼示例
      • 1. 創建Socket(服務端和客戶端都要)
      • 2. 綁定本地地址和端口(服務端用bind)
      • 3. 設置監聽(服務端listen)
      • 4. 等待并接受連接(服務端accept)
      • 5. 客戶端發起連接(connect)
      • 6. 數據收發(read/write)
      • 7. 關閉socket
    • 四、流程圖&示意
    • 五、錯誤處理機制(errno、perror、strerror)
    • 六、完整最簡服務端和客戶端代碼范例
      • 1. 服務端示例
      • 2. 客戶端示例
    • 七、通用經驗和常見問題排查
    • 八、圖解數據流和socket關系
    • 九、總結

Linux C語言網絡編程詳細入門教程:如何一步步實現TCP服務端與客戶端通信


前言

本文面向初學者,目標是讓你“一看就懂、能馬上動手實踐”。從零講起,手把手梳理服務端與客戶端的構建全過程,每個函數、參數、典型用法、數據流向、底層機制、注意事項全部細細拆解,讓你徹底明白如何讓兩個程序通過網絡可靠通信。


一、網絡通信基礎概念

  1. 什么是Socket?

    • Socket(套接字)是操作系統為進程之間通過網絡發送和接收數據而提供的一套“接口”
    • 類比現實世界,就是一臺機器(主機)上的“電話插孔”,只有插上電話線(建立連接)你們才能說話。
    • Socket抽象了所有底層的網絡細節,為開發者提供了“像讀寫文件一樣”進行網絡通信的方式。
  2. IP和端口

    • IP:主機的唯一網絡地址,相當于“電話號碼”。
    • 端口:主機內部區分不同網絡服務的編號,相當于“分機號”。
  3. TCP通信流程(面向連接)

    • 服務端先開啟,監聽一個IP+端口。
    • 客戶端主動連接服務端的IP+端口。
    • 建立連接(三次握手)。
    • 雙方可以互相發送和接收數據。
    • 通信完成后關閉連接。

二、服務端與客戶端的完整流程圖解

服務端主要流程:

  1. 創建Socket
  2. 綁定本地IP和端口(bind)
  3. 設置為監聽狀態(listen)
  4. 死循環等待客戶端連接(accept)
  5. 收發數據(read/write)
  6. 關閉通信(close)

客戶端主要流程:

  1. 創建Socket
  2. 配置服務器IP和端口
  3. 發起連接請求(connect)
  4. 收發數據(read/write)
  5. 關閉通信(close)

三、每一步的詳細講解和代碼示例

1. 創建Socket(服務端和客戶端都要)

作用:告訴內核“我要用網絡通信”,創建一個通信端點。

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
  • domain:協議族,常用AF_INET(IPv4)。
  • type:套接字類型,常用SOCK_STREAM(TCP,可靠流)。
  • protocol:協議,通常填0,表示由系統自動選擇。

舉例:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket error");exit(1);
}
  • 返回值:成功時是一個“文件描述符”,失敗返回-1。

2. 綁定本地地址和端口(服務端用bind)

作用:明確告訴操作系統“我用哪個IP+端口”來等待客戶端連接。只有bind了,別人才能找到你。

#include <netinet/in.h>
#include <string.h>
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;                   // 協議族
servaddr.sin_port = htons(8888);                 // 端口(本地字節序轉網絡字節序)
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    // 本機所有IPint ret = bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret == -1) {perror("bind error");exit(1);
}
  • htons/htonl 是將主機字節序轉為網絡字節序(大端)。
  • INADDR_ANY 讓你的服務監聽本機所有網卡(IP)。

3. 設置監聽(服務端listen)

作用:讓socket進入“監聽”狀態,準備接收連接請求。

int listen(int sockfd, int backlog);
  • sockfd:剛才創建并bind過的socket。
  • backlog:內核排隊等待連接的最大數量。

舉例:

if (listen(sockfd, 128) == -1) {perror("listen error");exit(1);
}
  • 這時操作系統會幫你排隊管理那些“想要連你”的客戶端。

4. 等待并接受連接(服務端accept)

作用:阻塞等待客戶端“來電”,接聽一個新連接,為每個連接分配一個新的socket。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd:監聽socket。
  • addr:傳出參數,獲取客戶端的IP+端口等信息。
  • addrlen:addr結構體大小(調用前要賦初值)。

舉例:

struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr*)&cliaddr, &cliaddr_len);
if (connfd == -1) {perror("accept error");continue; // 或exit(1)
}
  • connfd:每個客戶端連接都會分配一個新socket,獨立通信。
  • 注意:監聽socket(sockfd)只負責“等電話”,不能直接收發數據,后面通信都用connfd。

5. 客戶端發起連接(connect)

作用:主動“打電話”給服務端,發起三次握手,連接指定IP+端口。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

客戶端配置服務器地址:

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8888);
inet_pton(AF_INET, "192.168.1.100", &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("connect error");exit(1);
}
  • inet_pton 用于將點分十進制IP字符串轉為網絡字節序整數。

6. 數據收發(read/write)

作用:收發字節流數據,就像讀寫文件一樣。

  • write() 發送數據到對方
  • read() 從對方接收數據

例子(雙方都類似):

char buf[1024];
// 發送
write(sockfd, "hello", 5);
// 接收
int n = read(sockfd, buf, sizeof(buf)-1);
if (n > 0) {buf[n] = '\0';printf("收到: %s\n", buf);
}
  • 注意:read和write返回值要判斷,<=0說明對方關閉了連接或出錯。
  • 服務器處理建議:每個連接完成后記得close(connfd)。

7. 關閉socket

作用:釋放系統資源,斷開連接。

close(sockfd); // 對服務端監聽socket、通信socket、客戶端socket都適用

四、流程圖&示意

服務器端流程                      客戶端流程
--------------------------------------------------------
socket()                         socket()|                                 |
bind()                          connect()|                                 |
listen()                            ||                           ---------三次握手
accept() <--------+            ||                |           |
read()/write() <---+---> read()/write()|                |           |
close()         close()      close()

五、錯誤處理機制(errno、perror、strerror)

  1. 每一步系統調用都可能出錯,一定要檢查返回值(-1 代表失敗)。
  2. 出錯后操作系統會設置errno變量(int類型,存放錯誤碼)
  3. perror(“描述”) 會直接把你的描述和錯誤原因一起打印到標準錯誤輸出。
  4. strerror(errno) 把錯誤碼轉為字符串,可以寫日志或文件。

例子:

if (bind(sockfd, ...) == -1) {perror("bind error");// fprintf(logfile, "bind error: %s\n", strerror(errno));exit(1);
}
  • 建議每個步驟都這樣處理,排查故障時一清二楚。

六、完整最簡服務端和客戶端代碼范例

1. 服務端示例

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>#define SERVER_PORT 8888int main() {int listenfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERVER_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("bind error");exit(1);}if (listen(listenfd, 128) == -1) {perror("listen error");exit(1);}printf("Server is listening...\n");while (1) {struct sockaddr_in cliaddr;socklen_t cliaddr_len = sizeof(cliaddr);int connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);if (connfd == -1) {perror("accept error");continue;}char buf[1024];int n = read(connfd, buf, sizeof(buf)-1);if (n > 0) {buf[n] = '\0';printf("client says: %s\n", buf);write(connfd, buf, n); // 回顯}close(connfd);}close(listenfd);return 0;
}

2. 客戶端示例

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>#define SERVER_PORT 8888
#define SERVER_IP   "127.0.0.1"int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("connect error");exit(1);}char sendbuf[1024] = "hello server";write(sockfd, sendbuf, strlen(sendbuf));char recvbuf[1024];int n = read(sockfd, recvbuf, sizeof(recvbuf)-1);if (n > 0) {recvbuf[n] = '\0';printf("server says: %s\n", recvbuf);}close(sockfd);return 0;
}

七、通用經驗和常見問題排查

  1. 端口被占用,bind出錯:先用netstat -ntlp查端口占用,或改端口再試。
  2. connect失敗:IP、端口寫錯?服務器沒開?防火墻攔截?
  3. read/write出錯或為0:對方關閉了連接,需及時close。
  4. 每個連接用獨立socket,主循環不要關listenfd。
  5. 大項目建議引入多線程或select/epoll提升并發能力。

八、圖解數據流和socket關系

| 客戶端 |      網絡      |         服務端         |
+--------+---------------+-----------------------+
|        | ---connect--> | [listenfd]            |
|        | <---三次握手--|                       |
|        |               | accept()產生[connfd]  |
|        |<->read/write<>| [connfd]<->[listenfd] |
|        | ---close----->| [connfd closed]       |
  • listenfd負責排隊監聽,不用于通信。connfd負責與客戶端通信。

九、總結

  1. 先socket(),再bind()(服務端),然后listen(),accept()連接,read/write通信,close()結束。客戶端用connect()主動連接。
  2. 每一步都檢查錯誤,配合perror/strerror打印詳細信息,便于調試和維護。
  3. IP、端口、字節序、緩沖區管理要細心。
  4. 建議將代碼拆分成模塊,錯誤處理、日志、收發通信各自封裝。

只要理解并掌握上面每一步、每個關鍵函數的使用,你就能獨立搭建出穩定可靠的C語言網絡通信程序!每次遇到問題都可以翻回來看,一步步排查流程,問題迎刃而解。


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

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

相關文章

Tomcat 安裝和配置

一、Tomcat官網 Apache Tomcat - Welcome! 選擇解壓到任意一個盤&#xff01;&#xff01; 二、Tomcat配置 1&#xff09;在系統變量處新建一個變量CATALINA_HOME。CATALINA_HOME環境變量的值&#xff0c;設置為Tomcat的解壓安裝目錄 2&#xff09;找到系統變量Path&#xff0…

動態規劃 熟悉30題 ---上

本來是要寫那個二維動態規劃嘛&#xff0c;但是我今天在問題時候&#xff0c;一個大佬就把他初一時候教練讓他練dp的30題發出來了&#xff08;初一&#xff0c;啊雖然知道計算機這一專業&#xff0c;很多人從小就學了&#xff0c;但是我每次看到一些大佬從小學還是會很羨慕吧或…

基于stm32F10x 系列微控制器的智能電子琴(附完整項目源碼、詳細接線及講解視頻)

注&#xff1a;成品使用演示、項目源碼、項目文檔在文章末尾網盤鏈接中自取 所用硬件&#xff1a;STM32F103C8T6、無源蜂鳴器、44矩陣鍵盤、flash存儲模塊、OLED顯示屏、RGB三色燈、面包板、杜邦線、usb轉ttl串口 stm32f103c8t6 面包板 …

時間同步技術在電力系統中的應用

隨著電力自動化技術的發展&#xff0c;時間同步不僅可以為電力系統的事后故障分析提供支持&#xff0c;而且已經參與到電力系統的實時控制中來&#xff0c;其可靠性對電力系統的穩定運行影響越來越大。在電力系統中&#xff0c;時間同步技術廣泛應用于調度控制中心、發電廠、變…

XMLGregorianCalendar跟Date、localDateTime以及String有什么區別

1. java.util.Date&#xff08;已過時&#xff0c;不推薦新代碼使用&#xff09; 特點 表示時間戳&#xff1a;存儲自 1970-01-01 00:00:00 UTC&#xff08;Unix 紀元&#xff09; 以來的毫秒數。 問題&#xff1a; 不區分日期和時間&#xff0c;也沒有時區支持&#xff08;依…

Python網頁自動化Selenium中文文檔

1. 安裝 1.1. 安裝 Selenium Python bindings 提供了一個簡單的API&#xff0c;讓你使用Selenium WebDriver來編寫功能/校驗測試。 通過Selenium Python的API&#xff0c;你可以非常直觀的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常簡潔方便的A…

玩轉抖音矩陣:核心玩法與高效運營規則

一、 抖音矩陣&#xff1a;流量協同的生態網絡 抖音矩陣&#xff0c;本質是運營一個相互關聯、互相支持的抖音賬號群。核心目標在于通過賬號間的深度協同&#xff08;內容、流量、粉絲&#xff09;&#xff0c;打破單個賬號的流量天花板&#xff0c;實現11>2的效果。它不僅…

C++11 constexpr和字面類型:從入門到精通

文章目錄 引言一、constexpr的基本概念與使用1.1 constexpr的定義與作用1.2 constexpr變量1.3 constexpr函數1.4 constexpr在類構造函數中的應用1.5 constexpr的優勢 二、字面類型的基本概念與使用2.1 字面類型的定義與作用2.2 字面類型的應用場景2.2.1 常量定義2.2.2 模板參數…

用電腦通過USB總線連接控制keysight示波器

通過USB總線控制示波器的優勢 在上篇文章我介紹了如何通過網線遠程連接keysight示波器&#xff0c;如果連接的距離不是很遠&#xff0c;也可以通過USB線將示波器與電腦連接起來&#xff0c;實現對示波器的控制和截圖。 在KEYSIGHT示波器DSOX1204A的后端&#xff0c;除了有網口…

StarRocks 全面向量化執行引擎深度解析

StarRocks 全面向量化執行引擎深度解析 StarRocks 的向量化執行引擎是其高性能的核心設計&#xff0c;相比傳統行式處理引擎&#xff08;如MySQL&#xff09;&#xff0c;性能可提升 5-10倍。以下是分層拆解&#xff1a; 1. 向量化 vs 傳統行式處理 維度行式處理向量化處理數…

02 Deep learning神經網絡的編程基礎 邏輯回歸--吳恩達

1.邏輯回歸 邏輯回歸是一種用于解決二分類任務&#xff08;如預測是否是貓咪等&#xff09;的統計學習方法。盡管名稱中包含“回歸”&#xff0c;但其本質是通過線性回歸的變體輸出概率值&#xff0c;并使用Sigmoid函數將線性結果映射到[0,1]區間。 以貓咪預測為例 假設單個…

UDP 與 TCP 的區別是什么?

UDP&#xff08;用戶數據報協議&#xff09;與TCP&#xff08;傳輸控制協議&#xff09;有以下區別&#xff1a; 連接方式 - UDP&#xff1a;無連接&#xff0c;發送數據前不需要建立連接&#xff0c;也不維護連接狀態&#xff0c;因此UDP的通信效率較高&#xff0c;適合對實時…

6.計算機網絡核心知識點精要手冊

計算機網絡核心知識點精要手冊 1.協議基礎篇 網絡協議三要素 語法&#xff1a;數據與控制信息的結構或格式&#xff0c;如同語言中的語法規則語義&#xff1a;控制信息的具體含義和響應方式&#xff0c;規定通信雙方"說什么"同步&#xff1a;事件執行的順序與時序…

unipp---HarmonyOS 應用開發實戰

HarmonyOS 應用開發實戰指南 1. 開篇&#xff1a;為什么選擇 HarmonyOS&#xff1f; 最近在開發鴻蒙應用時&#xff0c;發現很多開發者都在問&#xff1a;為什么要選擇 HarmonyOS&#xff1f;這里分享一下我的看法&#xff1a; 生態優勢 華為手機用戶基數大&#xff0c;市場潛…

Python_day48隨機函數與廣播機制

在繼續講解模塊消融前&#xff0c;先補充幾個之前沒提的基礎概念 尤其需要搞懂張量的維度、以及計算后的維度&#xff0c;這對于你未來理解復雜的網絡至關重要 一、 隨機張量的生成 在深度學習中經常需要隨機生成一些張量&#xff0c;比如權重的初始化&#xff0c;或者計算輸入…

C++中的數組

在C中&#xff0c;數組是存儲固定大小同類型元素的連續內存塊。它是最基礎的數據結構之一&#xff0c;廣泛用于各種場景。以下是關于數組的詳細介紹&#xff1a; 一、一維數組 1. 定義與初始化 語法&#xff1a;類型 數組名[元素個數];示例&#xff1a;int arr[5]; // 定義…

three.js 零基礎到入門

three.js 零基礎到入門 什么是 three.js為什么使用 three.js使用 Three.js1. 創建場景示例 2.創建相機3. 創建立方體并添加網格地面示例 5. 創建渲染器示例 6. 添加效果(移動/霧/相機跟隨物體/背景)自動旋轉示例效果 相機自動旋轉示例 展示效果 實現由遠到近的霧示例展示效果 T…

Elasticsearch的寫入性能優化

優化Elasticsearch的寫入性能需要從多維度入手,包括集群配置、索引設計、數據處理流程和硬件資源等。以下是一些關鍵優化策略和最佳實踐: 一、索引配置優化 合理設置分片數與副本數分片數(Shards):過少會導致寫入瓶頸(無法并行),過多會增加集群管理開銷。公式參考:分…

FMC STM32H7 SDRAM

如何無痛使用片外SDRAM? stm32 已經成功初始化了 STM32H7 上的外部 SDRAM&#xff08;32MB&#xff09; 如何在開發中無痛使用SDRAM 使它像普通 RAM 一樣“自然地”使用? [todo] 重要 MMT(Memory Management Tool) of STM32CubeMx The Memory Management Tool (MMT) disp…

【AIGC】RAGAS評估原理及實踐

【AIGC】RAGAS評估原理及實踐 &#xff08;1&#xff09;準備評估數據集&#xff08;2&#xff09;開始評估2.1 加載數據集2.2 評估忠實性2.3 評估答案相關性2.4 上下文精度2.5 上下文召回率2.6 計算上下文實體召回率 RAGas&#xff08;RAG Assessment)RAG 評估的縮寫&#xff…