Linux網絡基礎(協議 TCP/IP 網絡傳輸基本流程 IP VS Mac Socket編程UDP)

文章目錄

  • 一.前言
  • 二.協議
    • 協議分層
      • 分層的好處
    • OSI七層模型
    • TCP/IP五層(或四層)模型
    • 為什么要有TCP/IP協議
    • TCP/IP協議與操作系統的關系(宏觀上是如何實現的)
    • 什么是協議
  • 三.網絡傳輸基本流程
    • 局域網(以太網為例)通信原理
      • MAC地址
      • 令牌環網
    • 封裝與解包分用
  • 四.IP地址
    • IP VS Mac地址
  • 五.Socket編程預備
    • 理解源IP地址和目的IP地址
    • 端口號
      • 端口號范圍劃分
    • 認識TCP協議與UDP協議
    • 網絡字節序(大小端)
      • 網絡主機相互轉換接口
    • socket編程接口
      • sockaddr結構
  • 六.Socket 編程 UDP
    • echo server 服務端
      • 收數據
    • Client 客戶端
    • 注意事項
    • 整體的代碼與效果
      • 效果
  • 七.網絡命令
    • Ping 命令
    • netstat 命令
    • pidof 命令

一.前言

一開始時,計算機都相互獨立,只能通過軟盤,才能把數據交給另一臺計算機
在這里插入圖片描述
后面因為效率原因,就有了服務器,都從服務區上拿數據
在這里插入圖片描述
美國一開始有貝爾實驗室、麻省理工實驗室…一開始實驗室各搞各的,所以局域網有很多種類的以太網、令牌網…
新技術的產生,一定產生了新設備(芯片技術應用到筆記本,就叫做計算機;用到汽車上,就叫做智能汽車),所以計算機的發展,肯定伴隨著新的設備(比如網絡通信,得有網線)
1994年中國進入互聯網,不僅讓騰訊阿里發展很好,華為和其它通信的公司也發展起來。
當計算機太多時,就有了交換機和路由器
在這里插入圖片描述
計算機越來越多,就有了廣域網
在這里插入圖片描述

二.協議

協議就是一種約定。
比如:1+1=2是公認,它也可以說成One plus one equals two。語言可以不同,但只能1+1=2。這就是一種協議。
計算機廠商上有很多,但要讓計算機之間有需要一種協議
能定制標準的都是世界公認的組織或公司,但定標準,不一定要實現標準。比如:華為定的5G標準,不一定要華為自己實現,但其它公司要做5G,得按照華為定的標準實現。

協議分層

協議本身就是軟件,為了更好的模塊化,解耦合,最好也要分層。

分層的好處

以兩個人說話方式為例:
在這里插入圖片描述
這是分兩層,語言層和通信設備層:
在這里插入圖片描述
這就是模塊化的解耦合,一層出現問題,另一層并不影響,下一層出現了問題,替換了協議,上一層完全不受影響。(軟件層劃分的越合理,越好,比如封裝、STL容器)
出了問題也好排查。張三聽不清楚李四打電話,無非就是語言不同或者交流方式出現了問題。

OSI七層模型

OSI(Open System Interconnection,開放系統互連)七層網絡模型稱為開放式系統互聯參考模型,是一個邏輯上的定義和規范。
定標準和實現標準是兩批人,實現由頂級的工程師實現,windows和Linux能通信就是因為兩者之間遵守著同一個協議。

分層名稱功能每層功能概述
7應用層針對特定應用的協議在這里插入圖片描述
6表示層設備固有數據格式和網絡標準數據格式的轉換在這里插入圖片描述
5會話層通信管理。負責建立和斷開通信連接(數據流動的邏輯通路)。管理傳輸層以下的分層在這里插入圖片描述
4傳輸層管理兩個節點之間的數據傳輸。負責可靠傳輸(確保數據被可靠地傳送到目標地址)在這里插入圖片描述
3網絡層地址管理與路由選擇。在這里插入圖片描述
2數據鏈路層互連設備之間傳送和識別數據幀。在這里插入圖片描述
1物理層以"0"、"1"代表電壓的高地、燈光的閃滅在這里插入圖片描述

實際真正落地的不是OSI七層模型,而是5層協議,會話層、表示層、應用層這三個規劃為一個大的應用層,因為會話層和表示層是不可能接入到操作系統中的。

TCP/IP五層(或四層)模型

TCP是傳輸層的協議,IP是網絡層的協議。TCP/IP是一組協議的代名詞。
5層分別為:

物理層規定什么是’0’、‘1’,特定的01序列是什么含義。通信媒介不一樣,規定傳遞的方式。
數據鏈路層設備和設備之間的傳遞和識別,有以太網、令牌環網、無線LAN等保證設備之間的通信。
網絡層兩個跨網絡的設備需要IP地址傳輸。
傳輸層數據傳輸長距離傳輸時丟包或亂序怎么處理?
應用層設備A從北京送到在西安的設備B,其中傳數據的目的,如何應用?

其中物理層是光電信號的傳遞,所以只談剩下的4層。

為什么要有TCP/IP協議

首先,即便是單個計算機,都是存在協議的,比如與磁盤通信會有磁盤相關的協議:SATA、IDE、SCSI等。設備之間的協議都是在計算機內部,比如內存和磁盤距離個10厘米,通信的成本低,問題就少。
網絡因為距離很遠,由于物理特性就會出現很多問題。比如男女朋友,在同一個學校,出現問題,解決問題的成本低;異地戀,則會提高成本。
在這里插入圖片描述
假設主機A發送數據給主機C的過程中,依次會產生的問題:
1.主機A發送給主機C,得把數據交給路由器。(異地戀給女朋友買東西,得交給快遞小哥)
2.網絡上這么多主機,如何準確的找到主機C。
3.如果我的數據在發送的過程中,在某一個節點丟失了怎么辦?(快遞小哥轉發錯地方了)
4.發送數據不是目的,只是手段,讓主機C使用數據是目的。(主機A把‘你好’發給主機C,主機C需要處理‘你好’,是以讀不回還是以讀亂回)
所以有了各種協議,去解決上面的問題。
TCP/IP協議是一種解決方案,本質問題是兩臺通信的主機距離變遠了,所以有了各種問題,需要通過TCP/IP協議這種解決方案去解決。

TCP/IP協議與操作系統的關系(宏觀上是如何實現的)

Windows和Linxu底層是不一樣的,傳輸層(典型協議是TCP)和網絡層(典型協議是IP)在操作系統內部實現的,雖然操作系統實現本身不一樣,但網絡部分必須是一樣的,這就是為什么Windows和Linux能通訊。
不同主機或廠商所對應不同設備之間可以通信的秘密就在于,大家實現的是相同的網絡協議棧。
在這里插入圖片描述

什么是協議

OS一般都是用C/C++寫的代碼。
假設有兩個主機A、B,兩個主機的內核的代碼不一樣,但傳輸層和網絡層的的代碼一樣。在傳輸層有一個結構體protocol,主機A發送struct protocol data給主機B。
問題:在通信的時候是傳輸二進制數據,主機B能識別data,并精確的識別a=10,b=20,c=30嗎?
在這里插入圖片描述
答:是可以的;因為雙方都有同一個結構體struct protocol,所以主機B能立馬識別abc的值。
所謂協議:就是通信雙方都認識的結構化的數據類型。
因為協議是分層的,所以每層雙方都有協議,同層之間,互相可以認識對方的協議。
以網上購物發快遞為例子:
在這里插入圖片描述
買鍵盤,實際就想要一個鍵盤。但實際給我的,除了鍵盤還給了盒子上貼的一張快遞單,我看到這個快遞單子,就能確認這是我的快遞。快遞單+鍵盤兩個共同構成了一個完整的報文。
快遞單上所有的信息都是發快遞的人填的,快遞小哥和我也明白快遞單上面的內容。這個單子就是三者之間的約定,這個約定是快遞公司定的(定標準的人)。發快遞的人、快遞小哥與收快遞的人一起遵守這個約定。
用C語言表示這個快遞單,不就是一個結構體。快遞單就是協議的報頭,鍵盤就是有效載荷,合在一起叫做報文。
發送方構建快遞單叫做構建報頭,把鍵盤裝起來叫做封裝。
綜上所述:協議就是約定

三.網絡傳輸基本流程

以太網、無線LAN等局域網之間的標準是可以不同的。

局域網(以太網為例)通信原理

問題:兩臺主機在同一個局域網,是否可以直接通信?
答:是可以的;宿舍的無線路由器,你和舍友連著同一個局域網,和隔壁宿舍連著是不同的局域網,小時候玩<<我的世界>>開個熱點把4G網絡關上依舊能一起玩,在同一局域網。
故事:在教室里,老師喊張三,問作業為什么沒做,站起來!教室里的同學都聽到了,張三分析老師剛剛說的信息,提取目的地址叫做張三,就站起來了,然后張三跟老師說:我作業交了。其他人分析剛剛老師說的消息里面的一個目的地址叫做張三,跟自己沒關系,直接把聽到的這句話丟棄掉,不做響應。
雙方在通信的整個過程,老師與張三都互相認為在單獨通信。周圍其他人也能聽到這個消息,但不做響應。
其中教室叫做局域網,老師為主機A,張三為主機B,兩者通信時只要知道對方名字就能通信,其他人能收到但不做響應,這就是局域網通信的基本原理。
總結:局域網能直接通信,每臺主機(局域網)都要有一個唯一的標識:MAC地址

MAC地址

MAC地址是數據鏈路層中相連的節點。(就是標識主機的唯一性)
長度為48位,6個字節,一半用16進制+冒號的形式來表示。
可以用ifconfig指令查看:

ifconfig

在這里插入圖片描述
Mac地址是集成在網卡中的地址,在出廠時就設置好了,可以說在全球具有唯一性。
問題:操作系統時如何獲取Mac地址?
答:開機時,網卡的驅動程序會根據網卡的協議,會把Mac地址讀到操作系統。
不僅僅網卡有唯一標識,很多設備都有唯一標識,比如磁盤也是有全球唯一標識,叫做序列號。
虛擬機中的Mac地址不是真實的地址,而是虛擬出來的,可能會產生沖突。
以主機A用以太網給主機E發送數據為例:
在這里插入圖片描述
以太網通信的原理:
1.在以太網中,任何時刻,只允許一臺機器向網絡中發送數據。
2.如果有多臺同時發送,就會產生數據干擾,稱為數據碰撞。(發送的01序列,之間可能干擾)
3.所有發送數據的主機要進行碰撞檢測和碰撞避免。(主機A把數據發出去了,檢測數據有沒有發送成功,主機A自己也是一臺主機,所以自己發送的數據自己能到,跟發的數據和收的數據做比較,查看是否碰撞,一旦發生碰撞了,就要進行碰撞避免。比如主機A與主機B發生了碰撞,兩主機都要休眠一定的隨機時間,再次碰撞的概率減小,同時這個局域網中少了兩臺主機,可以讓其它主機趁著這個間隙發送數據)
根據上面三個原理,很像一種臨界資源(公共的數據),只允許一種主機,訪問該臨界資源里的資源。
4.沒有交換機的情況下,一個以太網就是一個碰撞域。
5.局域網通信的過程中,主機對收到的報文確定是否發給自己的,是通過目標Mac地址判定。
可以通過系統角度來理解局域網通信。

令牌環網

在這里插入圖片描述
主機A持有數據,就能發消息。主機B持有數據,也能發消息。這不就是一把鎖。

封裝與解包分用

在同一個局域網內,發送消息的過程:
在這里插入圖片描述
其中每層都有協議,所以當進行上述傳輸流程時,要進行封裝和解包:
在這里插入圖片描述
明確概念:
以網絡層為例:網絡層報頭為報頭。傳輸層報頭、應用層報頭與“你好”為有效載荷。
在這里插入圖片描述
從上到下添加報頭,可以想象成一個入棧的結構,往上交付的過程就是出棧的過程,所以我們也稱為網絡協議棧:
在這里插入圖片描述
數據在網絡中發送的時候,一定最終要在硬件上跑,主機2的網卡先收到數據,然后再向上解包分用。就比如你在三樓宿舍要去3樓教學樓,只能從宿舍的三樓跑到1樓,然后再從教學樓的1樓跑到3樓:
在這里插入圖片描述
應用層存在上百種協議,傳輸層有tcp/udp…,網絡層有IP/ICMP…鏈路層有mac
若發送數據應用層用http,傳輸層用tcp,網絡層用ip,鏈路層用mac,那問題在于接收層,網絡層中有ip/icmp…等協議,如何知道發送方的網絡層用到什么協議?
在這里插入圖片描述
在前面說了,報頭(協議)就是一種結構體,那么封裝的時候,他會把有效載體封裝進結構體中:
在這里插入圖片描述
總結:網絡協議有兩個共性:
1.報頭和有效載荷分離的問題 – 解包。
2.除了應用層,每一層協議都得解決一個問題:自己的有載荷,應該交給上一層的那一種協議 – 分層。
快遞就有分用的功能,快遞小哥到當地了,給每一個收快遞的人打電話,不就是一種分用。電話是什么時候填的?是在發快遞時候填好了(封裝)。
報文 = 報頭 + 有效載荷
在不同場景下,不同的報文有不同的名字(比如在學校叫某同學,工作某同事)
在這里插入圖片描述
補充:
1.應用層數據通過協議棧發到網絡上時,每層協議都要加上一個數據首部(header),稱為封裝。
2.首部信息中包含了一些類似于首部有多長, 載荷(payload)有多長, 上層協議是什么等信息。
3.數據封裝成幀后發到傳輸介質上,到達目的主機后每層協議再剝掉相應的首部, 根據首部中的 “上層協議字段” 將數據交給對應的上層協議處理。

四.IP地址

上面講述的都是在同一局域網中通信,那么不在同一局域網如何通信?
路由器:路由器要能夠跨網絡轉發,至少連接兩個網絡,就得配上多個網絡與驅動程序。
下圖主機A的路由器與主機B的路由器分別配兩張網卡:1個網卡連接的是以太網;1個連接著令牌環網。
因為用的局域網不同,所以主機A用的以太網的驅動程序,主機B用的令牌環網的驅動程序。
在這里插入圖片描述
其中,IP地址是標識網絡中唯一性的,IP協議有兩個版本:IPv4和IPv6。
IPv4是一個4字節,32位的整數,通常用“點分十進制”的字符串標識IP地址,例如:192.168.0.1;用點分隔的每一個數字為一個字節,數字區間為0~255。
IPv6是一個16字節,128位的整數。
Linux下的IP地址:
在這里插入圖片描述
Windows下看網絡地址:ipconfig
在這里插入圖片描述
緊接著上面用戶A與用戶B通信,本質就是網絡協議棧之間的通信:
在這里插入圖片描述
路由器和用戶A屬于一個局域網,路由器和用戶B也屬于通一個局域網。他們兩兩配置的網卡是相似的,在同一網段就能通信,以Windows為例:
在這里插入圖片描述
在網絡層不僅要添加報頭,還要路由。(路由:信息要去172.168.22,肯定不是自己這個局域網(在相同的局域網中,主機都是同樣的開頭,比如192開頭),所以肯定不是交給內網,要交給路由器)
在這里插入圖片描述
路由:發現不是發給自己局域網主機的報文,就推送給服務器。
網絡層到數據鏈路層的報頭是:macA幀(其中包含scr:macA,代表是主機A發送的。還有dst:macleft),其余主機會收到該報文,但發現dst的地址不是他們自己,就不做處理,只有路由器會收到。路由器收到該報文,確認報文是自己的,向上交付給網絡層:
在這里插入圖片描述
網絡層再次路由,然后路由器發現你要去dst:172.168.2.2和網卡對應的IP地址(172.168.2.1)差不多,于是再把報文再進行向下交付,重新封裝(報頭:src:macRight,dst:macB)
后有數據電路層收到報文并解包交給上層。
在這里插入圖片描述

這個過程我們發現:主機A網絡層拿到的報文、路由器網絡層拿到的報文、主機B網絡層拿到的報文,他們原IP、目的IP、數據是一模一樣的:
在這里插入圖片描述
結論:網絡層(就是IP層)向上(包括網絡層)看到的所有的報文都是一樣的,所以IP可以屏蔽底層網絡的差異!
這就是為什么用的手機用無線網、令牌環、油量…都能相互通信!

IP VS Mac地址

IP地址和mac地址的區別:
故事:
唐僧去西天取經
在這里插入圖片描述
其中發現上面有兩套地址中,其中車遲國領主、女兒國女王、火焰山領主就相當于一張路由表,方便我們查詢。
唐僧問車遲國國王下一站去哪里時,國王想的過程就是查詢一次路由表的過程。
唐僧提供地址信息,車遲國國王提供的路由表的信息,兩個信息一結合就知道接下來唐僧要去女兒國。
其中IP地址不變,變得都是mac地址。
在這里插入圖片描述
問題:為什么車遲國國王提供的信息是女兒國,不是其它的國家?
答:你和朋友在北京,要去云南旅游:
在這里插入圖片描述
其中mac地址受IP地址影響。(階段性目標受最終目標影響),比如最終目標是成為一名博士,前一個階段得先是研究生。

五.Socket編程預備

理解源IP地址和目的IP地址

IP在網絡中是標識主機唯一性的,跨網絡的。
mac地址是在局域網中標識唯一性的,只在局域網內有效,出了局域網就把“衣服”脫掉了。
問題:數據傳輸到主機是目的嗎?主機A把數據交給主機B就完了嗎?
在這里插入圖片描述
答:不是的。因為數據都是給人用的,比如:聊天是人與人聊天,游覽網頁是人在游覽。人是通過QQ來聊天、通過游覽器游覽網頁。啟動QQ和游覽器都是進程,進程是人在系統中代表,只要數據交給進程,人就相當于拿到了數據:
在這里插入圖片描述
傳輸數據到主機不是目的,而是手段。到達主機內部,在交給主機內的進程,才是目的。
為了在任意主機內標識進程,引入了一個概念:端口號。

端口號

端口號(port)是傳輸層協議的內容:
1.端口號是一個2字節16位的整數;
2.端口號用來標識一個進程,告訴操作系統,當前的這個數據要交給哪一個進程來處理;
在傳輸層報頭當中,會把端口號(port)寫入其中:
在這里插入圖片描述
需要網絡通信的這類進程(比如QQ、B站…)啟動的時候,需要自己綁定特定的端口號。
傳輸層收到報文時,通過報文中的端口號進行轉發給指定的進程,我們在未來寫代碼的時候,就一定要和指定的port進行關聯:
在這里插入圖片描述
通信是兩個人在通信,也可以說是兩個進程在通信。
IP + PORT = 互聯網中唯一的一個進程
在這里插入圖片描述
主機收到數據不是目的,而是手段,真正收到數據的是進程。
綜上所述:網絡通信的本質是進程間通信!!!
進程之間通信需要:
1.保證獨立性。
2.看到一份公共資源(網絡)。
其中IP地址+PORTA(端口)叫做socket通信。
問題:PID也是標識進程唯一性的,為什么不能用PID標識,而用port標識?
答:1.從技術上是可以做到的,但是PID是系統級別的概念,而port是網絡的概念,讓傳輸層用PID把數據交道應用層,就會產生強耦合。如果一個PID發生變換,那么整個網絡部分都要調整。所以要做到系統是系統,網絡是網絡,系統與網絡要做到解耦。
2.所有的進程都有PID,但不是所有的進程都想網絡通信,所以在系統中,只有少量通信的進程,需要端口號,未來可以用端口號來分別該進程是否需要網絡通信。
例子:在學校,每個人都有學號,且每個人都有身份證號,為什么學校不用身份證號來分辨而多出個學號呢?
方便管理,這就很好的做到了社會層面和學校層面的解耦,學號變了不影響身份證號。
注意:一個端口號只能被一個進程占用。

端口號范圍劃分

騰訊的QQ號不是所有的都能用,其內部保留了一些。
端口號也不是都能用的,一共有0~65535個端口號,說明一個服務器可以啟動65536個網絡服務,但不會那么多,3、4個服務已經夠多了。
0~1023:知名端口號,HTTP,FTP,SSH等這些廣為使用的應用層協議,他們的端口號都是固定的,深度捆綁。(這就跟110、120電話的性質類似)
1024~65535:操作系統動態分配的端口號(可以自己手動綁定)、客戶端程序的端口號。

認識TCP協議與UDP協議

TCP協議:傳輸層協議、有連接、可靠傳輸、面向字節流。
UDP協議:傳輸層協議、無連接、不可靠傳輸、面向數據報。
傳輸數據時,如果數據丟失了,TCP協議可以提供相對應的策略(比如:丟失后重傳、亂了進行重組排序),UDP協議丟了就是不管。
那就說了,直接用TCP協議不就得了,其實可靠傳輸是其的特征,不是優缺點,因為要保證可靠傳輸,說明要做更多的工作,得知道哪些丟了、哪些沒丟。UDP協議不用做這么多工作,使用上更簡單。
TCP協議常用于轉賬的領域;UDP協議常用于直播領域:
在這里插入圖片描述

網絡字節序(大小端)

我們學習C語言時就明白,存在大小端的問題,就是數據的存儲方式。
把低權值位放到高地址就是大端機,把低權值位放到低地址處就是小端機:
在這里插入圖片描述
不同的機器存儲的方式是由差別的。
問題:如果主機A以大端的方式發送數據,主機B以小端的方式接收數據,不就反了?
在這里插入圖片描述
答:定了個標準,網絡通信必須大端!!即低地址高字節。

網絡主機相互轉換接口

未來為了使我們寫的程序具有可移植性,在寫通信的過程中大部分的大小端會自動幫我們轉了,但端口號和IP地址之類的需要自己轉化,系統提供了函數:
在這里插入圖片描述

socket編程接口

// 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)int socket(int domain, int type, int protocol);// 綁定端口號 (TCP/UDP, 服務器)      
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 開始監聽socket (TCP, 服務器)int listen(int socket, int backlog);// 接收請求 (TCP, 服務器)int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立連接 (TCP, 客戶端)int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen)

發現很多函數都用了sockaddr結構體,先說該結構體。

sockaddr結構

socket API是一層抽象的網絡編程接口,適用于各種底層網絡協議,如IPv4、IPv6。
socket的發明者不僅僅想讓進程可以完成網絡通信,也可以完成本主機通信:
在這里插入圖片描述
網絡在設計的時候,就考慮到讓用戶使用同一套的接口,能用來表示不同種類的通信方式,這不就是多態嘛!!

六.Socket 編程 UDP

echo server 服務端

首先要創建套接字。
socket函數:
在這里插入圖片描述
其中AF_UNIX與套接字SOCK_DGRAM都是宏:
在這里插入圖片描述
返回值:成功時,返回文件描述符;出錯時,返回-1
在這里插入圖片描述
創建套接字可以理解成把網卡打開了,在Linux系統中一切皆文件,網卡也是文件,所以可以通過返回的文件描述符對網卡設備進行讀寫(IO)。
未來進行收消息或發消息都需要sockfd(套接字):
在這里插入圖片描述

  1. 創建socket文件:
void InitServer() // 初始化_sockfd
{// 1.創建socket文件_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);
}
  1. 綁定:

客戶端和服務器都要有IP地址與端口號:
在這里插入圖片描述
bind函數,讓套接字信息(IP、端口號…)與套接字(socket)關聯起來。
在這里插入圖片描述
struct sockaddr_in 里面的內容:
在這里插入圖片描述

  • sin_family儲存AF_INET(網絡的信息)。
  • sin_port儲存16位的端口號。
  • sin_addr存儲IP36位的IP地址。
    發短信時,主機A不僅要把信息發送給主機B,主機A還把IP地址與端口交給主機B,因為主機B要給主機A應答。說明IP地址與端口都要走網絡,所以IP地址與端口號要轉成網絡序列(大端)。
    平常我們喜歡用string類型的變量當IP地址,但是使用時需要4字節的網絡序列,所以需要把string類型轉換為4字節的網絡序列。
    就有了接口:inet_addr
    在這里插入圖片描述
// 2.bindstruct sockaddr_in local;memset(&local,0,sizeof(local)); //置空local.sin_family = AF_INET;local.sin_port = htons(_localport); //主機轉網絡序列local.sin_addr.s_addr = inet_addr(_locallip.c_str()); //1.4字節 2.網絡序列int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local)); if(n < 0){LOG(FATAL,"bind error");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success");

服務器運行之后就不會關,就比如某殺毒軟件,你關了也關不上。
所以啟動是死循環,干什么事情在里面填寫:

void Start(){_isrunning = true;while(_isrunning){//otherthing}}

整體代碼如下:

#pragma once#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,
};int gsockfd = -1;
uint16_t glocalport = 888;// UdpServer user("192.1.1.1",8899);
class UdpServer : public nocopy //繼承單例
{
public:UdpServer(std::string &locallip,uint16_t localport = glocalport): _sockfd(gsockfd),_localport(localport),_locallip(locallip),_isrunning(false){}void InitServer(){// 1.創建socket文件_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);// 2.bindstruct sockaddr_in local;memset(&local,0,sizeof(local)); //置空local.sin_family = AF_INET;local.sin_port = htons(_localport); // 主機轉網絡序列local.sin_addr.s_addr = inet_addr(_locallip.c_str()); //1.string轉成4字節 2.主機轉網絡網絡序列int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n < 0){LOG(FATAL,"bind error");exit(BIND_ERROR);}LOG(DEBUG,"socket bind success");}void Start(){_isrunning = true;while(_isrunning){//otherthing}}~UdpServer(){}private:int _sockfd;uint16_t _localport;std::string _locallip;bool _isrunning;
};

可以用指令netstat查看:
在這里插入圖片描述
在這里插入圖片描述

收數據

雖然sockfd和文件描述符相似,但不能直接使用write和read…因為sockfd是數據報的,不是字節流的。
要用recvfrom來讀:
在這里插入圖片描述
在這里插入圖片描述
返回值:失敗返回-1;返回收到信息的大小。
發送消息:sendto函數
在這里插入圖片描述
返回值:失敗返回-1;返回發送信息的大小:
在這里插入圖片描述

代碼:

void Start(){_isrunning = true;char buffer[1024];while(_isrunning){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sockfd,&buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);}}

Client 客戶端

  • 首先客戶端一定在未來知道服務器的IP地址和端口號的。
    IP地址:比如在網頁登入QQ時,www.qq.com,這是域名,登入時會轉化成IP地址。
    在這里插入圖片描述
    端口號:跟服務端是強關聯的(比如:我們平常說打報警電話,很少說打報警電話110)。
  • 其次客服端一定要有自己的IP與端口,來區分客戶端的唯一性。
    server的端口號,必須由用戶指明,而且是明確的,不能隨意改變。(淘寶、百度…)
    client的端口號,一般不讓用戶自己設定,而是讓client OS隨機選擇。
    原因:服務端是被多個人訪問的,所以不能隨意改變端口號。客戶端,以手機為例,有微信、京東…不同公司的客戶端,比如淘寶喜歡888的端口號,京東也喜歡888的端口號,在點擊啟動的時候,因為一個進程綁定888后,其余進程就用不了888了,就造成淘寶能打開,京東就打不開了。所以不能固定綁定客戶端的端口號。
    綜上所述:client 需要 bind 它自己的IP和端口號,但是client 不需要 自己手動填充 bind它自己的IP和端口。
    問題:client如何選擇端口號,什么時候選擇端口號?
    答:客戶端在首次向服務器發送數據的時候,OS會自動給client bind 它自己家的IP和端口,在首次調用sendto函數時,綁定IP與端口號。
    client總的代碼:
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"// ./udp_client server-ip server-port
// ./udp_client 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << "server-ip server-port" << std::endl;}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 創建socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}struct sockaddr_in server;memset(&server, 0, sizeof(server)); //初始化server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);while (1){std::string line;std::cout << "Please Enter# ";getline(std::cin, line);int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server)); // 發數據if (n > 0){struct sockaddr_in temp;socklen_t len = sizeof(temp);char in_buffer[1024];int m = recvfrom(sockfd, in_buffer, sizeof(in_buffer)-1, 0, (struct sockaddr *)&temp, &len); //收數據if (m > 0){in_buffer[m] = 0;std::cout << in_buffer << std::endl;}else{break;}}else{break;}}return 0;
}

注意事項

  1. 若服務器和客戶端在同一臺機器上,通信的過程不會走到網絡里,這樣保證雙方的軟件內部不會出錯,測試通過再引入跨網絡通信。
    在這里插入圖片描述
  2. 云服務器綁定IP比較特殊,服務端不能直接(也強烈不建議)bind自己的公網IP:
    在這里插入圖片描述
    因為云服務器的共享IP是虛擬出來的,服務器上本身ip就沒有101.200.125.68這個IP。
    下面這兩個才是真正的服務器IP。
    在這里插入圖片描述
    內網IP是可以綁定的:
    在這里插入圖片描述
    但綁定了內網IP,是無法從外網上收消息了。
    為了解決上面的兩種情況,一般把云服務的IP地址設為0:
    在這里插入圖片描述
    0代表的是:可以讓服務器bind任意IP。
    舉個例子:比如服務器配了倆共享IP,IP1和IP2,端口號:8888,那么這個服務器可以收到IP1:8888的報文,也可以收到IP2:8888的報文:
    在這里插入圖片描述
    綁定的IP為0的服務器,就能收到端口號相同的任何IP報文了。

整體的代碼與效果

Log.hpp 用來檢查用的日志,直接調用LOG(level,內容);即可

#pragma once#include <iostream>
#include <unistd.h>
#include <time.h>
#include <cstdarg>
#include <pthread.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "LockGuard.hpp"#define SCREEN_TYPE 1
#define FILE_TYPE 2namespace log_ns
{std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level) // 日志等級{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "Error";case FATAL:return "FATAL";default:return "UNKONE";}}std::string GetCurrTime() // 目前的時間{time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class Logmessage // 日志的內容{public:std::string _level;pid_t _pid;std::string _filename;int _filenumber;std::string _time;std::string _messageinfo;};class Log{public:Log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const Logmessage &lg){printf("[%s][%d][%s][%d][%s][%s]\n",lg._level.c_str(),lg._pid,lg._filename.c_str(),lg._filenumber,lg._time.c_str(),lg._messageinfo.c_str());}void FlushLogToFile(const Logmessage &lg){char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s][%s]\n",lg._level.c_str(),lg._pid,lg._filename.c_str(),lg._filenumber,lg._time.c_str(),lg._messageinfo.c_str());int fd = open(_logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0)return perror("open file false");write(fd, logtxt, strlen(logtxt));close(fd);}void FlushLog(const Logmessage &lg) // 判斷刷新到到屏幕還是文件{if (lg._level == "DEBUG")return;LockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;default:printf("_type error \n");break;}}void LogMessage(std::string filename, int filenumber, int level, const char *format, ...) // 刷新的內容{Logmessage lg;lg._level = LevelToString(level);lg._pid = getpid();lg._filename = filename;lg._filenumber = filenumber;lg._time = GetCurrTime();va_list ap;va_start(ap, format);char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);va_end(ap);lg._messageinfo = log_info;FlushLog(lg);}~Log(){}private:int _type;            // 打印到屏幕還是文件中std::string _logfile; // 哪個文件出了問題};Log lg;#define LOG(level, format, ...)                                        \do                                                                 \{                                                                  \lg.LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \} while (0)
#define EnableScrean()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
}

NoCopy.hpp 單例模式

#pragma onceclass nocopy
{
public:nocopy(){}~nocopy(){}nocopy(const nocopy&) = delete;const nocopy& operator =(const nocopy& ) = delete;
};

UdpServer.hpp:

#pragma once#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,
};int gsockfd = -1;
uint16_t glocalport = 8888;// UdpServer user(8899);
class UdpServer : public nocopy
{
public:UdpServer(uint16_t localport = glocalport): _sockfd(gsockfd),_localport(localport),_isrunning(false){}void InitServer(){// 1.創建socket文件_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "create socket error");exit(SOCKET_ERROR);}// LOG(DEBUG, "create socket success,sockfd :%d", _sockfd);// 2.bindstruct sockaddr_in local;memset(&local, 0, sizeof(local)); // 置空local.sin_family = AF_INET;local.sin_port = htons(_localport); // 1.網絡序列local.sin_addr.s_addr = INADDR_ANY;int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error");exit(BIND_ERROR);}LOG(INFO, "socket bind success");}void Start(){_isrunning = true;char buffer[1024];while (_isrunning){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0) // 返回收到的信息{std::string ip = inet_ntoa(peer.sin_addr); // 4字節-》stringuint16_t port = htons(peer.sin_port);      // 網絡轉主機buffer[n] = 0;std::cout << "[" << ip << ":" << port << "]#" << buffer << std::endl;// LOG(DEBUG, "recvfrom return is :%d", n);std::string echo_server = "[udp_server echo] #";echo_server += buffer;LOG(DEBUG, "echo_server :%s", echo_server.c_str());sendto(_sockfd, echo_server.c_str(), sizeof(echo_server), 0, (struct sockaddr *)&peer, len);}}}~UdpServer(){if (_sockfd > gsockfd)::close(_sockfd);}private:int _sockfd;uint16_t _localport;bool _isrunning;
};

UdpServerMain.cc

#include "UdpServer.hpp"
#include <memory>// ./udp_server local-port
// ./udp_server 8888
int main(int argc, char *argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << "server-port" << std::endl;}EnableScrean();uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port);usvr->InitServer();usvr->Start();return 0;
}

UdpClientMain.cc

#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "NoCopy.hpp"
#include "Log.hpp"using namespace log_ns;// ./udp_client server-ip server-port
// ./udp_client 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << "server-ip server-port" << std::endl;exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 創建socketint sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());server.sin_port = htons(serverport);while (1){std::string line;std::cout << "Please Enter# ";getline(std::cin, line);LOG(DEBUG,"%s",line.c_str());int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr *)&server, sizeof(server)); // 發數據// LOG(DEBUG,"sendto return is %d",n);if (n > 0){struct sockaddr_in temp;socklen_t len = sizeof(temp);char in_buffer[1024];int m = recvfrom(sockfd, in_buffer, sizeof(in_buffer)-1, 0, (struct sockaddr *)&temp, &len);if (m > 0){in_buffer[m] = 0;std::cout << in_buffer << std::endl;}else{break;}}else{break;}}return 0;
}

效果

在這里插入圖片描述

七.網絡命令

Ping 命令

檢查網絡連通性。
問題:在Windows電腦上有一個云服務器,如何保證電腦與云服務器連通呢?(比如windows沒有連網,沒法連)
答:Ping命令就算檢查兩臺主機是否能聯通。
測試是否能連通網絡,連通百度:
在這里插入圖片描述
未來寫了一種網絡服務,比如寫了一個TCP的服務,請求時怎樣都拿不到結果,可以先拿Ping命令測試網絡是否連通,若連通,說明網絡服務本身就有問題。

  • Ping命令一旦啟動,就不會停止的,若只想Ping 1次,就有了選項-c(cont的意思)

在這里插入圖片描述

netstat 命令

netstat時一個用來查看網絡狀態的重要工具。
netstat通常用來查看網絡服務,UDP/TCP啟動起來就是一個進程,我們能用ps能查到該進程偏于進程方面的信息,若想查看網絡方面的屬性的字段,就用netstat:
在這里插入圖片描述
在這里插入圖片描述

-u查看udp服務
-t查看TCP服務
-a查看所有的服務
-p查看與哪個進程關聯
-n把能顯示成數字的,顯示成數字
-l把處于LISTEN狀態的TCP服務顯示出來

watch命令可以每隔x秒讓該命令執行一次:

watch -n 1 netstat -nuap #每隔一秒執行一次

在這里插入圖片描述

pidof 命令

拿到進程的PID。
服務器有時候處于后端,簡單的ctrl+c是殺不死的,所以需要kill命令。
經常會這么用:

pidof [進程名] | xargs kill -9

xargs的作用:把管道中的數據,轉換成后續的數據。
kill是用標準輸入文件描述符0,來把數據讀到kill中。
在這里插入圖片描述

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

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

相關文章

網絡安全-使用DeepSeek來獲取sqlmap的攻擊payload

文章目錄 概述DeepSeek使用創建示例數據庫創建API測試sqlmap部分日志參考 概述 今天來使用DeepSeek做安全測試&#xff0c;看看在有思路的情況下實現的快不快。 DeepSeek使用 我有一個思路&#xff0c;想要測試sqlmap工具如何dump數據庫的&#xff1a; 連接mysql數據庫&#…

AI繪畫軟件Stable Diffusion詳解教程(2):Windows系統本地化部署操作方法(專業版)

一、事前準備 1、一臺配置不錯的電腦&#xff0c;英偉達顯卡&#xff0c;20系列起步&#xff0c;建議顯存6G起步&#xff0c;安裝win10或以上版本&#xff0c;我的顯卡是40系列&#xff0c;16G顯存&#xff0c;所以跑大部分的模型都比較快&#xff1b; 2、科學上網&#xff0…

Linux NAT和代理服務器

目錄 0.前言 1.NAT 網絡地址轉換 1.1 NAT 技術背景 1.2 NAT的定義與分類 1.3 NAT的工作原理 1.4 NAT的缺陷 2.代理服務器 2.1 概述 2.2 正向代理 2.3 反向代理 2.4 NAT 與代理服務器的區別和聯系 3.小結 &#xff08;圖像由AI生成&#xff09; 0.前言 在前面的文章中&#x…

AI學習第七天

數組&#xff1a;基礎概念、存儲特性及力扣實戰應用 在計算機科學與數學的廣袤領域中&#xff0c;數組作為一種極為重要的數據結構&#xff0c;發揮著不可或缺的作用。它就像一個有序的 “數據倉庫”&#xff0c;能高效地存儲和管理大量數據。接下來&#xff0c;讓我們深入了解…

ue5 創建多列StreeView的方法與理解

創建StreeView的多列樣式怎么就像是創建單行單列差不多?貌似就是在單行單列中加入了多列widget? 示例代碼 DetailTabWidget #pragma once #include "TreeViewItemBase.h"class SDetailTabWidget : public SCompoundWidget {SLATE_BEGIN_ARGS(SDetailTabWidget){…

Linux之yum詳解

—— 小 峰 編 程 目錄 1、Linux軟件的安裝方式 2、什么是yum 3、配置網絡yum源 4、yum命令 【語法】 【yum常用命令】 1、Linux軟件的安裝方式 在CentOS系統中&#xff0c;軟件管理方式通常有三種方式&#xff1a; rpm安裝 、 yum安裝 以及 編譯安裝 。 2、什么是yum…

lvgl運行機制分析

lv_timer_handler() 是 LVGL 的“心臟”&#xff1a;這個函數會依次做以下事情&#xff1a; 處理定時器&#xff08;如動畫、延遲回調&#xff09;。 讀取輸入設備&#xff08;如觸摸屏、按鍵的狀態&#xff09;。 刷新臟區域&#xff08;僅重繪屏幕上發生變化的區域&#xf…

達夢數據庫授權給某個用戶查詢其他指定用戶下所有表的權限

方法1&#xff1a; 新版本有一個數據庫參數 GRANT_SCHEMA&#xff0c;表示是否開啟授予和回收模式權限功能。0&#xff1a;否&#xff1b;1&#xff1a;是 此參數為靜態參數&#xff0c;默認是0&#xff0c;將改參數修改為1后&#xff0c;重啟數據庫生效。 將參數修改為1 S…

人大金倉國產數據庫與PostgreSQL

一、簡介 在前面項目中&#xff0c;我們使用若依前后端分離整合人大金倉&#xff0c;在后續開發過程中&#xff0c;我們經常因為各種”不適配“問題&#xff0c;但可以感覺得到大部分問題&#xff0c;將人大金倉視為postgreSQL就能去解決大部分問題。據了解&#xff0c;Kingba…

Python之參數星號(*)使用筆記

背景 在學習python時發現方法調用和方法定義會經常發現有帶星號的標記&#xff0c;為了弄明白是怎么使用的。特此做個筆記。 一、參數符號對比速查表 符號類使用場景作用描述示例無符號函數定義/調用普通位置參數或關鍵字參數.def func(a, b)*函數定義收集多余位置參數為元組…

使用haproxy實現MySQL服務器負載均衡

一、環境準備 主機名IP地址備注openEuler-1192.168.121.11mysql-server-1openEuler-2192.168.121.12mysql-server-2openEuler-3192.168.121.13clientRocky-1192.168.121.51haproxy 二、mysql-server配置 [rootopenEuler-1 ~]# yum install -y mariadb-server [rootopenEuler…

Python與Web3.py庫:構建去中心化應用的未來

Python與Web3.py庫&#xff1a;構建去中心化應用的未來 在區塊鏈的世界里&#xff0c;“去中心化”是最核心的理念之一&#xff0c;它賦予了用戶更多的控制權和自由&#xff0c;消除了傳統中心化系統中的單點故障和信任問題。而在這場技術革命中&#xff0c;Web3.0無疑是最受矚…

對“預訓練”的理解

預訓練有什么用 傳統的機器學習是偏數學的&#xff0c;對數據的量不做過多要求&#xff0c;而深度學習的項目通常是有大量的數據可供使用。 在平常的任務或者項目中&#xff0c;我們可能并沒有大量數據&#xff0c;只有少量數據&#xff0c;在這時我們就可以通過“借用”有大…

VMware Ubuntu 共享目錄

在VMware中掛載Ubuntu共享目錄需要以下步驟&#xff0c;分為設置共享文件夾和在Ubuntu中掛載兩部分&#xff1a; 一、VMware 設置共享文件夾 關閉Ubuntu虛擬機 在配置前&#xff0c;建議先關閉虛擬機&#xff08;若已運行需關閉&#xff0c;部分VMware版本支持熱添加&#xff0…

AF3 crop_chains函數解讀

AlphaFold3 feature_processing_multimer模塊的crop_chains函數的功能是對多條鏈的蛋白質結構預測任務中的MSA(多序列比對)特征和模板特征進行裁剪(cropping)。裁剪的目的是為了控制輸入模型的MSA序列數量和模板數量,以適應模型的輸入限制或優化計算效率。 源代碼: def…

Java基礎-數組,集合創建方式

Java 中 new 關鍵字的作用 在 Java 中&#xff0c;new 關鍵字用于 在堆內存中分配空間 并創建對象。 數組 和 集合 在 Java 中都是對象&#xff0c;因此必須使用 new 來創建實例。Java 和 C 之間的主要區別在于 內存管理 和 對象的創建方式。 Java 與 C 中數組 & 集合的創…

LeeCode題庫第三十九題

39.組合總和 項目場景&#xff1a; 給你一個 無重復元素 的整數數組 candidates 和一個目標整數 target &#xff0c;找出 candidates 中可以使數字和為目標數 target 的 所有 不同組合 &#xff0c;并以列表形式返回。你可以按 任意順序 返回這些組合。 candidates 中的 同…

大模型應用案例 | 大模型+金融運維,擎創攜手某證券創新運維能力新范式

一、當大模型遇上金融運維&#xff1a;一場讓告警處理“脫胎換骨”的變革 2022年底&#xff0c;ChatGPT的橫空出世讓AI技術徹底出圈&#xff1b;短短兩年后&#xff0c;大模型已悄然潛入金融行業的“心臟地帶”——運維系統。面對指數級暴增的告警信息、碎片化的處理流程&#…

【MongoDB】在Windows11下安裝與使用

官網下載鏈接&#xff1a;Download MongoDB Community Server 官方參考文檔&#xff1a;https://www.mongodb.com/zh-cn/docs/manual/tutorial/install-mongodb-on-windows/#std-label-install-mdb-community-windows 選擇custom類型&#xff0c;其他默認 注意&#xff0c;此選…

阿里云輕量級服務器通過寶塔安裝PgVector要點

設置環境變量&#xff1a; export PG_HOME/www/server/pgsql export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/www/server/pgsql/lib export PG_CONFIG/www/server/pgsql/bin/pg_config export PGDATA/www/server/pgsql/data PATH$PATH:$HOME/.local/bin:$HOME/bin:$PG_HOME/bin ali…