主頁 > 軟體設計 > 【Linux】網路編程套接字(一)—— UDP編程

【Linux】網路編程套接字(一)—— UDP編程

2020-12-27 10:09:56 軟體設計

文章目錄

  • 1. 預備知識
    • 1.1 源IP地址和目的IP地址
    • 1.2 認識埠號
    • 1.3 理解 "埠號" 和 "行程ID"
    • 1.4 源埠號和目的埠號
    • 1.5 認識UDP協議
    • 1.6 認識TCP協議
    • 1.7 網路位元組序
  • 2. socket編程介面
    • 2.1 socket 常見API
      • 2.1.1 創建
      • 2.1.2 系結
      • 2.1.3 關閉
    • 2.2 sockaddr結構
      • 2.2.1 sockaddr 結構
      • 2.2.2 sockaddr_in 結構
      • 2.2.3 in_addr 結構
      • 2.2.4 小結
    • 2.3 地址轉換函式
    • 2.4 例子
  • 3. 簡單的UDP網路程式
    • 3.1 UDP的socket API詳解
      • 3.1.1 發送資料
      • 3.1.2 接收資料
    • 3.2 客戶端程式
    • 3.3 服務端程式
    • 3.4 封裝的UDP
      • 3.4.1 udp_socket.hpp
      • 3.4.2 udp_server.cpp
      • 3.4.3 udp_client.cpp

1. 預備知識

1.1 源IP地址和目的IP地址

源IP地址:指的就是發送資料包的那個電腦的IP地址,
目的IP地址:就是想要發送到的那個電腦的IP地址,

1.2 認識埠號

埠號(port)是傳輸層協議的內容,

  • 埠號是一個2位元組16位的整數;
  • 埠號用來標識一個行程, 告訴作業系統, 當前的這個資料要交給哪一個行程來處理;
  • IP地址 + 埠號能夠標識網路上的某一臺主機的某一個行程;
  • 一個埠號只能被一個行程占用,

1.3 理解 “埠號” 和 “行程ID”

系統編程的pid 表示唯一一個行程;此處我們的埠號也是唯一表示一個行程,
一個行程可以系結多個埠號
一個埠號不能被多個行程系結

1.4 源埠號和目的埠號

傳輸層協議(TCP和UDP)的資料段中有兩個埠號,分別叫做源埠號和目的埠號,就是在描述 “資料是誰發的,要發給誰”,

1.5 認識UDP協議

我們先對UDP(User Datagram Protocol 用戶資料報協議)有一個直觀的認識;后面再詳細討論,

  • 傳輸層協議
  • 無連接
  • 不可靠傳輸
  • 面向資料報

1.6 認識TCP協議

此處我們也是對TCP(Transmission Control Protocol 傳輸控制協議)有一個直觀的認識;后面再詳細討論,

  • 傳輸層協議
  • 有連接
  • 可靠傳輸
  • 面向位元組流

1.7 網路位元組序

記憶體中的多位元組資料相對于記憶體地址有大端和小端之分, 磁盤檔案中的多位元組資料相對于檔案中的偏移地址也有大端小端之分, 網路資料流同樣有大端小端之分,

那么如何定義網路資料流的地址呢?

  • 發送主機通常將發送緩沖區中的資料按記憶體地址從低到高的順序發出;
  • 接收主機把從網路上接到的位元組依次保存在接識訓沖區中,也是按記憶體地址從低到高的順序保存;
  • 因此,網路資料流的地址應這樣規定:先發出的資料是低地址,后發出的資料是高地址
  • TCP/IP協議規定,網路資料流應采用大端位元組序,即低地址高位元組
  • 不管這臺主機是大端機還是小端機, 都會按照這個TCP/IP規定的網路位元組序來發送/接收資料;
  • 如果當前發送主機是小端, 就需要先將資料轉成大端; 否則就忽略, 直接發送即可,
    在這里插入圖片描述

為使網路程式具有可移植性, 使同樣的C代碼在大端和小端計算機上編譯后都能正常運行, 可以呼叫以下庫函式做網路位元組序和主機位元組序的轉換,

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);  // 主機位元組序轉網路位元組序,轉化4位元組
uint16_t htons(uint16_t hostshort);  // 主機位元組序轉網路位元組序,轉化2位元組

uint32_t ntohl(uint32_t netlong);  // 網路位元組序轉主機位元組序,轉化4位元組
uint16_t ntohs(uint16_t netshort);  // 網路位元組序轉主機位元組序,轉化2位元組
  • h表示host, n表示network, l表示32位長整數, s表示16位短整數,
  • 例如htonl表示將32位的長整數從主機位元組序轉換為網路位元組序, 例如將IP地址轉換后準備發送,
  • 如果主機是小端位元組序, 這些函式將引數做相應的大小端轉換然后回傳;
  • 如果主機是大端位元組序, 這些函式不做轉換, 將引數原封不動地回傳,

2. socket編程介面

2.1 socket 常見API

2.1.1 創建

// 創建 socket 檔案描述符 (TCP/UDP, 客戶端 + 服務器)
int socket(int domain, int type, int protocol);
引數:
    domain:地址域 —> 網路層使用什么協議
        AF_INET: ipv4版本的ip協議
        AF_INET6: ipv6版本的ip協議
        AF_UNIX: 域套接字
    type:套接字的型別
        SOCK_DGRAM: 用戶資料報套接字 —> 默認協議是UDP協議
        SOCK_STREAM: 流式套接字 —> 默認的協議是TCP協議
    protocol:協議
        SOCK_DGRAM: IPPROTO_UDP(17)
        SOCK_STREAM: IPPROTO_TCP(6)
        也可以傳遞0,表示使用套接字的默認協議
回傳值:套接字描述符,本質上還是一個檔案描述符
  • socket()打開一個網路通訊埠, 如果成功的話, 就像open()一樣回傳一個檔案描述符;
  • 應用程式可以像讀寫檔案一樣用read/write在網路上收發資料;
  • 如果socket()呼叫出錯則回傳-1;
  • 對于IPv4, family引數指定為AF_INET;
  • 對于TCP協議, type引數指定為SOCK_STREAM, 表示面向流的傳輸協議;
  • protocol引數的介紹從略, 指定為0即可,

2.1.2 系結

// 系結埠號 (TCP/UDP, 服務器)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addr_len);
引數:
    sockfd:套接字描述符
    addr:地址資訊
    addr_len:傳入結構體的真實位元組數量
  • 服務器程式所監聽的網路地址和埠號通常是固定不變的, 客戶端程式得知服務器程式的地址和埠號后就可以向服務器發起連接;
  • 服務器需要呼叫bind系結一個固定的網路地址和埠號;
  • bind()成功回傳0, 失敗回傳-1;
  • bind()的作用是將引數sockfd和addr系結在一起;
  • struct sockaddr *是一個通用指標型別, addr引數實際上可以接受多種協議的sockaddr結構體, 而它們的長度各不相同, 所以需要引數addr_len指定結構體的長度,

注意系結ip的時候

  1. 可以直接系結網卡對應的ip地址;
  2. 也可以系結0.0.0.0表示當前機器當中的任意網卡的ip地址;
  3. 系結127.0.0.1表示系結本地回環地址,只訪問本地網路協議堆疊(訪問自己,測驗地址),

2.1.3 關閉

// 關閉套接字
close(int sockfd);
引數:
    sockfd:套接字描述符

2.2 sockaddr結構

socket API是一層抽象的網路編程介面, 適用于各種底層網路協議, 如IPv4、IPv6,然而, 各種網路協議的地址格式并不相同,
在這里插入圖片描述

2.2.1 sockaddr 結構

//通用的結構體:
struct sockaddr 
{
    sa_family_t  sa_family;  //地址型別, AF_xxx, 2位元組
    char  sa_data[14];  // 協議地址, 14位元組
};

2.2.2 sockaddr_in 結構

// AF_INET的結構體
struct sockaddr_in 
{
    sa_family_t  sin_family;  //地址型別, 2位元組
    in_port_t  sin_port;  // 埠號, 2位元組
    struct in_addr  sin_addr;  // IP地址, 4位元組
    unsigned char  sin_zero[8];  // 8位元組填充, 目的是與addr對齊
};

雖然socket api的介面是sockaddr, 但是我們真正在基于IPv4編程時, 使用的資料結構是sockaddr_in,
這個結構里主要有三部分資訊: 地址型別, 埠號, IP地址,

2.2.3 in_addr 結構

typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
};

in_addr用來表示一個IPv4的IP地址,其實就是一個32位的整數,

2.2.4 小結

  • IPv4和IPv6的地址格式定義在netinet/in.h中, IPv4地址用sockaddr_in結構體表示, 包括16位地址型別, 16位埠號和32位IP地址,

  • IPv4、IPv6地址型別分別定義為常數AF_INET、AF_INET6. 這樣, 只要取得某種sockaddr結構體的首地址, 不需要知道具體是哪種型別的sockaddr結構體, 就可以根據地址型別欄位確定結構體中的內容,

  • socket API可以都用struct sockaddr *型別表示, 在使用的時候需要強制轉化成sockaddr_in; 這樣的好處是程式的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各種型別的sockaddr結構體指標做為引數,

2.3 地址轉換函式

基于IPv4的socket網路編程,sockaddr_in中的成員struct in_addr sin_addr表示32位 的IP 地址,但是我們通常用點分十進制的字串表示IP 地址,以下函式可以在字串表示 和in_addr表示之間轉換,

// 點分十進制轉網路二進制
#include <netinet/in.h>
in_addr_t inet_addr(const char *cp);

inet addr()函式的作用是:將Internet主機地址從IPv4數字和點符號轉換為按網路位元組順序的二進制資料,如果輸入無效,則回傳INADDR_NONE(通常為-1),

2.4 例子

#include <stdio.h>    
#include <unistd.h>    
#include <sys/socket.h>  // 套接字   
#include <netinet/in.h>  // IP地址——> 網路位元組序     
#include <arpa/inet.h>  // 主機 ——> 網路位元組序                                                                                    
#include <iostream>    

int main()    
{    
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);    
    if(sockfd < 0)    
    {    
        perror("socket");    
        return -1;    
    }    

    //地址資訊
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(18989);
    //私網IP:172.17.0.12
    //1. 將點分十進制的IP地址轉化成無符號的4位元組的整數
    //2. 將該整數轉化成為網路位元組序(二進制)
    addr.sin_addr.s_addr = inet_addr("172.17.0.12");

    // (struct sockaddr*)&addr 將addr_in強轉為addr
    int ret = bind(sockfd, (sockaddr*)&addr, sizeof(addr)); 
    if(ret < 0)
    {
        perror("bind");
        return -1;
    }

    while(1)    
    {    
        sleep(1);    
    }    
    return 0;    
}    

3. 簡單的UDP網路程式

在這里插入圖片描述

3.1 UDP的socket API詳解

3.1.1 發送資料

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 
               const struct sockaddr *dest_addr, socklen_t addrlen);
引數:
    sockfd: 套接字描述符
    buf: 要發送的內容
    len: 發送資料的長度
    flags: 0 表示阻塞發送
    dest_addr: 要將資料發送到哪里去,對端的地址資訊
    addr_len: 地址資訊的長度

3.1.2 接收資料

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addr1en) ;
引數:
    sockfd: 套接字描述符
    buf: 接收資料準備的緩沖區
    len: 緩沖區接收的最大能力
    flags: 0 表示阻塞接收
    src_addr: 訊息發送端的地址資訊,接識訓來的訊息是從哪里來的
    addrlen: 輸入輸出型引數,回傳的就是地址資訊的真實長度

3.2 客戶端程式

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

int main()
{
    //1.創建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("socket");
        return -1;
    }

    while(1)
    {
        //2.發送資料
        char buf[1024] = { 0 };
        printf("please enter message: ");
        std::cin >> buf;
		
		// 對端(服務端)的地址資訊
        struct sockaddr_in svr_addr; 
        svr_addr.sin_family = AF_INET;
        svr_addr.sin_port = htons(19999);
        svr_addr.sin_addr.s_addr = inet_addr("172.17.0.12"); 
		socklen_t svr_len = sizeof(svr_addr);

        ssize_t send_size = sendto(sockfd, buf, strlen(buf), 0, (sockaddr*)&svr_addr, svr_len);
        if(send_size < 0)
        {
            perror("sendto");
            return -1;
        }

        //3.接收應答
        memset(buf, '\0', sizeof(buf));
        ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
        if(recv_size < 0)
        {
            perror("recvfrom");
            return -1;
        }

        //4.列印應答
        printf("server say: %s\n", buf);
    }

    close(sockfd);
    return 0;
}

3.3 服務端程式

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

int main()
{
    //1.創建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("socket");
        return -1;
    }

    //2.系結埠
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(19999);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return -1;
    }

    while(1)
    {
        //3.接收
        char buf[1024] = { 0 };
        	
		// 對端(客戶端)的地址資訊
        struct sockaddr_in cli_addr; 
        socklen_t cli_len = sizeof(cli_addr);
		
        ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (sockaddr*)&cli_addr, &cli_len);
        if(recv_size < 0)
        {
            perror("recvfrom");
            return -1;
        }

        //4.處理
        printf("client say: %s\n", buf);
        printf("please enter respond: ");
        
        //5.回復應答
        fflush(stdout); // 強制重繪標準輸出
        std::cin >> buf;

        ssize_t send_size = sendto(sockfd, buf, strlen(buf), 0, (sockaddr*)&cli_addr, cli_len);
        if(send_size < 0)
        {
            perror("sendto");
            return -1;
        }
    }

    close(sockfd);
    return 0;
}

3.4 封裝的UDP

3.4.1 udp_socket.hpp

#pragma once

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

class UdpApi
{
    public:
        UdpApi()
        {
            sockfd_ = -1;
        }

        // 創建套接字
        int CreateSocket()
        {
            sockfd_ = socket(AF_INET, SOCK_DGRAM, 17);
            if(sockfd_ < 0)
            {
                perror("socket");
                return -1;
            }
            return 0;
        }

        // 系結地址資訊
        int Bind(std::string ip, uint16_t port)
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = inet_addr(ip.c_str());

            int ret = bind(sockfd_, (sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                perror("bind");
                return -1;
            }
            return 0;
        }

        // 發送資料
        int SendData(std::string data, struct sockaddr_in* addr, socklen_t addrlen)
        {
            ssize_t send_size = sendto(sockfd_, data.c_str(), data.size(), 0, (sockaddr*)addr, addrlen);
            if(send_size < 0)
            {
                perror("sendto");
                return -1;
            }
            return send_size;
        }
        
        // 接收資料
        int RecvData(std::string* data, struct sockaddr_in* addr, socklen_t* addrlen)
        {
            char buf[1024] = { 0 };
            ssize_t recv_size = recvfrom(sockfd_, buf, sizeof(buf) - 1, 0, (sockaddr*)addr, addrlen);
            if(recv_size < 0)
            {
                perror("recvfrom");
                return -1;
            }

            data->assign(buf, strlen(buf));
            return recv_size;
        }

        void Close()
        {
            close(sockfd_);
        }

    private:
        int sockfd_;
};

3.4.2 udp_server.cpp

#include "udp_socket.hpp"

#define CHECK_ERT(p) if(p < 0){return -1;}

int main()
{
    UdpApi ua; // 服務端的ua
    CHECK_ERT(ua.CreateSocket());
    CHECK_ERT(ua.Bind("0.0.0.0", 19999));

    while(1)
    {
        struct sockaddr_in cli_addr;                                         
        socklen_t cli_len = sizeof(cli_addr);  
        
        std::string data; 
        ua.RecvData(&data, &cli_addr, &cli_len);
        printf("client say: %s\n", data.c_str());

        data.clear();

        printf("please enter respond: ");
        fflush(stdout);
        std::cin >> data;
        ua.SendData(data, &cli_addr, cli_len);
    }
    ua.Close();
    return 0;
}

3.4.3 udp_client.cpp

#include "udp_socket.hpp"
#include <stdlib.h>

#define CHECK_ERT(p) if(p < 0){return -1;}

int main(int argc, char* argv[])
{
    // ./udp_cli_package -ip [svr_ip] -port [svr_port]
    if(argc != 5)
    {
        printf("./udp_cli_package -ip [svr_ip] -port [svr_port]\n");
        return -1;
    }

    std::string svr_ip;
    uint16_t svr_port;
    for(int i = 0; i < argc; i++)
    {
        if(strcmp(argv[i], "-ip") == 0)
        {
            svr_ip = argv[i + 1];
        }
        else if(strcmp(argv[i], "-port") == 0)
        {
            svr_port = atoi(argv[i + 1]); //char* -> int
        }
    }

    UdpApi ua; // 客戶端的ua
    CHECK_ERT(ua.CreateSocket());

    while(1)
    {
        struct sockaddr_in svr_addr;
        svr_addr.sin_family = AF_INET;
        svr_addr.sin_port = htons(svr_port);
        svr_addr.sin_addr.s_addr = inet_addr(svr_ip.c_str());

        std::string data;
        printf("please input message: ");
        fflush(stdout);
        std::cin >> data;
        ua.SendData(data, &svr_addr, sizeof(svr_addr));

        data.clear();

        ua.RecvData(&data, NULL, NULL);
        printf("svr say:%s\n", data.c_str());
    }

    ua.Close();
    return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/240846.html

標籤:其他

上一篇:流水線中資料相關的理解(RAW、WAR和WAW)

下一篇:清心閣--多執行緒系列之執行緒狀態和主要轉換方法(四)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more