使用套接字建立連線 TCP

2021-10-03 13:14:54 字數 3278 閱讀 7724

int socket(int domain, int type, int protocol)

domain: pf_inet, pf_inet6以及pf_local等,表示什麼型別的套接字

type:

.sock_stream:表示位元組流,tcp

.sock_dgram :表示資料報,udp

.sock_raw      :表示原始套接字

protoc:原本是用來指定通訊協議,但現在基本廢棄,因為協議已經通過前面兩個引數指定完成,一般置0;

建立出來的套接字如果需要被別人使用,就需要呼叫bind函式把套接字和套接字位址繫結,就像去電信局登記我們的**號碼一樣;

int bind(int fd, sockaddr *addr, socklen_t len);
需要注意第二個引數是通用位址格式:sockaddr * addr。 這裡雖然接收的是通用位址格式,實際上傳入的引數可能是ipv4,ipv6或本地套接字格式。

bind函式會根據len欄位判斷傳入的引數addr該怎麼解析,len欄位表示的就是傳入的位址長度,它是乙個可變值。

這樣,可以把它理解成

int bind(int fd, void* addr, socklen_t len);
那之前為什麼不直接使用void呢?原因是void後面才定義的啦,bsd設計套接字的時候大約在2023年,那個時候還沒有void *的支援。

所以每次使用的的時候,都需要將ipv4,ipv6或本地套接字格式轉為通用套接字格式,內部根據len對位址進行解析和判斷。

struct sockadd_in addr;

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

bind監聽設定通配位址,不挑活,只要有來,我就接。

對於ipv4,使用inaddr_any來完成設定

對於ipv6,使用in6addr_any來完成設定

struct sockaddr_in addr;

addr.sin_addr.s_addr = htonl(inaddr_any);

當然也可以把埠設定為0,就相當於把埠的選擇權交給作業系統核心來處理,作業系統核心會根據一定的演算法選擇乙個空閒的埠,完成套接字的繫結。

bind只是讓我們的套接字和位址關聯,如同登記**號碼。如果別人要打通**,還需要把**裝置接入**線,讓伺服器真正處於可接聽的狀態,這個過程需要依賴listen函式。

int listen(int socketfd, int backlog);

伺服器收到客戶端的連線請求了,應答成功,連線建立,這時候作業系統核心需要把這個事件通知到應用程式,並讓應用程式感知到這個連線。相當於拿起**筒開始交流了。

int accept(int listenfd, struct sockaddr *cliadd, sockelen_t *addrlen);
listenfd是前面通過bind,listen一系列操作而得到的套接字。

clidd:是通過指標方式獲取的客戶端的位址,addrlen告訴我們位址的大小,這裡理解成當我們拿起**機時,看到了來電顯示,知道了對方的號碼;

函式的返回值是乙個全新的描述符,代表了與客戶端的連線。

第一步還是和服務端一樣要先建立乙個套接字。

不一樣的是客戶端需要呼叫connect向服務端發起請求。

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

第乙個引數是連線套接字,第二三個引數是servaddr和addlen分別代表指向套接字位址結構的指標和該結構的大小。套接字位址結構必須含有伺服器的ip和port;

在呼叫connect前不必非得呼叫bind函式,因為如果需要的話,核心會確定源ip位址,並按照一定的演算法選擇乙個臨時埠作為源埠。

如果是tcp套接字,那麼connect會激發tcp的三次握手,而且僅在連線建立成功或出錯時才返回。其**錯返回可能有一下幾種情況:

1).三次握手無法建立,客戶端發出的syn包沒有響應,於是返回了timeout錯誤。這比較常見的原因是伺服器的資訊填錯;

2).客戶端收到了rst(復位)回答,這時候客戶端會立即返回connection refused錯誤。比較常見於客戶端傳送連線請求時的請求埠寫錯,因為rst是tcp在發生錯誤時傳送的tcp分節。

產生rst的三個條件:目的地為某埠的syn到達,然而該埠上沒有正在監聽的伺服器;tcp想取消乙個已有連線;tcp接收到乙個根本不存在的連線上的分節。

3).客戶發出syn包在網路上引起"destination unreachable",即目的地不可達的錯誤。可能是客戶端和伺服器的路由不通。

1.客戶端的協議棧向伺服器端傳送了 syn 包,並告訴伺服器端當前傳送序列號 j,客戶端進入 sync_sent 狀態;

2.伺服器端的協議棧收到這個包之後,和客戶端進行 ack 應答,應答的值為 j+1,表示對 syn 包 j 的確認,同時伺服器也傳送乙個 syn 包,告訴客戶端當前我的傳送序列號為 k,伺服器端進入 sync_rcvd 狀態;

3.客戶端協議棧收到 ack 之後,使得應用程式從 connect 呼叫返回,表示客戶端到伺服器端的單向連線建立成功,客戶端的狀態為 established,同時客戶端協議棧也會對伺服器端的 syn 包進行應答,應答資料為 k+1;

4.應答包到達伺服器端後,伺服器端協議棧使得 accept 阻塞呼叫返回,這個時候伺服器端到客戶端的單向連線也建立成功,伺服器端也進入 established 狀態。

形象一點的比喻是這樣的,有 a 和 b 想進行通話:

a 先對 b 說:「喂,你在麼?我在的,我的口令是 j。」

b 收到之後大聲回答:「我收到你的口令 j 並準備好了,你準備好了嗎?我的口令是 k。」

a 收到之後也大聲回答:「我收到你的口令 k 並準備好了,我們開始吧。」

可以看到,這樣的應答過程總共進行了三次,這就是 tcp 連線建立之所以被叫為「三次握手」的原因了。

伺服器端通過建立 socket,bind,listen 完成初始化,通過 accept 完成連線的建立。

客戶端通過建立 socket,connect 發起連線建立請求。

**例項可以參看:tcp通訊例項

Tcp套接字連線 二

include include using namespace std pragma comment lib,ws2 32.lib intmain 檢查if lobyte wsadata.wversion 1 hibyte wsadata.wversion 1 建立tcp 套接字 socket so...

套接字學習之tcp連線的建立

需要重新拿起書本複習複習,所以就有了下面的博文,希望我能堅持學習,堅持努力。tcp連線的建立總的來說就是乙個跟 系統類似的套路 對於伺服器 1.建立乙個socket用於通訊 類似listenfd socket af inet,sock stream,0 2.繫結乙個位址好讓別人能找到你 類似 str...

socket套接字 TCP使用

使用流程 建立套接字 connect鏈結tcp伺服器 命令 socket物件.connect 伺服器ip,伺服器port 3.收 發資料recv 接收資料的大小 send 傳送資料 4.關閉套接字 import socket def main 建立socekt物件 tcp socket socket...