日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux C/C++ UDP Socket 网络通信

發(fā)布時間:2023/12/9 linux 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux C/C++ UDP Socket 网络通信 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Python微信訂餐小程序課程視頻

https://edu.csdn.net/course/detail/36074

Python實戰(zhàn)量化交易理財系統(tǒng)

https://edu.csdn.net/course/detail/35475
昨晚 Vv 讓我給她講講網(wǎng)絡(luò)編程,于是我就傻乎乎的帶她入了門…

以下內(nèi)容為講課時制作的筆記~

1. socket() 函數(shù)

1.1 頭文件

復(fù)制代碼
  • 1

#include

1.2 函數(shù)參數(shù)

示例:int socket(int domain, int type, int protocol){...}

  • domain:設(shè)置協(xié)議域(協(xié)議族)

    • AF_INET:IPV4
    • AF_INET6:IPV6
    • ??\cdots

協(xié)議族決定了 socket 的地址類型,在通信中必須采用對應(yīng)類型的地址

  • type:指定 socket 類型

    • SOCKET_STREAM:流式 socket,針對于面向連接的 TCP 服務(wù)應(yīng)用
    • SOCKET_DGRAM:數(shù)據(jù)報式 socket,針對于無連接的 UDP 服務(wù)應(yīng)用
    • ??\cdots
  • protocal:指定協(xié)議

    • 000:自動選擇第二個參數(shù)類型對應(yīng)的傳輸協(xié)議
    • IPPROTO_TCP:TCP傳輸協(xié)議
    • IPPROTO_UDP:UDP傳輸協(xié)議
    • ??\cdots

type 和 protocal 不能隨意組合,如 SOCKET_STREAM 不能和 IPPROTO_UDP 組合

1.3 返回值

示例:int sock_fd = socket(AF_INET, SOCKET_DGRAM, 0);

  • sock_fd = -1:套接字創(chuàng)建失敗
  • sock_fd = x(x >= 0):套接字創(chuàng)建成功,返回套接字的文件描述符(索引)
  • 套接字描述符是一個整數(shù)類型的值。每個進程的進程空間里都有一個套接字描述符表,該表中存放著套接字描述符和套接字數(shù)據(jù)結(jié)構(gòu)的對應(yīng)關(guān)系。該表中有一個字段存放新創(chuàng)建的套接字的描述符,另一個字段存放套接字數(shù)據(jù)結(jié)構(gòu)的地址,因此根據(jù)套接字描述符就可以找到其對應(yīng)的套接字數(shù)據(jù)結(jié)構(gòu)。每個進程在自己的進程空間里都有一個套接字描述符表但是套接字數(shù)據(jù)結(jié)構(gòu)都是在操作系統(tǒng)的內(nèi)核緩沖里。

    1.4 Socket是什么?

    socket是對TCP/IP協(xié)議簇的封裝,它的出現(xiàn)只是使得程序員更方便地使用TCP/IP協(xié)議棧而已。socket本身并不是協(xié)議,它是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,是一組調(diào)用接口(TCP/IP網(wǎng)絡(luò)的API函數(shù))

    2. bind()函數(shù)

    2.1 sockaddr

    復(fù)制代碼
    • 1
    • 2
    • 3
    • 4
    • 5

    #include struct sockaddr{ sa\_family\_t sin_family; // 協(xié)議族 char sa_data[14]; // 14 個字節(jié),包含套接字中的目標地址和目標端口信息 };

    2.2 sockaddr_in

    復(fù)制代碼
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    `#include // 或 #include
    struct in_addr{
    In_addr_t s_addr; // 32位 IPv4 地址
    };

    struct sockaddr_in{
    sa_family_t sin_family; // 協(xié)議族
    uint16_t sin_port; // 16位 TCP/UDP 端口號 (端口號最大是 65535 = 2^16 - 1)
    struct in_addr; // 32位 IP 地址
    char sin_zero[8]; // 不使用 (為了讓sockaddr與sockaddr_in兩個數(shù)據(jù)結(jié)構(gòu)保持大小相同而保留的空字節(jié))
    };`

    • sin_port 和 sin_addr 都必須是網(wǎng)絡(luò)字節(jié)序(NBO),一般可視化的數(shù)字都是主機字節(jié)序(HBO)。
    • sockaddr_in 和 sockaddr 是并列的結(jié)構(gòu),指向 sockaddr_in 的結(jié)構(gòu)體的指針也可以指向 sockadd 的結(jié)構(gòu)體,并代替它。

    2.3 函數(shù)參數(shù)

    示例:int bind(sock_fd, const struct sockaddr* address, socklen_t address_len);

    • sock_fd:套接字描述符
    • address:sockaddr結(jié)構(gòu)指針,該結(jié)構(gòu)中包含了要綁定的地址和端口號
    • address_len:address緩沖區(qū)的長度
    • socklen_t 即 unsigned int
    • sizeof 的返回值也是 unsigned int

    2.4 返回值

    示例:

    復(fù)制代碼
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    // 綁定 ip port struct sockaddr\_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9123); // htons 主機字節(jié)序轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序 // 方法1: // INADDR\_ANY 是通配地址,即本機所有 ip 都綁定上。 INADDR\_ANY 轉(zhuǎn)換過來就是0.0.0.0 inet\_pton(AF_INET, INADDR_ANY, &addr.sin_addr.s_addr); // 方法2: // inet\_addr()作用是將一個IP字符串轉(zhuǎn)化為一個網(wǎng)絡(luò)字節(jié)序的整數(shù)值,用于sockaddr\_in.sin\_addr.s\_addr。 addr.sin_addr.s_addr = inet\_addr("192.168.0.115"); int res = bind(sock_fd, (struct sockaddr *) &addr, sizeof(addr));

    • res = 0:綁定成功
    • res = -1:綁定失敗

    2.5 作用

    將 addr 指向的 sockaddr 結(jié)構(gòu)體中描述的一些屬性(IP地址、端口號、地址簇)與 socket 套接字綁定,也叫給套接字命名。

    調(diào)用 bind() 后,就為 socket 套接字關(guān)聯(lián)了一個相應(yīng)的地址與端口號,即發(fā)送到該地址該端口的數(shù)據(jù)可通過 socket 讀取和使用。當然也可通過該 socket 發(fā)送數(shù)據(jù)到指定目的。

    對于Server,bind()是必須要做的事情,服務(wù)器啟動時需要綁定指定的端口來提供服務(wù)(以便于客戶向指定的端口發(fā)送請求),對于服務(wù)器 socket 綁定地址,一般而言將 IP 地址賦值為 INADDR_ANY(該宏值為0),即無論發(fā)送到系統(tǒng)中的哪個 IP 地址(當服務(wù)器有多張網(wǎng)卡時會有多個 IP 地址)的請求都采用該 socket 來處理,而無需指定固定 IP。

    對于 Client,一般而言無需主動調(diào)用 bind(),一切由操作系統(tǒng)來完成。在發(fā)送數(shù)據(jù)前,操作系統(tǒng)會為套接字隨機分配一個可用的端口,同時將該套接字和本地地址信息綁定。

    關(guān)于套接字更詳細的使用,可參考:https://github.com/qiyu56/network/tree/master/udp

    3. sendto() 函數(shù)

    3.1 函數(shù)參數(shù)

    示例:int sendto(int sock_fd, const void *buf, int len, int flags, const struct sockaddr *address, socklen_t address_len);

    • sock_fd:套接字描述符

    • void *buf:UDP 數(shù)據(jù)報緩存區(qū)(包含待發(fā)送數(shù)據(jù))

    • void* 指針可以指向任意類型的數(shù)據(jù):
    • 復(fù)制代碼
      • 1
      • 2
      • 3void *p; int *a; p = a; // a = (int *)p;
    • UDP 數(shù)據(jù)報緩存區(qū):
    • sendto 把數(shù)據(jù)放在 sendbuf(緩沖區(qū)),通知操作系統(tǒng)來取

    • 操作系統(tǒng)在適當?shù)臅r候過來取數(shù)據(jù),并發(fā)到網(wǎng)絡(luò)
      這意味著:存入數(shù)據(jù)和發(fā)送數(shù)據(jù)存在時間差(異步的),如果存入數(shù)據(jù)太快太多,緩沖區(qū)會滿。緩沖區(qū)滿的處理

    • 知道緩沖區(qū)有剩余空間(阻塞)

    • 新發(fā)送的數(shù)據(jù)沒有存入緩沖區(qū)(直接丟掉)
      丟包對 UDP 來說是很正常,在使用 UDP 時就應(yīng)該允許丟包

    • len:UDP數(shù)據(jù)報的長度

    • flags:調(diào)用方式標志位(一般設(shè)置為 000,先不掌握)

    • sockaddr *address:sockaddr結(jié)構(gòu)指針,該結(jié)構(gòu)中包含了要發(fā)送的地址和端口號

    • address_len:address緩沖區(qū)的長度

    • socklen_t 即 unsigned int
    • sizeof 的返回值也是 unsigned int

    3.2 返回值

    示例:

    復(fù)制代碼
    • 1
    • 2
    • 3

    char buf[128] = ""; fgets(buf, sizeof(buf) , stdin); int res = sendto(sock_fd , buf , strlen(buf) , 0, (struct sockaddr *) &server_addr, sizeof(server_addr));

    • res = x:發(fā)送成功,xxx 為發(fā)送出去的字符數(shù)
    • res = -1:發(fā)送失敗

    3.3 作用

    把 UDP 數(shù)據(jù)報發(fā)給指定地址。

    4. revcfrom() 函數(shù)

    4.1 函數(shù)參數(shù)

    示例:recvfrom(int socke_fd, const void *buf, int len, int flags, struct sockaddr *address, socklen_t *address_len)

    • sock_fd:套接字描述符

    • void *buf:UDP 數(shù)據(jù)報緩存區(qū)(包含所接收的數(shù)據(jù))

    • UDP 數(shù)據(jù)報緩存區(qū):
    • 操作系統(tǒng)不停把從網(wǎng)絡(luò)上接收數(shù)據(jù),緩存在 recvbuf(緩沖區(qū)) 里
    • recvfrom從緩存區(qū)里接收數(shù)據(jù)
      這意味著:不論你是否去取數(shù)據(jù),操作總是把數(shù)據(jù)收下來存好,recfrom是從recvbuf里取走現(xiàn)成的數(shù)據(jù),如果不及時取走,則緩沖區(qū)會滿。緩沖區(qū)滿的處理:
      • 新的數(shù)據(jù)不被接收
      • 刪除緩沖區(qū)里的現(xiàn)有的數(shù)據(jù),存放新的數(shù)據(jù)。
    • len:UDP數(shù)據(jù)報的長度

    • flags:調(diào)用方式標志位(一般設(shè)置為 000,先不掌握)

    • sockaddr *address:sockaddr結(jié)構(gòu)指針,該結(jié)構(gòu)中包含了發(fā)送方的地址和端口號(可以為 NULL)

    • address_len:socklen_t 指針,指向了 address 結(jié)構(gòu)體的長度(可以為 NULL)

    4.2 返回值

    示例:

    復(fù)制代碼
    • 1
    • 2

    char buf[128] = ""; int recv_len = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &client_len);

    • recv_len = x:接收成功,xxx 為接收到的字符數(shù)
    • res = -1:接收失敗

    4.3 作用

    接收發(fā)送方的網(wǎng)絡(luò)數(shù)據(jù)。

    5. 服務(wù)器代碼與客戶端代碼

    Server.cpp

    復(fù)制代碼
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    `#include
    #include
    #include
    #include
    #include
    using namespace std;

    int main(int argc , char *argv[]){
    cout << “Server:\n”;
    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock_fd < 0) {
    perror(“socket 創(chuàng)建失敗”);
    return 0;
    }
    cout << “socket 創(chuàng)建成功!\n”;
    // 綁定 ip port
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9123);
    // inet_pton(AF_INET, “192.168.0.111”, &addr.sin_addr.s_addr);
    addr.sin_addr.s_addr = inet_addr(“192.168.0.115”); //INADDR_ANY 通配地址,即本機所有 ip 都綁定上。 INADDR_ANY 轉(zhuǎn)換過來就是0.0.0.0
    int res = bind(sock_fd, (struct sockaddr *) &addr, sizeof(addr));
    if(res < 0) {
    perror(“綁定失敗”);
    close(sock_fd);
    return 0;
    }
    cout << “socket 綁定(命名)成功!\n”;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    while(1){

    char buf[128] = “”;
    int recv_len = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &client_len);
    printf(“來自 ip 地址為 %s 端口號為 %d 的信息:%s 信息的總長度為 %d\n” , inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf, recv_len);
    sendto(sock_fd, buf, recv_len, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
    }
    close(sock_fd);
    return 0;
    }`

    Client.cpp

    復(fù)制代碼
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    `#include
    #include
    #include
    #include
    #include
    using namespace std;
    int main(int argc, char *argv[]){
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9123); // 服務(wù)器端口
    inet_pton(AF_INET, “192.168.0.115”, &server_addr.sin_addr.s_addr);

    int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock_fd < 0)
    perror("");

    while(1){
    char buf[128] = “”;
    cin.getline(buf , sizeof(buf));
    int res = sendto(sock_fd , buf , strlen(buf) , 0, (struct sockaddr *) &server_addr, sizeof(server_addr));
    char read_buf[128] = “”;
    recvfrom(sock_fd, read_buf, sizeof(read_buf), 0, NULL, NULL);
    printf(“共發(fā)送 %d 個字符數(shù)\n” , res);
    }
    close(sock_fd);
    return 0;
    }`

    總結(jié)

    以上是生活随笔為你收集整理的Linux C/C++ UDP Socket 网络通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。