TCP 與 UDP 協議的 socket 通信

   剛剛有同事問我,tcp 是怎麼進行 socket 通訊的,udp 能進行 socket 通信嘛,想想這些基礎的知識竟一下子語塞,不知道怎麼回答了,在此,我整理了一下相關知識點,以公衆號文章的形式發表一下,個人粗淺的理解,如有不當之處,請各位大佬多多指點修正。

    現在進入正文,咱們先了解一下 socket, 說到 socket, 就得說到互聯網的一個架構,c/s 架構,c/s 是客戶端和服務端 英文的首字母,咱們舉一個最常見應用到這個架構的例子,大部分的小夥伴都會玩王者榮耀吧,玩王者就必須要連接一個服務器一般是騰訊雲服務器,對於我們玩家來說管理王者的騰訊雲服務器就是服務端,服務端有了,那誰是客戶端呢,所謂客戶就是我們這些玩王者的小夥伴,客戶端當然就是王者榮耀 app 了。

 簡單說完了 c/s, 我們再來用一張圖直觀演示一下 socket 在互聯網中的位置

在網絡通信中,Socket 端點由 IP 地址和端口號標識;而在編程中,Socket 是封裝了通信能力的操作系統接口。

_      簡單的_

說了一下 socket, 咱們再說一下 TCP 協議,tcp 協議屬於網絡協議,什麼叫網絡協議呢,通俗點說 就是兩臺互聯網機器 雙方就通信如何進行所必須共同遵守的約定和通信規則的集合。在網絡上通信的雙方只有遵守相同的協議,才能正確地交流信息,就像人們交談時要使用同一種語言一樣,如果談話裏使用不同的語言,就會造成雙方都不知所云,交流就被迫中斷_

_   說到 tcp 協議大家應該很容易想到的就是三次握手,四次揮手,話不多說,咱先上圖,便於大家理解:_

爲什麼創建鏈接需要 3 步,而斷開鏈接則需要 4 步呢?

可以看到,三次握手之前是沒有數據傳輸的,並且其中第二次是一次性發送了一個請求和一個確認。所以減少了一次操作。而四次揮手涉及到數據的傳輸,所以不可能簡化成三次揮手。(四次揮手也是不同於三次握手,四次揮手也是建立在雙向鏈接通道的基礎之上的,而三次握手的時候該雙向通道還未建立成功),如下圖

FIN_WAIT_1:代表主動發起斷開鏈接請求

FIN_WAIT_2:代表此時的 Client 端不會再主動向 Server 端發送數據

TIME_WAIT:代表 Client 端還要回復最後一條確認消息,回覆完畢後雙向鏈接正式關閉

CLOSE_WAIT:代表關閉等待

LAST_ACK:代表持續的確認(即只要 Client 端沒有回覆第 4 條信息,Server 端就不斷嘗試發送斷開鏈接的 FIN 請求)

    別講太複雜了,咱的理解目前也只是到這個層面,下面再來看看 udp 協議,UDP 協議是一種基於數據報的格式(也被稱爲基於消息),不同於 TCP 的字節流格式。UDP 的數據報格式是有頭有尾的,這一點很重要。對應下圖

另外,udp 協議是不需要建立雙向鏈接通道的,並且 udp 發消息發一次就不會管了,不管收沒收到。

最後用一張表格比較一下這兩大協議,讓大家看得更直觀一些

講完這兩大協議和 socket 的概念,最後咱講講,他們是怎麼通過 socket 通信的吧。

TCP Socket:面向連接,可靠傳輸(類似打電話)

UDP Socket :無連接,不可靠傳輸(類似發短信)

這邊咱直接上代碼,看不懂的可以跳過:

服務端代碼(C 語言示例)

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <unistd.h>

int main() {

    // 1. 創建 TCP Socket

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    // 2. 綁定 IP 和端口

    struct sockaddr_in addr;

    addr.sin_family = AF_INET;

    addr.sin_addr.s_addr = INADDR_ANY; // 監聽所有網卡

    addr.sin_port = htons(8080);       // 端口號

    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));

    // 3. 開始監聽

    listen(server_fd, 5); // 允許最多 5 個客戶端排隊

    // 4. 接受客戶端連接

    int client_fd = accept(server_fd, NULL, NULL);

    // 5. 發送數據

    char msg[] = "Hello TCP Client!";

    send(client_fd, msg, sizeof(msg), 0);

    // 6. 關閉連接

    close(client_fd);

    close(server_fd);

    return 0;

}

客戶端代碼

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main() {

    // 1. 創建 TCP Socket

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    // 2. 連接服務器

    struct sockaddr_in server_addr;

    server_addr.sin_family = AF_INET;

    server_addr.sin_port = htons(8080);

    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // 服務器 IP

    connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 3. 接收數據

    char buffer[1024] = {0};

    recv(sock, buffer, 1024, 0);

    printf("Server says: %s\n", buffer);

    // 4. 關閉連接

    close(sock);

    return 0;

}

TCP Socket 特點

優點

缺點

UDP  socket 通訊流程

服務端代碼(c 語言):

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main() {

    // 1. 創建 UDP Socket

    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 綁定 IP 和端口

    struct sockaddr_in addr;

    addr.sin_family = AF_INET;

    addr.sin_addr.s_addr = INADDR_ANY;

    addr.sin_port = htons(8080);

    bind(sock, (struct sockaddr*)&addr, sizeof(addr));

    // 3. 接收數據(無連接,直接收)

    char buffer[1024];

    struct sockaddr_in client_addr;

    socklen_t addr_len = sizeof(client_addr);

    recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&client_addr, &addr_len);

    // 4. 發送數據(無連接,直接發)

    char msg[] = "Hello UDP Client!";

    sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&client_addr, addr_len);

    // 5. 關閉 Socket

    close(sock);

    return 0;

}

客戶端代碼

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main() {

    // 1. 創建 UDP Socket

    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 發送數據(無需 connect)

    struct sockaddr_in server_addr;

    server_addr.sin_family = AF_INET;

    server_addr.sin_port = htons(8080);

    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    char msg[] = "Hello Server!";

    sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 3. 接收數據

    char buffer[1024] = {0};

    recvfrom(sock, buffer, 1024, 0, NULL, NULL);

    printf("Server says: %s\n", buffer);

    // 4. 關閉 Socket

    close(sock);

    return 0;

}

UDP Socket 特點

 優點

 缺點

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/58EixVHdm2TspZno7hKf9A