Linux網路程式設計(二) 基本TCP套接字程式設計

2021-06-23 01:21:14 字數 3683 閱讀 2693

為了執行網路io,乙個程序必須先呼叫socket函式,指定期望通訊的協議型別

1: #include
2: int socket(int family,int type,int protocol);
3: //返回:成功返回非負描述符,若出錯返回-1
family引數的值

af_inet      ipv4協議

af_inet6    ipv6協議

type引數的值

sock_stream    位元組流套接字

sock_dgram     資料報套接字

protocol引數的值

ipproto_tcp       tcp傳輸協議

ipproto_udp       udp傳輸協議

tcp客戶利用connect函式建立於tcp服務端的連線

1: #include
2: int connect(int socked,const

struct sockaddr *servaddr,socklen_t addrlen);

3: //返回:若成功則為0,出錯為-1
sockfd是由socket函式返回的描述符,第二個和第三個引數分別是指向套接字位址結構的指標和該結構的大小。

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

呼叫connect函式將激發tcp的三路握手過程,而且僅在建立成功後或出錯才返回,其中出錯可能有以下幾種情況

(1)若tcp沒有收到syn分節的相應,則返回etimeout錯誤。(呼叫connect函式時,核心傳送乙個syn,若無響應,則等待6秒後再傳送,仍無響應則等大氣24秒,總共等待75秒後無響應返回錯誤)

(2)若對客戶的syn的響應是rst分節,則表明服務端主機在我們指定的埠沒有程序正在等待與之連線。這是一種硬錯誤,客戶一接收到rst就馬上返回econnrefused錯誤

(3)若客戶發出的syn在中間的某個路由器上引發了目的不可達icmp錯誤,則認為是一種軟錯誤。客戶主機核心儲存該訊息,並按第一種情況中所述的時間間隔傳送syn。若在某個規定的時間後仍未收到響應(一般為75秒),則把儲存的訊息作為ehostunreach或enetunreach錯誤返回個程序。

下圖顯示connect後激發三次握手過程

connect函式將當前套接字從closed狀態轉換為syn_sent狀態,若成功則再裝換到established狀態。若失敗則套接字不再可用,必須關閉,我們不能對這樣的套接字再次呼叫connect

bind函式將乙個本地協議位址賦予乙個套接字。

1: #include
2: int bind(int sockfd,const

struct sockaddr *myaddr,socklen_t addrlen);

3: //返回:若成功返回0,出錯返回-1
listen函式僅由tcp伺服器呼叫,它做兩件事

(1)當socket建立乙個套接字時,他被假設為乙個主動套接字,也就是說它是乙個呼叫connect函式發起連線的客戶端套接字。listen函式將乙個未連線的套接字轉換成乙個被動套接字。呼叫listen導致套接字從closed狀態轉換到listen狀態

(2)本函式的第二個引數指定了核心為相應套接字排隊的最大連線數

1: #include
2: int listen(int sockfd,int backlog);
3: //若成功返回0,若出錯返回-1
核心為每乙個給定的監聽套接字維護兩個佇列:

(1)未完成連線佇列:每個這樣的syn分節對應一項:已由某個客戶發出並到達伺服器,而伺服器正在等待完成相應的tcp三路握手過程。這些套接字處於syn_rcvd狀態

(2)已完成連線佇列:每個已完成tcp三次握手過程的客戶對應其中一項。這些套接字處於established狀態

下圖描繪了上述的兩個佇列

當來自客戶的syn到達時,tcp在未完成連線佇列中建立乙個新項,然後響應以三路握手的第二個分節:伺服器的syn響應,其中捎帶對客戶syn的ack。這一項一直儲存在未完成連線佇列中,直到三路握手的第三個分節(客戶對伺服器syn的ack)到達或者超時為止。

如果三路握手正常完成,該項就從未完成佇列移到已完成連線佇列的隊尾。當程序呼叫accept時,已完成佇列中的隊頭返回給程序,如果程序為空,那麼程序投入睡眠,直到tcp在該佇列中放入一項才喚醒它。

在三路握手正常完成的前提下,未完成連線佇列中的任意一項在其中存留的時間就是乙個rtt

當乙個syn到達時,若這些佇列是滿的,tcp就忽略這個分節,不傳送rst。這麼做是因為:要是伺服器返回乙個rst,客戶的connect呼叫就會立即返回乙個錯誤,而不是tcp正常重傳機制來處理

在三路握手完成後,但在伺服器呼叫accept之前到達的資料應該由伺服器tcp排隊,最大資料量為相應已連線套接字的接收緩衝區大小

accept函式有tcp伺服器呼叫,用於從已完成連線隊頭返回乙個已完成連線。如果隊列為空,那麼程序被投入睡眠。

1: #include
2: int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
3: //返回:若成功為非負描述符,若出錯為-1
返回值為已連線套接字,第乙個引數為監聽套接字

這兩個函式返回與某個套接字關聯的本地協議位址,或者返回與某個套接字關聯的外地協議位址

1: #include
2: int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
3: int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
4: //返回:若成功返回0,若出錯則為-1
第乙個引數為套接字描述符,必須是已連線套接字描述符,而不是監聽套接字描述符

乙個基本的併發伺服器模型最簡單的方法就是fork乙個子程序來服務每個客戶

1: pid_t pid;
2: int listenfd,connfd;
3: listenfd=socket();
4:
5: bind(listenfd,....);
6: listen(listenfd,listenq);
7: for(;;)
8:
17: close(connfd);   //parent closes connectfd socket
18: }
如果父程序每個由accept返回的已連線套接字都不呼叫close

首先,父程序將耗盡可用的描述符,因為在任何時刻可擁有的開啟著的描述符數通常是有限制的。

而且,沒有乙個客戶將會被終止。當子程序關閉已連線套接字時,它的引用計數將由2遞減為1且保持為1,因為父程序不關閉任何已連線套接字,這將妨礙tcp連線終止序列的發生,導致連線一直開啟著。

Linux 網路程式設計 TCP

一 tcp通訊步驟 tcp伺服器部分 1 呼叫函式socket 建立乙個socket 2 設定sockaddr in資訊,如要連線伺服器的ip和埠等屬性 3 呼叫函式bind 繫結ip位址 埠等資訊到socket上 4 呼叫函式listen 設定允許的最大連線數 5 呼叫函式accept 等待來自客...

Linux 網路程式設計(TCP)

include include include include include include include include int main int argc,char argv bzero server addr,sizeof struct sockaddr in server addr.si...

linux網路程式設計 初探TCP

伺服器端等待客戶端連線,連線成功後,列印客戶端的ip和port,然後迴圈接收資料,緩衝區無資料就阻塞待待。include include include include include include include include include include define maxbuf 10 ...