国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】揭開套接字編程的神秘面紗(上)

這篇具有很好參考價值的文章主要介紹了【Linux】揭開套接字編程的神秘面紗(上)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

??? 作者:@阿亮joy.
??專欄:《學(xué)會Linux》
?? 座右銘:每個優(yōu)秀的人都有一段沉默的時光,那段時光是付出了很多努力卻得不到結(jié)果的日子,我們把它叫做扎根
【Linux】揭開套接字編程的神秘面紗(上)

??預(yù)備知識??

源IP地址和目的IP地址

源 IP 地址指發(fā)送方的 IP 地址,而目的 IP 地址是指接收方的 IP 地址,源 IP 地址和目的 IP 地址是包含在數(shù)據(jù)包的IP 頭部(IP header)中的,IP 頭部是每個 IP 數(shù)據(jù)包都必須包含的一部分。這兩個地址在網(wǎng)絡(luò)傳輸過程中是不變的,因為它們是數(shù)據(jù)包的一部分,并且用于確定數(shù)據(jù)包的源和目的地。

在網(wǎng)絡(luò)傳輸過程中,每個節(jié)點都會讀取 IP 頭部的內(nèi)容,并基于其中的源 IP 地址和目的 IP 地址等信息來決定下一步的路由和轉(zhuǎn)發(fā)操作。有了源 IP 地址和目的 IP 地址,還不能完成網(wǎng)絡(luò)傳輸,還需要 MAC 地址。IP 地址用于標(biāo)識主機在網(wǎng)絡(luò)中的位置,MAC 地址用于標(biāo)識主機的網(wǎng)絡(luò)接口。在不同網(wǎng)絡(luò)之間的通信,通常需要使用 IP 地址進行路由和轉(zhuǎn)發(fā),而在同一局域網(wǎng)內(nèi)的通信,則需要使用 MAC地址進行直接傳輸。

端口號

端口號是傳輸層協(xié)議的內(nèi)容,它是用于標(biāo)識網(wǎng)絡(luò)應(yīng)用程序的通信端口的一個 16 位的數(shù)字,其取值范圍是 0 到 65535。其中 0 到 1023 的端口號被保留用于一些特定的服務(wù)和應(yīng)用程序,稱為“系統(tǒng)端口”或“熟知端口”,例如 HTTP 服務(wù)使用的端口號為 80,SMTP 服務(wù)使用的端口號為 25。每個端口號都與一個特定的應(yīng)用程序或服務(wù)相關(guān)聯(lián),用于區(qū)分同一主機上的不同應(yīng)用程序或在網(wǎng)絡(luò)上的不同主機上的不同應(yīng)用程序。

在一個網(wǎng)絡(luò)數(shù)據(jù)包中,源和目標(biāo)主機的 IP 地址用于標(biāo)識主機在網(wǎng)絡(luò)中的位置,而源和目標(biāo)端口號則用于標(biāo)識主機上的應(yīng)用程序(進程)。網(wǎng)絡(luò)應(yīng)用程序使用端口號來與其他應(yīng)用程序進行通信。在網(wǎng)絡(luò)傳輸中,源主機上的應(yīng)用程序?qū)?shù)據(jù)發(fā)送到目標(biāo)主機上的特定端口號,目標(biāo)主機會將數(shù)據(jù)包路由到相應(yīng)的應(yīng)用程序上進行處理。因此,端口號的作用是為應(yīng)用程序提供一種可靠的通信機制,使不同的應(yīng)用程序可以在同一主機上共存,或者在不同主機上進行通信。

注:IP地址 + 端口號能夠標(biāo)識網(wǎng)絡(luò)上的某一臺主機的某一個進程,一個端口號只能被一個進程占用,而一個進程可以綁定多個端口號。

PID和端口號的區(qū)別

PID 可以標(biāo)識唯一的進程,而端口號也能標(biāo)識唯一的一個進程。那為什么網(wǎng)絡(luò)通信不采用 PID 來表示唯一的進程呢?雖然 PID 在某些情況下可以用來標(biāo)識特定的應(yīng)用程序(進程),但在網(wǎng)絡(luò)中,PID 不是一種可靠的方式來標(biāo)識應(yīng)用程序,原因如下:

  • PID 只在單個計算機上是唯一的:每個計算機上的進程都有自己的 PID,因此,在不同的計算機上運行的同一應(yīng)用程序具有不同的 PID。這意味著在網(wǎng)絡(luò)上使用 PID 來標(biāo)識應(yīng)用程序時,它只能標(biāo)識單個計算機上的應(yīng)用程序,而不能唯一地標(biāo)識整個網(wǎng)絡(luò)中的應(yīng)用程序。
  • PID 是動態(tài)的:當(dāng)一個應(yīng)用程序在計算機上啟動時,它被分配一個 PID。但是,當(dāng)該應(yīng)用程序終止后,該 PID 將被釋放并可以被操作系統(tǒng)重新分配給其他進程。因此,使用 PID 來標(biāo)識應(yīng)用程序可能會導(dǎo)致標(biāo)識符沖突或標(biāo)識符混淆,因為一個新的應(yīng)用程序可能會被分配以前已經(jīng)被釋放的 PID。

相比之下,端口號是一種更可靠的方式來標(biāo)識網(wǎng)絡(luò)中的應(yīng)用程序,因為它在整個網(wǎng)絡(luò)中是唯一的,并且不會被操作系統(tǒng)重新分配給其他應(yīng)用程序。此外,端口號可以方便地被網(wǎng)絡(luò)管理人員和安全人員用于限制或控制網(wǎng)絡(luò)通信,從而增強網(wǎng)絡(luò)安全性。

套接字

套接字(socket)是一種用于在計算機網(wǎng)絡(luò)中進行通信的軟件設(shè)備,它提供了一種抽象層,使得應(yīng)用程序可以使用統(tǒng)一的接口來進行網(wǎng)絡(luò)通信,而無需了解底層網(wǎng)絡(luò)協(xié)議的復(fù)雜性。

套接字通常由一個 IP 地址和一個端口號組成,它們一起標(biāo)識了網(wǎng)絡(luò)中的一個特定的節(jié)點。一個套接字可以用來建立連接、發(fā)送和接收數(shù)據(jù),同時也可以被關(guān)閉和釋放。

在應(yīng)用程序中,套接字通常由操作系統(tǒng)提供的套接字庫進行管理。套接字庫提供了一組函數(shù),使得應(yīng)用程序可以方便地創(chuàng)建、綁定、監(jiān)聽和連接套接字,并進行數(shù)據(jù)的發(fā)送和接收。套接字庫也提供了一些高級函數(shù),如 select 和 poll,用于進行異步通信和多路復(fù)用。

套接字可以用于各種類型的網(wǎng)絡(luò)通信,如 TCP、UDP 和RAW 等協(xié)議。TCP 套接字提供了面向連接的、可靠的數(shù)據(jù)傳輸服務(wù),適用于需要可靠傳輸?shù)膽?yīng)用程序,如 Web 瀏覽器、郵件客戶端等;UDP 套接字則提供了無連接、不可靠的數(shù)據(jù)傳輸服務(wù),適用于實時性要求高、數(shù)據(jù)傳輸量較小的應(yīng)用程序,如在線游戲、語音聊天等;RAW 套接字則可以讓應(yīng)用程序直接訪問網(wǎng)絡(luò)協(xié)議棧,適用于需要自定義協(xié)議和進行網(wǎng)絡(luò)調(diào)試的應(yīng)用程序。

綜上,網(wǎng)絡(luò)編程也被稱為套接字編程。

認(rèn)識UDP協(xié)議

UDP 協(xié)議(用戶數(shù)據(jù)報協(xié)議)是一種無連接的、面向數(shù)據(jù)報、不可靠的協(xié)議,它不提供連接建立和數(shù)據(jù)校驗等功能,而是將數(shù)據(jù)直接打包成數(shù)據(jù)報發(fā)送,不保證數(shù)據(jù)的可靠性。UDP 適用于實時性要求較高的應(yīng)用,如在線游戲、語音聊天等,因為它具有低延遲、高吞吐量的優(yōu)勢。UDP 的優(yōu)點在于它的簡單、高效,但是由于它不保證數(shù)據(jù)的可靠性,因此需要應(yīng)用程序自己處理數(shù)據(jù)的錯誤和丟失等問題。

認(rèn)識TCP協(xié)議

TCP 協(xié)議(傳輸控制協(xié)議)是一種面向連接的、年面向字節(jié)流的、可靠的協(xié)議,它通過三次握手建立連接,并提供了流控制、擁塞控制、錯誤校驗等功能,保證數(shù)據(jù)傳輸?shù)目煽啃?。TCP 適用于對數(shù)據(jù)可靠性要求較高的應(yīng)用,如 Web 瀏覽器、郵件客戶端、文件傳輸?shù)取CP 協(xié)議的優(yōu)點在于它可以保證數(shù)據(jù)的可靠傳輸,但是由于它的連接建立和數(shù)據(jù)校驗等過程會增加網(wǎng)絡(luò)傳輸?shù)难舆t和開銷,因此在實時性要求較高的應(yīng)用中并不適用。

總的來說,TCP 適用于對數(shù)據(jù)可靠性要求較高的應(yīng)用,UDP 適用于實時性要求較高、數(shù)據(jù)傳輸量較小的應(yīng)用。在實際應(yīng)用中,根據(jù)應(yīng)用的不同需求,選擇合適的協(xié)議進行網(wǎng)絡(luò)傳輸,也可以根據(jù)需要將 TCP 和 UDP 協(xié)議結(jié)合使用,來達到更好的效果。

網(wǎng)絡(luò)字節(jié)序

網(wǎng)絡(luò)字節(jié)序(Network Byte Order)是一種統(tǒng)一的字節(jié)序,用于在計算機網(wǎng)絡(luò)中進行數(shù)據(jù)傳輸。由于不同的計算機可能使用不同的字節(jié)序(大小端),因此在網(wǎng)絡(luò)傳輸中,需要使用一種固定的字節(jié)序來確保數(shù)據(jù)的正確傳輸和解析。

網(wǎng)絡(luò)字節(jié)序采用的是大端字節(jié)序(Big-endian):將高位字節(jié)存放在內(nèi)存的低地址處,低位字節(jié)存放在內(nèi)存的高地址處。在網(wǎng)絡(luò)字節(jié)序中,所有數(shù)據(jù)類型(如整型、浮點型、字符型等)都采用相同的字節(jié)序,這樣就可以保證在不同的計算機上進行數(shù)據(jù)傳輸和解析時,不會出現(xiàn)字節(jié)序不一致的問題。

【Linux】揭開套接字編程的神秘面紗(上)
【Linux】揭開套接字編程的神秘面紗(上)

在 C 語言中,可以使用 htons、htonl、ntohs、ntohl 等函數(shù)來進行字節(jié)序轉(zhuǎn)換,其中,htons 和 htonl 函數(shù)用于將主機字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,ntohs 和 ntohl 函數(shù)用于將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換為主機字節(jié)序。

【Linux】揭開套接字編程的神秘面紗(上)
【Linux】揭開套接字編程的神秘面紗(上)

總之,網(wǎng)絡(luò)字節(jié)序是一種固定的字節(jié)序,用于在計算機網(wǎng)絡(luò)中進行數(shù)據(jù)傳輸和解析,保證了不同計算機之間數(shù)據(jù)的互通性和正確性。

??套接字編程??

套接字的分類

  • 域間套接字(Inter-process Communication Socket,IPC Socket):也叫UNIX域套接字(Unix Domain Socket),是一種特殊的套接字類型,用于在同一臺計算機上不同進程之間進行通信,屬于進程間通信(IPC)機制的一種。它不需要經(jīng)過網(wǎng)絡(luò)協(xié)議棧的處理,因此傳輸速度非??欤膊粫艿骄W(wǎng)絡(luò)攻擊的影響。域間套接字使用一個本地的文件名作為地址,進程可以通過這個地址來建立連接并進行通信。

  • 原始套接字(Raw Socket):也叫原始套接字類型,可以直接訪問傳輸層以下的網(wǎng)絡(luò)層協(xié)議,用于構(gòu)造和發(fā)送自定義的網(wǎng)絡(luò)協(xié)議數(shù)據(jù)包。原始套接字通常用于網(wǎng)絡(luò)協(xié)議分析、網(wǎng)絡(luò)攻擊、網(wǎng)絡(luò)安全等領(lǐng)域。使用原始套接字需要有足夠的權(quán)限,因為它可以直接訪問網(wǎng)絡(luò)層協(xié)議,對網(wǎng)絡(luò)安全帶來潛在的威脅。

  • 網(wǎng)絡(luò)套接字(Internet Socket):也叫基于IP協(xié)議的套接字,是使用 TCP / IP 協(xié)議族進行通信的套接字類型。網(wǎng)絡(luò)套接字提供可靠的面向連接的通信服務(wù),支持 TCP 和 UDP 協(xié)議。在使用網(wǎng)絡(luò)套接字時,需要使用 IP 地址和端口號來標(biāo)識網(wǎng)絡(luò)中的進程,IP 地址用于標(biāo)識主機,端口號用于標(biāo)識主機上的應(yīng)用程序。網(wǎng)絡(luò)套接字使用 IPv4 或 IPv6 協(xié)議,其中 IPv4 協(xié)議使用 32 位地址,IPv6 協(xié)議使用 128 位地址,能夠更好地滿足互聯(lián)網(wǎng)的需求。

那 Linux 是如何設(shè)計這三類套接字的呢?Linux 系統(tǒng)為不同類型的套接字提供了統(tǒng)一的 socket API,并根據(jù)不同的地址族和套接字類型實現(xiàn)了不同的網(wǎng)絡(luò)協(xié)議和數(shù)據(jù)結(jié)構(gòu)。應(yīng)用程序只需要按照 API 規(guī)范來創(chuàng)建和使用套接字,就可以實現(xiàn)進程間通信和網(wǎng)絡(luò)通信等功能。

套接字的數(shù)據(jù)結(jié)構(gòu)

【Linux】揭開套接字編程的神秘面紗(上)

sockaddr、sockaddr_in 和 sockaddr_un 都是在 socket 編程中用于表示套接字地址的數(shù)據(jù)結(jié)構(gòu),它們之間的關(guān)系如下:

  • sockaddr 是一個通用的套接字地址結(jié)構(gòu)體,它包含了地址族、地址信息等字段。在 socket 編程中,通常需要將sockaddr 類型的地址轉(zhuǎn)換成對應(yīng)的具體類型的地址結(jié)構(gòu)體,例如 sockaddr_in 或 sockaddr_un,才能方便地進行相關(guān)的操作。sockaddr 的結(jié)構(gòu)體定義如下:
struct sockaddr 
{
    sa_family_t sa_family;      //地址族(AF_xxx)
    char        sa_data[14];    //14字節(jié)協(xié)議特定地址信息
};
  • sa_family 表示地址族,具體取值可以是 AF_UNIX、AF_INET、AF_INET6 等,sa_data 字段是協(xié)議特定的地址信息。在實際使用中,sockaddr 通常會被轉(zhuǎn)換為其他具體的地址結(jié)構(gòu)體,例如 sockaddr_in 或 sockaddr_un。

  • sockaddr_in 是 Internet 域套接字地址結(jié)構(gòu)體,它在sockaddr 的基礎(chǔ)上增加了 IPv4 地址和端口號字段。sockaddr_in 的結(jié)構(gòu)體定義如下:

struct sockaddr_in 
{
    sa_family_t sin_family;      //地址族(AF_INET)
    uint16_t sin_port;           //16位端口號
    struct in_addr sin_addr;     //32位IPv4地址
    char sin_zero[8];           //不使用的填充字段
};
  • sockaddr_un 是 Unix 域套接字地址結(jié)構(gòu)體,它在sockaddr 的基礎(chǔ)上增加了一個路徑名字段。sockaddr_un的結(jié)構(gòu)體定義如下:
struct sockaddr_un 
{
    sa_family_t sun_family;     //地址族(AF_UNIX)
    char sun_path[108];         //文件路徑名
};

在實際的 socket 編程中,通常需要根據(jù)具體的網(wǎng)絡(luò)協(xié)議和地址族來選擇使用合適的套接字地址結(jié)構(gòu)體。例如,如果要使用 IPv4 協(xié)議進行通信,就需要使用 sockaddr_in 來表示 IPv4 地址和端口號。如果要實現(xiàn) Unix 域套接字通信,就需要使用 sockaddr_un 來表示文件路徑名。而sockaddr 則可以作為通用的套接字地址結(jié)構(gòu)體,用于一些通用的場景,例如進行套接字地址轉(zhuǎn)換等。

socket常見API

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

【Linux】揭開套接字編程的神秘面紗(上)

Linux 系統(tǒng)中,域間套接字、原始套接字和網(wǎng)絡(luò)套接字都是通過 socket API 來創(chuàng)建和使用的。

  • 原始套接字:在 Linux 系統(tǒng)中,原始套接字通常需要 root 權(quán)限才能創(chuàng)建和使用。創(chuàng)建原始套接字時,應(yīng)用程序需要調(diào)用 socket() 函數(shù),指定地址族參數(shù)為 AF_PACKET,類型參數(shù)為 SOCK_RAW,然后再調(diào)用 bind() 函數(shù)將原始套接字綁定到指定的網(wǎng)卡上。通過原始套接字,應(yīng)用程序可以訪問傳輸層以下的網(wǎng)絡(luò)層協(xié)議,例如IP、ICMP、ARP等。
  • 域間套接字:在 Linux 系統(tǒng)中,域間套接字通常被實現(xiàn)為文件系統(tǒng)中的一個文件。創(chuàng)建域間套接字時,應(yīng)用程序需要調(diào)用 socket() 函數(shù),指定地址族參數(shù)為 AF_UNIX,類型參數(shù)為 SOCK_STREAM 或 SOCK_DGRAM,然后再調(diào)用 bind() 函數(shù)將文件名綁定到套接字上。通過文件名,進程可以找到對應(yīng)的域間套接字,進行進程間通信。
  • 網(wǎng)絡(luò)套接字:在 Linux 系統(tǒng)中,網(wǎng)絡(luò)套接字通常使用 IPv4 或 IPv6 協(xié)議進行通信。創(chuàng)建網(wǎng)絡(luò)套接字時,應(yīng)用程序需要調(diào)用 socket() 函數(shù),指定地址族參數(shù)為 AF_INET 或 AF_INET6,類型參數(shù)為 SOCK_STREAM 或 SOCK_DGRAM,然后再調(diào)用 bind() 函數(shù)將套接字綁定到指定的 IP 地址和端口號上。通過網(wǎng)絡(luò)套接字,應(yīng)用程序可以實現(xiàn)基于 TCP 或 UDP 協(xié)議的網(wǎng)絡(luò)通信。

??UDP服務(wù)器??

echo服務(wù)器

echo 服務(wù)器想要實現(xiàn)的功能是將客戶端發(fā)送過來的數(shù)據(jù),回顯給客戶端。

recvfrom 函數(shù)的最后兩個參數(shù)的含義:

【Linux】揭開套接字編程的神秘面紗(上)

為什么服務(wù)端進行綁定時,建議綁定全零的 IP 地址呢?

【Linux】揭開套接字編程的神秘面紗(上)

UdpServer.hpp

#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include "Log.hpp"
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SIZE 1024

class UdpServer
{
public:
    UdpServer(uint16_t port, std::string ip = "")
        : _port(port)
        , _ip(ip)
    {}

    void InitServer()
    {
        // 1. 創(chuàng)建套接字
        // 前兩個參數(shù)已經(jīng)能確定是是UDP的網(wǎng)絡(luò)通信了,第三個參數(shù)設(shè)置為0即可
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        if(_socket < 0) // 創(chuàng)建套接字失敗
        {
            logMessage(FATAL, "errno:%d, strerror", errno, strerror(errno));
            exit(2);
        }

        // 2. 綁定端口號(將用戶設(shè)置的ip和端口號與當(dāng)前的進程強綁定)
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        // 服務(wù)器的IP地址和端口號是要發(fā)給對方主機的,首先要發(fā)送到網(wǎng)絡(luò)
        local.sin_port = htons(_port);
        // 點分十進制的IP地址: "192.168.110.132"
        // 每一個區(qū)域取值范圍是[0-255]: 1字節(jié) -> 4個區(qū)域
        // 理論上,表示一個IP地址,其實4字節(jié)就夠了
        // 需要將點分十進制字符串風(fēng)格的IP地址轉(zhuǎn)成4字節(jié)的二進制序列
        // 4字節(jié)的二進制序列,還需要從主機序列轉(zhuǎn)為網(wǎng)絡(luò)序列
        // 以上過程可以通過inet_addr函數(shù)來完成
        // sin_addr.s_addr是4字節(jié)的二進制網(wǎng)絡(luò)序列
        // INADDR_ANY表示發(fā)給這臺主機上的指定端口的數(shù)據(jù)都要交給UdpServer
        // 如果綁定指定IP,就只能接收發(fā)給該IP的數(shù)據(jù)
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if(bind(_socket, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "errno:%d, strerror", errno, strerror(errno));
            exit(3);
        }
        logMessage(NORMAL, "Init UdpServer Success!");
    }

    void StartServer()
    {
        // 網(wǎng)絡(luò)服務(wù)器是常駐進程,永遠(yuǎn)不會退出,除非掛掉了
        char buffer[SIZE];
        while(true)
        {
            struct sockaddr_in peer;   // 輸出型參數(shù)
            bzero(&peer, sizeof(peer)); // 將比特位全部置為0
            socklen_t len = sizeof(peer);  // 輸入輸出型參數(shù)
            ssize_t s = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                buffer[s] = '\0';
                uint16_t clientPort = ntohs(peer.sin_port);
                std::string clientIP = inet_ntoa(peer.sin_addr);
                printf("clientIP:%s clientPort:%d# %s\n", clientIP.c_str(), clientPort, buffer);
            }
            // 寫回數(shù)據(jù)
            sendto(_socket, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);
        }
    }

    ~UdpServer()
    {
        if(_socket >= 0) close(_socket); // 關(guān)閉文件描述符
    }

private:
    uint16_t _port;
    std::string _ip;
    int _socket;
};

#endif

功能說明:

  • UdpServer 的構(gòu)造函數(shù)是指定服務(wù)器的 IP 地址和端口號。
  • InitServer 接口的功能是創(chuàng)建套接字和綁定端口號。
  • StartServer 接口的功能是接收客戶端的數(shù)據(jù)并將數(shù)據(jù)寫回給客戶端。

UdpServer.cc

#include "UdpServer.hpp"
#include <memory>

static void Usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << "Port" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    uint16_t port = atoi(argv[1]);
    std::unique_ptr<UdpServer> ptr(new UdpServer(port));
    ptr->InitServer();
    ptr->StartServer();

    return 0;
}

UdpClient.cc

為什么客戶端不能顯式綁定端口號呢?

客戶端通常不需要顯式綁定端口號,因為客戶端只需要連接到服務(wù)端的指定端口號即可。當(dāng)客戶端向服務(wù)端發(fā)起連接請求時,操作系統(tǒng)會自動為客戶端分配一個隨機的空閑端口號,并在連接請求中包含該端口號信息,以便服務(wù)端能夠返回數(shù)據(jù)給正確的客戶端端口。

這種自動分配端口號的機制稱為“臨時端口號”或“短暫端口號”,它的使用使得客戶端和服務(wù)端的通信更加簡單和可靠。同時,如果客戶端需要綁定特定的端口號,則必須要保證該端口號未被其他應(yīng)用程序占用,否則會導(dǎo)致連接失敗。因此,客戶端通常不需要顯式綁定端口號。

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>

static void Usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " ServerIP ServerPort" << std::endl;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    // 1. 創(chuàng)建套接字
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        std::cerr << "Socket Error!" << std::endl;
        exit(2);
    }

    char buffer[1024];
    std::string message;
    struct sockaddr_in server;
    memset(&server, sizeof(server), 0);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(argv[1]);
    server.sin_port = htons(atoi(argv[2]));

    // 客戶端一般不需要顯示地綁定端口號,如果客戶端綁定了一個固定的
    // IP和端口號,那么其他客戶端也綁定了這個端口號,這時候客戶端就會
    // 綁定失敗。所以客戶端一般不需要顯式地綁定端口號,而是讓操作系統(tǒng)
    // 隨機選擇一個端口號進行綁定。什么時候進行綁定,當(dāng)客戶端首次發(fā)送
    // 數(shù)據(jù)給服務(wù)器時,操作系統(tǒng)會自動進行客戶端的端口綁定

    while(true)
    {
        std::cout << "Please Enter Your Message: ";
        std::getline(std::cin, message);
        if(message == "quit") break;
        sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        // 接收數(shù)據(jù):當(dāng)前的客戶端有可能是別的主機的服務(wù)端
        ssize_t s = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &len);
        if(s > 0)
        {
            buffer[s] = '\0';
            std::cout << "server# " << buffer << std::endl;
        }
    }

    close(sock);

    return 0;
}

功能測試以及注意事項

netstat 是一個用于顯示和分析 Linux 系統(tǒng)網(wǎng)絡(luò)連接狀態(tài)的命令行工具。下面是常用的 netstat 參數(shù)的含義:

  • -a 顯示所有的網(wǎng)絡(luò)連接狀態(tài),包括 TCP、UDP 和 UNIX域套接字。
  • -t 只顯示 TCP 連接狀態(tài)。
  • -u 只顯示 UDP 連接狀態(tài)。
  • -n 不進行主機名和端口號的解析,使用數(shù)字形式來顯示地址和端口號。
  • -p 顯示與連接相關(guān)聯(lián)的程序名稱和進程ID。
  • -e 顯示與連接相關(guān)聯(lián)的擴展信息,如 TCP 的 SACK 和 Windows 擴展選項。
  • -c 持續(xù)輸出網(wǎng)絡(luò)連接狀態(tài),每隔一秒鐘更新一次。
  • -l 僅顯示監(jiān)聽狀態(tài)的連接。

使用 netstat 命令時,可以根據(jù)具體需要組合使用這些參數(shù),以便查看系統(tǒng)的網(wǎng)絡(luò)連接狀態(tài)。例如,使用 netstat -tunap 可以同時顯示所有 TCP 和 UDP 連接狀態(tài),并顯示與每個連接相關(guān)聯(lián)的程序名稱和進程 ID。

【Linux】揭開套接字編程的神秘面紗(上)

什么是本地環(huán)回?

本地環(huán)回(loopback)是一種計算機網(wǎng)絡(luò)通信的機制,它允許計算機通過一個虛擬的網(wǎng)絡(luò)接口與自己通信。在 TCP / IP 協(xié)議中,本地環(huán)回地址被定義為 127.0.0.1,也稱為回環(huán)地址。

當(dāng)計算機通過回環(huán)地址發(fā)送數(shù)據(jù)包時,操作系統(tǒng)會將這些數(shù)據(jù)包送回到發(fā)送者本身,而不是發(fā)送到網(wǎng)絡(luò)上。這種機制使得計算機可以自我測試和調(diào)試,同時也可以用于本地服務(wù)的訪問和通信。

在網(wǎng)絡(luò)編程中,本地環(huán)回地址可以用來測試和調(diào)試客戶端和服務(wù)器程序。例如,可以將客戶端程序連接到回環(huán)地址的某個端口上,以模擬連接到遠(yuǎn)程服務(wù)器的情況。同樣地,服務(wù)器程序也可以監(jiān)聽回環(huán)地址上的某個端口,以模擬接收來自遠(yuǎn)程客戶端的請求。

總之,本地環(huán)回是一種非常有用的網(wǎng)絡(luò)通信機制,它使得計算機可以在不涉及真實網(wǎng)絡(luò)的情況下進行自我測試和調(diào)試,以及本地服務(wù)的訪問和通信。

Linux系統(tǒng)中常用的文件傳輸工具:rz(收) 和 sz(發(fā)),通過 sz 指令就可以將 Linux 下的程序發(fā)到 Windows 系統(tǒng)上了,rz 指令可以將 Windows 上的文件傳給 Linux系統(tǒng)。

日志功能

#pragma once

#include <cstdio>
#include <cstdarg>
#include <string>
#include <iostream>
#include <ctime>

// 日志等級
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4

#define LOGFILE "./ThreadPool.log"

const char* levelMap[] = 
{
    "DEBUG",
    "NORMAL",
    "WARNING",
    "ERROR",
    "FATAL"
};

void logMessage(int level, const char* format, ...)
{
    // 只有定義了DEBUG_SHOW,才會打印debug信息
    // 利用命令行來定義即可,如-D DEBUG_SHOW
#ifndef DEBUG_SHOW
    if(level == DEBUG) return;
#endif

    char stdBuffer[1024];   // 標(biāo)準(zhǔn)部分
    time_t timestamp = time(nullptr);
    // struct tm *localtime = localtime(&timestamp);
    snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", levelMap[level], timestamp);

    char logBuffer[1024];   // 自定義部分
    va_list args;   // va_list就是char*的別名
    va_start(args, format); // va_start是宏函數(shù),讓args指向參數(shù)列表的第一個位置
    // vprintf(format, args); // 以format形式向顯示器上打印參數(shù)列表
    vsnprintf(logBuffer, sizeof logBuffer, format, args);

    va_end(args);   // va_end將args弄成nullptr

    // FILE* fp = fopen(LOGFILE, "a");
    printf("%s%s\n", stdBuffer, logBuffer);
    // fprintf(fp, "%s%s\n", stdBuffer, logBuffer);    // 向文件中寫入日志信息
    // fclose(fp);
}

Makefile

.PHONY:all
all:UdpClient UdpServer

UdpClient:UdpClient.cc
	g++ -o $@ $^ -std=c++11
UdpServer:UdpServer.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f UdpClient UdpServer

指令服務(wù)器

指令服務(wù)器要實現(xiàn)的功能就是服務(wù)端將客戶端發(fā)送過來的數(shù)據(jù)當(dāng)做 Linux 指令,然后在服務(wù)端執(zhí)行該指令并將指令的執(zhí)行結(jié)果返回給客戶端。

要實現(xiàn)指令服務(wù)器,我們需要借助 popen 函數(shù)。

popen 函數(shù)是 C 語言標(biāo)準(zhǔn)庫中的一個函數(shù),它可以創(chuàng)建一個進程并與之建立一個管道。該管道可以實現(xiàn)進程之間的通信,父進程可以向子進程發(fā)送數(shù)據(jù),并讀取子進程的輸出。popen 函數(shù)原型如下:

FILE *popen(const char *command, const char *type);

其中,command 參數(shù)是要執(zhí)行的命令或程序,type 參數(shù)是打開的模式,可以是 “r”(只讀模式)或 “w”(只寫模式)。函數(shù)返回一個文件指針,可以像讀寫文件一樣操作管道。

例如,以下代碼創(chuàng)建一個進程并向其發(fā)送數(shù)據(jù),并從管道中讀取子進程的輸出:

#include <stdio.h>

int main() 
{
    FILE *fp;
    char buffer[1024];
    fp = popen("ls -l", "r");
    if (fp == NULL) 
    {
        printf("Error: Failed to execute command.\n");
        return -1;
    }
    while (fgets(buffer, sizeof(buffer), fp) != NULL) 
    {
        printf("%s", buffer);
    }
    pclose(fp);
    return 0;
}

該程序打開一個名為 ls -l 的進程,并將其標(biāo)準(zhǔn)輸出讀取到一個緩沖區(qū)中,最后輸出到屏幕上。需要注意的是,popen 函數(shù)可能會存在一些安全隱患,因為在打開進程時會執(zhí)行一個命令字符串。因此,在使用該函數(shù)時需要謹(jǐn)慎,避免輸入不受信任的命令字符串。

注:只需要改寫 echo 服務(wù)器的 StartServer 接口即可。strcaststr 是查找子串的函數(shù),它的查找是忽略大小寫的。

void StartServer()
{
    // 網(wǎng)絡(luò)服務(wù)器是常駐進程,永遠(yuǎn)不會退出,除非掛掉了
    char buffer[SIZE];
    while(true)
    {
        struct sockaddr_in peer;   // 輸出型參數(shù)
        bzero(&peer, sizeof(peer)); // 將比特位全部置為0
        socklen_t len = sizeof(peer);  // 輸入輸出型參數(shù)

        char ret[256];
        std::string cmdRet; // 命令的執(zhí)行結(jié)果
        ssize_t s = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
        if(s > 0)
        {
            buffer[s] = '\0';
            // 過濾危險指令
            if(strcasestr(buffer, "rm") != nullptr || strcasestr(buffer, "rmdir") != nullptr)
            {
                std::string errMessage = "哎呦,你干嘛!";
                std::cout << errMessage << buffer << std::endl;
                sendto(_socket, errMessage.c_str(), errMessage.size(), 0, (struct sockaddr*)&peer, len);
                continue; 
            }

            // buffer中的內(nèi)容看做指令
            FILE* fp = popen(buffer, "r");
            if(fp == nullptr) // 打開文件失敗
            {
                logMessage(ERROR, "errno:%d strerror:%s\n", errno, strerror(errno));
                continue;
            }
            // 從文件中讀取指令執(zhí)行結(jié)果
            while(fgets(ret, sizeof(ret), fp) != nullptr)
            {
                cmdRet += ret;
            }
            fclose(fp);

            uint16_t clientPort = ntohs(peer.sin_port);
            std::string clientIP = inet_ntoa(peer.sin_addr);
            printf("clientIP:%s clientPort:%d# %s\n", clientIP.c_str(), clientPort, buffer);
        }
        // 將指令的執(zhí)行結(jié)果發(fā)送給客戶端
        sendto(_socket, cmdRet.c_str(), cmdRet.size(), 0, (struct sockaddr*)&peer, len);
    }
}

【Linux】揭開套接字編程的神秘面紗(上)

簡易的公共聊天室

簡易版的公共聊天室的主要功能是將一個用戶發(fā)的信息同步到其他用戶中去,那么這就以為這一個用戶既要發(fā)信息,也要接收其他用戶發(fā)的信息,而這兩個過程可以通過寫線程和讀線程來模擬。而服務(wù)端則需要將已經(jīng)向服務(wù)端發(fā)過消息的用戶記錄下來,以便后續(xù)將一個用戶發(fā)的消息同步給其他用戶。

UdpServer.hpp

class UdpServer
{
public:
    void StartServer()
    {
        // 網(wǎng)絡(luò)服務(wù)器是常駐進程,永遠(yuǎn)不會退出,除非掛掉了
        char buffer[SIZE];
        while(true)
        {
            struct sockaddr_in peer;   // 輸出型參數(shù)
            bzero(&peer, sizeof(peer)); // 將比特位全部置為0
            socklen_t len = sizeof(peer);  // 輸入輸出型參數(shù)

            char ret[256];
            char key[64];
            ssize_t s = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                buffer[s] = '\0';
                uint16_t clientPort = ntohs(peer.sin_port);
                std::string clientIP = inet_ntoa(peer.sin_addr);
                // key是用戶的IP地址加上端口號
                snprintf(key, sizeof(key), "IP:%s Port:%d", clientIP.c_str(), clientPort);
                logMessage(NORMAL, "key: %s", key);
                // 查找用戶是否注冊過
                auto it = _users.find(key);
                // 需要將第一次發(fā)消息向服務(wù)器的用戶保存起來
                if(it == _users.end())
                {
                    logMessage(NORMAL, "Add A New User: %s", key);
                    _users[key] = peer;
                }
            }
            // 給所有的用戶發(fā)送該消息
            for(auto& it : _users)
            {
                std::string sendMessage = key;
                sendMessage += "# ";
                sendMessage += buffer;
                logMessage(NORMAL, "Push A Message To All Users");
                sendto(_socket, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr*)&(it.second), sizeof(it.second));
            }
        }
    }
private:
    std::unordered_map<std::string, struct sockaddr_in> _users;
};

Thread.hpp

#pragma once

#include <string>
#include <pthread.h>
#include <cstdio>

typedef void*(*func_t)(void*);

class ThreadData
{
public:
    void *_args;        // 線程執(zhí)行例程的參數(shù)
    std::string _name;  // 線程名
    pthread_t _tid;     // 線程ID
};

class Thread
{
public:
    Thread(int num, func_t callBack, void* args)
        : _func(callBack)
    {
        char nameBuffer[64];
        snprintf(nameBuffer, sizeof nameBuffer, "Thread %d", num);
        _data._args = args;
        _data._name = nameBuffer;
    }

    // 創(chuàng)建線程
    void Create()
    {
        pthread_create(&_data._tid, nullptr, _func, (void*)&_data);
    }

    // 等待線程
    void Join()
    {
        pthread_join(_data._tid, nullptr);
    }

    // 返回線程的名字
    std::string Name()
    {
        return _data._name;
    }

    ~Thread()
    {}

private:
    func_t _func;       // 線程的執(zhí)行例程
    ThreadData _data;   // 線程的屬性
};

注:Thread.hpp 是對線程進行了封裝,詳細(xì)介紹可以參考這篇博客:線程池的實現(xiàn)。

UdpClient.cc

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <memory>
#include "Thread.hpp"

uint16_t ServerPort = 0;
std::string ServerIP;

static void Usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " ServerIP ServerPort" << std::endl;
}

// 寫線程
static void* UdpSend(void* args)
{
    ThreadData* td = (ThreadData*)args;
    int sock = *(int*)td->_args;

    std::string message;
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(ServerIP.c_str());
    server.sin_port = htons(ServerPort);

    while(true)
    {
        std::cerr << "Please Enter Your Message: ";
        std::getline(std::cin, message);
        if(message == "quit") break;
        sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
    }

    return nullptr;
}

// 讀線程
static void* UdpRecv(void* args)
{
    ThreadData* td = (ThreadData*)args;
    int sock = *(int*)td->_args;
    std::string threadName = td->_name;

    char buffer[1024];
    while(true)
    {
        memset(buffer, sizeof buffer, 0);
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t s = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &len);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout << buffer << std::endl;
        }
    }

    return nullptr;
}


int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    // 1. 創(chuàng)建套接字
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
    	// 細(xì)節(jié):使用cerr標(biāo)準(zhǔn)錯誤,這樣可以為后面的重定向做準(zhǔn)備
        std::cerr << "Socket Error!" << std::endl;
        exit(2);
    }

    ServerIP = argv[1];
    ServerPort = atoi(argv[2]);

    // 讀線程和寫線程都不會修改端口號,所以不需要進行加鎖保護
    // 讀寫進程使用的是同一個端口號sock,sock是文件描述符
    // 說明Udp是全雙工的(可以同時進行讀寫且不受干擾)
    std::unique_ptr<Thread> Sender(new Thread(1, UdpSend, (void*)&sock));
    std::unique_ptr<Thread> Recver(new Thread(2, UdpRecv, (void*)&sock));
    // 創(chuàng)建寫線程和讀線程
    Sender->Create();
    Recver->Create();
    // 等待寫線程和讀線程
    Sender->Join();
    Recver->Join();

    close(sock);

    return 0;
}

【Linux】揭開套接字編程的神秘面紗(上)
注:mkfifo 創(chuàng)建命名管道,并將用戶接收到的信息重定向到管道文件中,然后用另一個會話從管道文件中讀取該用戶收到的信息。

什么是全雙工通信方式?為什么 Udp 協(xié)議是全雙工的?

全雙工通信是指在通信的雙方可以同時進行發(fā)送和接收數(shù)據(jù)的通信方式。這種通信方式可以實現(xiàn)同時雙方交換信息,從而提高通信效率。

UDP協(xié)議是一個無連接的、面向數(shù)據(jù)報的協(xié)議,同時它也是一種全雙工的通信方式,即在同一時刻,它允許數(shù)據(jù)的發(fā)送和接收。

Windows 版本的客戶端

#include <WinSock2.h>
#include <iostream>
#include <string>
#include <thread>
#include <memory>
#include <cstring>

using namespace std;
#pragma comment(lib,"ws2_32.lib") //固定用法

uint16_t serverport = 8080;
// serverip需要替換成自己的云服務(wù)器的公網(wǎng)IP
std::string serverip = "xxx.xxx.xxx.xxx"; 

void Sender(SOCKET clientSocket)
{
	sockaddr_in dstAddr;
	dstAddr.sin_family = AF_INET;
	dstAddr.sin_port = htons(serverport);
	dstAddr.sin_addr.S_un.S_addr = inet_addr(serverip.c_str());
	while (true)
	{
		std::string message;
		std::cerr << "請輸入# ";
		std::getline(std::cin, message);
		if (message == "quit") break;
		sendto(clientSocket, message.c_str(), (int)message.size(), 0, (sockaddr*)&dstAddr, sizeof(dstAddr));
		message.clear();
	}
}

void Recver(SOCKET clientSocket)
{
	char buffer[1024];
	while (true)
	{
		memset(buffer, sizeof buffer, 0);
		struct sockaddr_in temp;
		int len = sizeof(temp);
		int s = recvfrom(clientSocket, buffer, sizeof buffer, 0, (sockaddr*)&temp, &len);
		if (s > 0)
		{
			buffer[s] = '\0';
			cout << "server echo# " << buffer << endl;
		}
	}
}

int main()
{
	// windows 獨有的
	WSADATA WSAData;
	WORD sockVersion = MAKEWORD(2, 2);
	if (WSAStartup(sockVersion, &WSAData) != 0)
		return 0;

	SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (INVALID_SOCKET == clientSocket)
	{
		cout << "socket error!";
		return 0;
	}

	unique_ptr<thread> send(new thread(Sender, clientSocket));
	unique_ptr<thread> recv(new thread(Recver, clientSocket));
	send->join();
	recv->join();

	// windows 獨有
	closesocket(clientSocket);
	WSACleanup();

	return 0;
}

??總結(jié)??

本篇博客主要講解了什么是源IP地址和目的IP地址、什么是端口號、PID和端口號的區(qū)別、什么是套接字、簡單認(rèn)識UDP和TCP協(xié)議、什么是網(wǎng)絡(luò)字節(jié)序、套接字的分類、數(shù)據(jù)結(jié)構(gòu)和常見API以及使用UDP協(xié)議來進行編寫服務(wù)端和客戶端等。以上就是本篇博客的全部內(nèi)容了,如果大家覺得有收獲的話,可以點個三連支持一下!謝謝大家啦!??????文章來源地址http://www.zghlxwxcb.cn/news/detail-408882.html

到了這里,關(guān)于【Linux】揭開套接字編程的神秘面紗(上)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【Linux】TCP套接字編程

    【Linux】TCP套接字編程

    目錄 前言 UDP服務(wù)器的完善 線程的封裝 結(jié)構(gòu)定義 接口實現(xiàn) 環(huán)形隊列 結(jié)構(gòu)定義 接口實現(xiàn) 加鎖 信號量的申請與釋放 入隊與出隊 整體組裝? 初始化與析構(gòu) 信息接收線程 消息發(fā)送線程 TCP套接字 創(chuàng)建套接字 listen accept 收發(fā)操作 客戶端的編寫 進一步完善 多進程 多線程 總結(jié) 上篇

    2024年02月05日
    瀏覽(21)
  • Linux網(wǎng)絡(luò)編程(二-套接字)

    Linux網(wǎng)絡(luò)編程(二-套接字)

    目錄 一、背景知識 1.1 端口號 1.2 網(wǎng)絡(luò)字節(jié)序 1.3 地址轉(zhuǎn)換函數(shù)? 二、Socket簡介 三、套接字相關(guān)的函數(shù)? 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3.5 accept()? 3.6 read()/recv()/recvfrom() 3.7 send()/sendto() ?3.8 close() ?四、UPD客服/服務(wù)端實驗? 1.1 端口號 端口號是訪問服務(wù)器的標(biāo)識 ,就好像

    2024年01月22日
    瀏覽(166)
  • 【Linux】網(wǎng)絡(luò)---->套接字編程(TCP)

    【Linux】網(wǎng)絡(luò)---->套接字編程(TCP)

    TCP的編程流程:大致可以分為五個過程,分別是準(zhǔn)備過程、連接建立過程、獲取新連接過程、消息收發(fā)過程和斷開過程。 1.準(zhǔn)備過程:服務(wù)端和客戶端需要創(chuàng)建各自的套接字,除此之外服務(wù)端還需要綁定自己的地址信息和進行監(jiān)聽。注意:服務(wù)端調(diào)用listen函數(shù)后,處理監(jiān)聽狀

    2024年02月04日
    瀏覽(101)
  • 【Linux】網(wǎng)絡(luò)編程套接字一

    【Linux】網(wǎng)絡(luò)編程套接字一

    上篇博客由唐僧的例子我們知道: 在IP數(shù)據(jù)包頭部中,有兩個IP地址,分別叫做源IP地址,和目的IP地址。 思考一下: 不考慮中間的一系列步驟,兩臺主機我們光有IP地址就可以完成通信了嘛? 想象一下發(fā)qq消息的例子,有了IP地址能夠把消息發(fā)送到對方的機器上。 但是我們把

    2024年03月26日
    瀏覽(1082)
  • 【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    【Linux網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程套接字二

    喜歡的點贊,收藏,關(guān)注一下把! TCP和UDP在編程接口上是非常像的,前面我們說過TCP是面向連接的,UDP我們上篇博客也寫過了,我們發(fā)現(xiàn)UDP服務(wù)端客戶端寫好啟動直接就發(fā)消息了沒有建立連接。TCP是建立連接的,注定在寫的時候肯定有寫不一樣的地方。具體怎么不一樣,我們

    2024年04月15日
    瀏覽(101)
  • Linux網(wǎng)絡(luò)編程——tcp套接字

    Linux網(wǎng)絡(luò)編程——tcp套接字

    本章Gitee倉庫:tcp套接字 客戶端: 客戶端: 關(guān)于構(gòu)造和初始化,可以直接在構(gòu)造的時候,將服務(wù)器初始化,那為什么還要寫到 init 初始化函數(shù)里面呢? 構(gòu)造盡量簡單一點,不要做一些“有風(fēng)險”的操作。 tcp 是面向連接的,通信之前要建立連接,服務(wù)器處于等待連接到來的

    2024年02月20日
    瀏覽(96)
  • linux【網(wǎng)絡(luò)編程】之網(wǎng)絡(luò)套接字預(yù)備

    linux【網(wǎng)絡(luò)編程】之網(wǎng)絡(luò)套接字預(yù)備

    在【網(wǎng)絡(luò)基礎(chǔ)】中我們提到了IP地址,接下來了解一下網(wǎng)絡(luò)通信中其他方面的知識 端口號是一個2字節(jié)16位的整數(shù); 端口號用來標(biāo)識一個進程, 告訴操作系統(tǒng), 當(dāng)前的這個數(shù)據(jù)要交給哪一個進程來處理; 一個端口號只能被一個進程占用 通信原理 (公網(wǎng))IP唯一標(biāo)識一臺主機,這樣兩臺

    2024年02月05日
    瀏覽(298)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(TCP)

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(TCP)

    目錄 地址轉(zhuǎn)換函數(shù) 字符串IP轉(zhuǎn)整數(shù)IP 整數(shù)IP轉(zhuǎn)字符串IP 關(guān)于inet_ntoa 簡單的單執(zhí)行流TCP網(wǎng)絡(luò)程序 TCP socket API 詳解及封裝TCP socket? 服務(wù)端創(chuàng)建套接字? 服務(wù)端綁定? 服務(wù)端監(jiān)聽? 服務(wù)端獲取連接? 服務(wù)端處理請求 客戶端創(chuàng)建套接字 客戶端連接服務(wù)器 客戶端發(fā)起請求 服務(wù)器測試

    2024年03月21日
    瀏覽(113)
  • 【Linux】網(wǎng)絡(luò)基礎(chǔ)+UDP網(wǎng)絡(luò)套接字編程

    【Linux】網(wǎng)絡(luò)基礎(chǔ)+UDP網(wǎng)絡(luò)套接字編程

    只做自己喜歡做的事情,不被社會和時代裹挾著前進,是一件很奢侈的事。 1. 首先計算機是人類設(shè)計出來提高生產(chǎn)力的工具,而人類的文明綿延至今一定離不開人類之間互相的協(xié)作,既然人類需要協(xié)作以完成更為復(fù)雜的工作和難題,所以計算機作為人類的工具自然也一定需要

    2024年02月08日
    瀏覽(89)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(預(yù)備知識+UDP)

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字(預(yù)備知識+UDP)

    目錄 預(yù)備知識 1. 理解源IP地址和目的IP地址 2. 理解源MAC地址和目的MAC地址 3. 認(rèn)識端口號 ?4. 理解源端口號和目的端口號 5. 端口號(port) vs 進程pid 6. 認(rèn)識TCP協(xié)議和認(rèn)識UDP協(xié)議 7. 網(wǎng)絡(luò)字節(jié)序 socket編程接口? 1. socket 常見API 2. sockaddr結(jié)構(gòu)? 簡單的UDP網(wǎng)絡(luò)程序? 1. 服務(wù)端創(chuàng)建udp

    2024年02月19日
    瀏覽(93)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包