AF_INET與套接字
建立套接字的函式是socket(),函式原型為:
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
其中“int domain”引數表示套接字要使用的協議簇,協議簇的在“linux/socket.h”裡有詳細定義,常用的協議簇:
- AF_UNIX(本機通訊)
- AF_INET(TCP/IP-IPv4)
- AF_INET6(TCP/IP-IPv6)
其中“type”引數指的是套接字型別,常用的型別有:
- SOCK_STREAM(TCP流)
- SOCK_DGRAM(UDP資料報)
- SOCK_RAW(原始套接字)
最後一個“protocol”一般設定為“0”,也就是當確定套接字使用的協議簇和型別時,這個引數的值就為0,但是有時候建立原始套接字時,並不知道要使用的協議簇和型別,也就是domain引數未知情況下,這時protocol這個引數就起作用了,它可以確定協議的種類。
socket是一個函式,那麼它也有返回值,當套接字建立成功時,返回套接字,失敗返回“-1”,錯誤程式碼則寫入“errno”中。
建立套接字:
python中socket預設為Ipv4,tcp模式。
服務端
sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((ip,port))#繫結ip和埠號 sock.listen(5)#開啟監聽,允許5個連線, while True: conn,addr = sock.accept() buf = conn.recv(1024) if len(buf): print(buf)
conn.send(buf) sock.close()
listen引數的問題
可以表示可建立socket連線的排隊的個數
windows,mac此連線引數有效
Linux此連線引數無效,預設最大
客戶端
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip,port))#監聽ip和埠號 sock.send(messages) while True: buf = sock.recv(1024)#一次接受1024位元組資料 if len(buf): print(buf) sock.close()
socket程式設計中主動關閉和被動關閉
tcp中server,client都可能是主動關閉方或者被動關閉方,現闡述下兩者之間的關係:
客戶端(client) 伺服器(server)
close() Fin x -> 讀通道關閉(close_wait)
寫通道關閉 <- Ack x+1
讀通道關閉(time_wait) <- Fin y close()
ack y+1 -> 寫通道關閉
2x msl closed
closed
- 客戶端呼叫函式close(),這時,客戶端會發送一個FIN訊號給伺服器
- 伺服器收到FIN訊號,關閉套接字的讀通道,並將自己的狀態設定為CLOSE_WAIT(表示被動關閉),並返回一個ACK給客戶端
- 客戶端收到ACK,關閉套接字寫通道
- 接下來伺服器會呼叫close():
- 伺服器close(),傳送一個FIN到客戶端。
- 客戶端收到FIN,關閉讀通道,並將自己的狀態設定成TIME_WAIT,傳送一個ACK給伺服器
- 伺服器收到ACK,關閉寫通道,並將自己的狀態設定為CLOSE。
- 客戶端等待兩個最大資料傳輸時間,然後將自己的狀態設定成CLOSED。
有了上面的背景知識,對於我們系統線上一個case分析就很簡單了!
首先是主動關閉日誌很多,後來是被動關閉的日誌
由於server端發現了大量閒置的沒有Io的socket連線,有監聽器在監聽是否存在閒置的socket連線,就釋放並關閉這些連線,time_wait就出現了,這個時候應用方客戶端重啟應用,釋放了資源包括一些客戶端連線,這個時候close_wait出現了,正好是以上日誌所反映的
同時time_wait狀態的連線是不會釋放核心資源,所以服務端不要輕易close!
socket是全雙工管道,雖然只有一對socket,但實際上是兩個通道,讀寫是不會衝突的。
python 解決close_wait過多問題
close_wait產生的原因:
首先要知道客戶端和服務端的連線是使用套接字通訊的,TCP/IP協議建立連線需要三次握手,而關閉client與server的連線需要進行四步,如圖:
建立連線後常用的三個狀態是:ESTABLISHED 表示正在通訊,TIME_WAIT 表示主動關閉,CLOSE_WAIT 表示被動關閉。
通過上圖,我們來分析,什麼情況下,連線處於CLOSE_WAIT狀態呢?
就是服務端在被動關閉收到FIN,未發出自己FIN的情況下就處於CLOSE_WAIT狀態了,通常CLOSE_WAIT的持續時間很
短,但是在某些特殊狀態下就可能長時間存在,如果出現大量close_wait的現象,主要原因可能是一方關閉了socket連結,但是另一方
忙與讀或者寫,沒有關閉連線。程式碼需要判斷socket,一旦讀到0,斷開連線,read返回負,檢查一下errno(errno是記錄系統的最後
一次錯誤程式碼。),如果不是AGAIN,就斷開連線。