Menu
快讀
  • 旅遊
  • 生活
    • 美食
    • 寵物
    • 養生
    • 親子
  • 娛樂
    • 動漫
  • 時尚
  • 社會
  • 探索
  • 故事
  • 科技
  • 軍事
  • 国际
快讀

TCP、UDP通信總結

2021 年 3 月 11 日 御月居月子中心

一、什麽是socket?

Socket的英文原義是“孔”或“插座”。在編程中,Socket被稱做套接字,是網絡通信中的一種約定。Socket編程的應用無處不在,我們平時用的QQ、微信、浏覽器等程序,都與Socket編程有關。我們平時使用浏覽器查資料,這個過程的技術原理是怎樣的呢?

TCP、UDP通信總結

TCP、UDP通信總結

這裏的SYN和ACK是都是標志位,其中SYN代表新建一個連接,ACK代表確認。其中m、n都是隨機數。具體說明如:

  • 第一次握手:SYN標志位被置位,客戶端向服務端發送一個隨機數m。
  • 第二次握手:ACK、SYN標志位被置位。服務端向客戶端發送m+1表示確認剛才收到的數據,同時向客戶端發送一個隨機數n。
  • 第三次握手:ACK標志被置位。客戶端向服務端發送n+1表示確認收到數據。

TCP斷開(四次揮手)

TCP斷開連接時,客戶端、服務端要完成四次數據交互工作才能建立連接,常稱爲四次揮手。可形象比喻爲如下對話:

客戶端:服務端您好,我發送數據完畢了,即將和您斷開連接。

服務端:客戶端您好,我稍稍准備一下,再給您斷開

服務端:客戶端您好,我准備好了,您可以斷開連接了。

客戶端:好的,合作愉快!

具體示意圖爲:

TCP、UDP通信總結

(2)UDP通信socket編程過程

TCP、UDP通信總結

(2)Linux的程序:

在linux下,“一切都是文件”,所以這裏我們的套接字也當做文件來看待。

服務端程序linux_tcp_server.c:

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

#define BUF_LEN 100

int main(void) { int ServerFd, ClientFd; char Buf[BUF_LEN] = {0}; struct sockaddr ClientAddr; int addr_len = 0, recv_len = 0; struct sockaddr_in ServerSockAddr; int optval = 1; /* 創建服務端文件描述符 */ if (-1 == (ServerFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { printf(“socket error!\n”); exit(1); } /* 設置服務端信息 */ memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 給結構體ServerSockAddr清零 ServerSockAddr.sin_family = AF_INET; // 使用IPv4地址 ServerSockAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 自動獲取IP地址 ServerSockAddr.sin_port = htons(6666); // 端口

// 設置地址和端口號可以重複使用 if (setsockopt(ServerFd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { printf(“setsockopt error!\n”); exit(1); } /* 綁定操作,綁定前加上上面的socket屬性可重複使用地址 */ if (-1 == bind(ServerFd, (struct sockaddr*)&ServerSockAddr, sizeof(struct sockaddr))) { printf(“bind error!\n”); exit(1); } /* 進入監聽狀態 */ if (-1 == (listen(ServerFd, 10))) { printf(“listen error!\n”); exit(1); } addr_len = sizeof(struct sockaddr);

while (1) { /* 監聽客戶端請求,accept函數返回一個新的套接字,發送和接收都是用這個套接字 */ if (-1 == (ClientFd = accept(ServerFd, (struct sockaddr*)&ClientAddr, &addr_len))) { printf(“accept error!\n”); exit(1); }

/* 接受客戶端的返回數據 */ if ((recv_len = recv(ClientFd, Buf, BUF_LEN, 0)) < 0) { printf(“recv error!\n”); exit(1); } printf(“客戶端發送過來的數據爲:%s\n”, Buf); /* 發送數據到客戶端 */ send(ClientFd, Buf, recv_len, 0); /* 關閉客戶端套接字 */ close(ClientFd); /* 清空緩沖區 */ memset(Buf, 0, BUF_LEN); }

return 0; }

客戶端程序linux_tcp_client.c:

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>

#define BUF_LEN 100

int main(void) { int ClientFd; char Buf[BUF_LEN] = {0}; struct sockaddr_in ServerSockAddr; /* 向服務器發起請求 */ memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); ServerSockAddr.sin_family = AF_INET; ServerSockAddr.sin_addr.s_addr = inet_addr(“127.0.0.1”); ServerSockAddr.sin_port = htons(6666); while (1) { /* 創建客戶端socket */ if (-1 == (ClientFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { printf(“socket error!\n”); exit(1); } /* 連接 */ if (-1 == connect(ClientFd, (struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr))) { printf(“connect error!\n”); exit(1); } printf(“請輸入一個字符串,發送給服務端:”); gets(Buf); /* 發送數據到服務端 */ send(ClientFd, Buf, strlen(Buf), 0); memset(Buf, 0, BUF_LEN); // 重置緩沖區 /* 接受服務端的返回數據 */ recv(ClientFd, Buf, BUF_LEN, 0); printf(“服務端發送過來的數據爲:%s\n”, Buf); memset(Buf, 0, BUF_LEN); // 重置緩沖區 close(ClientFd); // 關閉套接字 } return 0; }

Linux下編譯就不需要添加-lwsock32參數:

gcc linux_tcp_server.c -o linux_tcp_server gcc linux_tcp_client.c -o linux_tcp_client

實驗現象:

TCP、UDP通信總結

經上網查詢發現是端口重複使用,可以在調用bind()函數之前調用setsockopt()函數以解決端口重複使用的問題:

相關文章:

  • HTTP是如何使用TCP連接
  • 身爲前端,你不得不懂的一些HTTP知識(附贈3道面試題)
  • 淘寶超強“帶貨王”——直播低延遲的背後有何貓膩?
  • 從雙十一的物流大戰,看全球通信網絡的低延遲優化
  • 告知你不爲人知的 UDP:連接性和負載均衡
  • docker如何實現跨主機通信
国际

發佈留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

©2025 快讀 | 服務協議 | DMCA | 聯繫我們