零基礎學習計算機網絡編程----socket實現UDP協議

? ? ? ? 本章將會詳細的介紹如何使用 socket 實現 UDP 協議的傳送數據。有了前面基礎知識的鋪墊。對于本章的理解將會變得簡單。將會從基礎的 Serve 的初始化,進階到 Client 的初始化,以及 run。最后實現一個簡陋的小型的網絡聊天室。

????????

目錄

????????1.UdpSever.h

? ? ? ?1.1 構造函數

? ? ? ?1.2 initServe 函數

? ? ? ? ?2. UdpSever.cc

? ? ? ? 3. udpClient.hpp?

? ? ? ? ?3.1 構造函數

? ? ? ? 3.2 init(初始化函數)

? ? ? ? 3.3 run

? ? ? 4. udpClient.cc

5. 對于??UdpSever.h 中run函數的實現

?6. 結尾


?

????????1.UdpSever.h

? ? ? ? ? ? 在這個文件里面需用定義 Sever 的一個類,里面變量為:_ip, _port,_sockfd。要實現的函數為構造函數、初始化(init)、運行(run)、析構函數。進行初始化是實現服務器的網絡的核心,當然run 也非常重要。關于實現構造函數的時候為什么只需要 port 以及defaultip是什么我后面都會進行講解。

class udpServer{public:udpServer(uint16_t port): _ip(defaultIp), _port(port){}void initServe(){}void start(){}~udpServer() {}private:uint16_t _port;string _ip;int _sockfd;};

? ? ? ?1.1 構造函數

????????里面使用了 port,因為這個服務器的特點,我們使用的 linux 有2個,或是2個以上的 ip,因此我們在進行綁定的時候是不可以進行直接綁定的。要不然當使用不同的 ip 的時候,會導致數據無法從客戶端發送到服務端。因此需要使用默認的ip = “0.0.0.0”。(后面的cc文件中使用智能指針的方式進行定義)

? ? ? ??1.2 initServe 函數

????????進行初始化,本質就是使用 socket 打開文件,會返回文件描述符(可以理解為打開網卡這個文件,然后進行傳遞數據)。然后進行 bind ,使用 struct sockaddr_in loacl, 對于這個結構體里面的數據進行填寫,然后進行 bind 綁定。

? ? ? ? 對于soket 函數,官方的定義為:返回一個文件描述符fd, 第一個參數是選擇網絡通信還是本地通信,第二變量表示是使用udp 還是 tcp, 我們使用的是 sock_dgram(數據報的方式發送數據),第三個變量表示阻塞狀態默認為 0;最后如何是返回 -1 進行退出操作。

 _sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERROR);}

? ? ? ? 使用 bind 函數進行綁定。?需要文件的描述符,一個 sockaddr 的結構體,sizeof(sockaddr 的結構體)。關鍵就是在于處理這個 sockaddr 我們使用的 sockaddr_in 然后進行類型的轉化。最后我們使用的是先進行清零,然后 第一個位置放上協議家族:AF_INET, 第二位置放上 port 端口號需要使用 htons(host主機到net網絡當中為 short 的 16 位的方式), ip 直接使用 inet_addr(將點分十進制轉化位 32 位的整形,然后變成網絡當中的大端方式發送)。還需要注意的是由于 inet_addr 傳遞的字符串的指針,所以是使用 c_str。整體的代碼如下。

?

void initServe(){// 使用 socket 函數,然后進行綁定_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERROR);}// 進行band 綁定,注意是使用結構體進行綁定struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());// 設置好 addr_in 就去進行綁定int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n == -1){cerr << "bind_error" << " : " << strerror(errno) << endl;exit(BIND_ERROR);}}

? ? ? ? 對于啟動函數,他本質就是一個無限的循環。?

for(;;)

? ? ? ? ?2. UdpSever.cc

? ? ? ? 就。是在運行的時候,我們需要用戶使用 ./ UdpSever port 如果不是就會提示如何使用。然后因為 argv[1] 使用的是字符串的方式,直接 atoi 就可以轉化位整型。然后就是智能指針的定義,進行初始化,以及啟動函數。

#include "udpServe.hpp"
#include <memory>
#include <unordered_map>using namespace djx;
static void Usage(std::string tmp)
{std::cout << "Usage:\n" << tmp << "local_port\n\n";
}
int main(int argc, char* argv[])
{if(argc != 2){//說明輸入錯誤,需要重新輸入Usage(argv[0]);exit(IN_ERROR);}uint16_t port = atoi(argv[1]);//然后需要啟動定義好的 udpstd::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServe();usvr->start();return 0;
}

? ? ? ? ?最終實現的樣式為這樣,已經成功運行了。

? ? ? ? 3. udpClient.hpp?

? ? ? ? ? ? ? ? 跟udpSever是一樣的都是4個函數,接下來我將會一一介紹。

? ? ? ? ?3.1 構造函數

? ? ? ? ? ? ? ? 這個構造函數就需要告訴你,你要發送的服務器的ip地址,以及 port 端口號。 所以定義如下:

udpClient(uint16_t serveport, string serveip):_serveport(serveport),_serveip(serveip){}

? ? ? ? 3.2 init(初始化函數)

? ? ? ? ? ? ? ? 與udpSever一樣都是使用socket,然后使用bind。但是這個bind 不要們進行顯示的綁定,但是是需要進行綁定的。 為了保證獨立性,ip 與 port 由操作系統為我們進行提供。

void initClient(){//click 是實現服務端的傳送數據_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(2);}//進行綁定不要顯示的進行綁定,是操作系統會幫助我們進行生成隨機獨立唯一的一個 port}

? ? ? ? 3.3 run

? ? ? ? run就是運行函數,使用函數接口 sendto ,這個是一個輸出型函數。是要將buf 緩沖里面的東西發送到 severip 和 severport 的 服務器當中,所以需要使用 sockaddr_in 對于里面的內容進行初始化。然后是對于 buffer 內容的輸入。是使用 string message 類型的來進行存放數據。

 void run(){//需要使用函數 sendto, 但是在之前需要先獲取到 secve 的ip 以及 portstruct sockaddr_in serve;serve.sin_family = AF_INET;serve.sin_port = htons(_serveport);serve.sin_addr.s_addr = inet_addr(_serveip.c_str());while(1){string message;cin >> message;sendto(_sockfd, message.c_str(), sizeof(message), 0, (struct sockaddr*)&serve, sizeof(serve));}}

? ? ? 4. udpClient.cc

? ? ? ? 與 Serve 類型都是先進行判斷輸入的argc 是為 3,如果是就進行啟動。不是就告訴你如何使用。

#include "udpClient(fianll).hpp"
#include <memory>
using namespace std;
using namespace djx;
static void Usage(std::string tmp)
{cout << "Usage:\n\t" << tmp << " local_ip "<< "local_port\n\n";
}
int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[2]);string ip = argv[1];std::unique_ptr<udpClient> uclick(new udpClient(port, ip));uclick->initClient();uclick->run();return 0;
}

5. 對于??UdpSever.h 中run函數的實現

? ? ? ? start 函數需要接受從客服端發來的信息,通過之前啟動的 sokce 以及 bind,啟動了 udp 協議。內核就會幫助我們對于定義的 struct scokaddr_in 進行內容的填充(來自客戶端發送過來的數據)。里面需要的函數為 recvfrom 這是一個輸入輸出型參數。從? struct scokaddr_in 獲取輸入參數,將參數輸出到 buffer 這個緩存當中,然后進行打印。

? ? ? ? 整體的實現如下,其中 full 定義為 1024 個整形。然后 s 表示的是buffer 里面讀取到的個數,如果是小于0,就表示沒有數據,直接結束此次循環。為了照顧計算機網絡的數據嚴格傳送的特點,len 的長度需要定義為 socklen_t 。使用 char 的時候寫入的大小是從 0 開始的,所以是要sizeof -? 1.

void start(){char buffer[full]; for (;;){// 用來存放 哪個click發送來的數據struct sockaddr_in peer;socklen_t len = sizeof(peer); // 為了照顧vecvfrom函數,所以使用這個socken_t;ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = 0; string message = buffer;    // s表示獲取到的長度string click_ip = inet_ntoa(peer.sin_addr); // 直接就是從結構體變過來int click_port = ntohs(peer.sin_port);cout << click_ip << "[" << click_port << "]#" << message << endl;}}}

?6. 結尾

? ? ? ? 到此 udp 簡單實現已經結束了,還不是很全面因為我們是要得到數據,然后實現相應的工作,這里還沒有實現功能,就是實現了一個簡陋的網絡聊天室。任意一臺主機通過輸入./udpClient ip + port, 我都可以在我的服務器上看見對應的信息。

????????以上是對于計算網絡中基礎知識的了解,這個文章用于我的學習記錄,如果是有其他的錯誤還請批評指正。如果對你有幫助還請給我點個贊👍👍👍。?????

????????

? ? ? ? ? ? ? ? ?

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

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

相關文章

普中STM32F103ZET6開發攻略(二)

接上文&#xff1a;普中STM32F103ZET6開發攻略&#xff08;一&#xff09;-CSDN博客 各位看官老爺們&#xff0c;點擊關注不迷路喲。你的點贊、收藏&#xff0c;一鍵三連&#xff0c;是我持續更新的動力喲&#xff01;&#xff01;&#xff01; 目錄 接上文&#xff1a;普中…

用提示詞寫程序(3),VSCODE+Claude3.5+deepseek開發edge擴展插件V2

edge擴展插件;篩選書簽,跳轉搜索,設置背景 鏈接: https://pan.baidu.com/s/1nfnwQXCkePRnRh5ltFyfag?pwd86se 提取碼: 86se 導入解壓的擴展文件夾: 導入擴展成功: edge擴展插件;篩選書簽,跳轉搜索,設置背景

電腦桌面便簽軟件哪個好?桌面好用便簽備忘錄推薦

在日常辦公中&#xff0c;一款優秀的桌面便簽工具能顯著提升工作效率。面對市面上琳瑯滿目的選擇&#xff0c;不少用戶都難以抉擇。如果你正在尋找一款兼具輕量化與多功能性的便簽軟件&#xff0c;那么集實用性與便捷性于一身的"好用便簽"&#xff0c;或許就是你的理…

性能優化 - 工具篇:基準測試 JMH

文章目錄 Pre引言1. JMH 簡介2. JMH 執行流程詳解3. 關鍵注解詳解3.1 Warmup3.2 Measurement3.3 BenchmarkMode3.4 OutputTimeUnit3.5 Fork3.6 Threads3.7 Group 與 GroupThreads3.8 State3.9 Setup 與 TearDown3.10 Param3.11 CompilerControl 4. 示例代碼與分析4.1 關鍵點解讀…

2025年十大AI幻燈片工具深度評測與推薦

我來告訴你一個好消息。 我們已經親自測試和對比了市面上最優秀的AI幻燈片工具&#xff0c;讓你無需再為選擇而煩惱。 得益于AI技術的飛速發展&#xff0c;如今你可以快速制作出美觀、專業的幻燈片。 這些智能平臺的功能遠不止于配色美化——它們能幫你頭腦風暴、梳理思路、…

雪花算法:分布式ID生成的優雅解決方案

一、雪花算法的核心機制與設計思想 雪花算法&#xff08;Snowflake&#xff09;是由Twitter開源的分布式ID生成算法&#xff0c;它通過巧妙的位運算設計&#xff0c;能夠在分布式系統中快速生成全局唯一且趨勢遞增的ID。 1. 基本結構 雪花算法生成的是一個64位&#xff08;lo…

第1章:走進Golang

第1章&#xff1a;走進Golang 一、Golang簡介 Go語言&#xff08;又稱Golang&#xff09;是由Google的Robert Griesemer、Rob Pike及Ken Thompson開發的一種開源編程語言。它誕生于2007年&#xff0c;2009年11月正式開源。Go語言的設計初衷是為了在不損失應用程序性能的情況下…

Higress項目解析(二):Proxy-Wasm Go SDK

3、Proxy-Wasm Go SDK Proxy-Wasm Go SDK 依賴于 tinygo&#xff0c;同時 Proxy - Wasm Go SDK 是基于 Proxy-Wasm ABI 規范使用 Go 編程語言擴展網絡代理&#xff08;例如 Envoy&#xff09;的 SDK&#xff0c;而 Proxy-Wasm ABI 定義了網絡代理和在網絡代理內部運行的 Wasm …

NVMe IP現狀掃盲

SSD優勢 與機械硬盤&#xff08;Hard Disk Driver, HDD&#xff09;相比&#xff0c;基于Flash的SSD具有更快的數據隨機訪問速度、更快的傳輸速率和更低的功耗優勢&#xff0c;已經被廣泛應用于各種計算領域和存儲系統。SSD最初遵循為HDD設計的現有主機接口協議&#xff0c;例…

`docker commit` 和 `docker save`區別

理解 docker commit 和 docker save 之間的區別對于正確管理 Docker 鏡像非常重要。讓我們詳細解釋一下這兩個命令的作用及其區別。 1. docker commit 作用&#xff1a; docker commit roop-builder roop:v1 命令的作用是基于一個正在運行的容器 roop-builder 創建一個新的鏡…

Linux內核體系結構簡析

1.Linux內核 1.1 Linux內核的任務 從技術層面講&#xff0c;內核是硬件和軟件之間的一個中間層&#xff0c;作用是將應用層序的請求傳遞給硬件&#xff0c;并充當底層驅動程序&#xff0c;對系統中的各種設備和組件進行尋址。從應用程序的角度講&#xff0c;應用程序與硬件沒有…

python爬蟲:Ruia的詳細使用(一個基于asyncio和aiohttp的異步爬蟲框架)

更多內容請見: 爬蟲和逆向教程-專欄介紹和目錄 文章目錄 一、Ruia概述1.1 Ruia介紹1.2 Ruia特點1.3 安裝Ruia1.4 使用案例二、基本使用2.1 Request 請求2.2 Response - 響應2.3 Item - 數據提取2.4 Field 提取數據2.5 Spider - 爬蟲類2.6 Middleware - 中間件三、高級功能3.1 …

網絡攻防技術二:密碼學分析

文章目錄 一、傳統密碼分析方法1、根據明文、密文等信息的掌握情況分類 2、從密碼分析途徑分類二、密碼旁路分析1、概念2、旁路分析方法三、現代密碼系統1、對稱密碼&#xff08;單密鑰&#xff09;2、公開密碼&#xff08;成對密鑰&#xff09; 四、典型對稱密碼&#xff08;單…

Linux --TCP協議實現簡單的網絡通信(中英翻譯)

一、什么是TCP協議 1.1 、TCP是傳輸層的協議&#xff0c;TCP需要連接&#xff0c;TCP是一種可靠性傳輸協議&#xff0c;TCP是面向字節流的傳輸協議&#xff1b; 二、TCPserver端的搭建 2.1、我們最終好實現的效果是 客戶端在任何時候都能連接到服務端&#xff0c;然后向服務…

pc端小卡片功能-原生JavaScript金融信息與節日日歷

代碼如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金融信息與節日日歷</title><…

C語言——獲取變量所在地址(uint8和uint32的區別)

前言&#xff1a; 1.使用uint8 *的原因 在C語言中&#xff0c;獲取或操作一個4字節地址&#xff08;指針&#xff09;時使用uint8_t*&#xff08;即unsigned char*&#xff09;而不是uint32_t*&#xff0c;主要基于以下關鍵原因&#xff1a; 1.1. 避免違反嚴格別名規則&…

Python----目標檢測(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理與網絡結構)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 標題&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 機構&#xff1a;華盛頓大學&#xff08;University of Washington&#xff09; 發表時間&#xff1a;2018年 代…

50天50個小項目 (Vue3 + Tailwindcss V4) ? | Form Wave(表單label波動效果)

&#x1f4c5; 我們繼續 50 個小項目挑戰&#xff01;—— FormWave組件 倉庫地址&#xff1a;https://github.com/SunACong/50-vue-projects 項目預覽地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 組件目標 構建一個美觀、動態的登錄表單&#xff0…

【數據結構】--二叉樹--堆(上)

一、樹的概念和結構 概念&#xff1a; 樹是一種非線性的數據結構&#xff0c;他是由n(n>0)個有限結點組成一個具有層次關系的集合。其叫做樹&#xff0c;是因為他倒過來看就和一棵樹差不多&#xff0c;其實際上是根在上&#xff0c;樹枝在下的。 樹的特點&#xff1a; 1…

linux有效裁剪視頻的方式(基于ffmpeg,不改變分辨率,幀率,視頻質量,不需要三方軟件)

就是在Linux上使用OBS Studio錄制一個講座或者其他視頻&#xff0c;可能總有些時候會多錄制一段時間&#xff0c;但是如果使用剪映或者PR這樣的工具在導出的時候總需要煩惱導出的格式和參數&#xff0c;比如剪映就不支持mkv格式的導出&#xff0c;導出成mp4格式的視頻就會變得很…