套接字基礎
C/S架構,即客戶端/服務器架構,B/S架構(瀏覽器/服務器),也屬於C/S架構
socket介紹
socket套接字就是為了完成C/S架構軟件的開發。socket依賴於網絡,所以騷年,網絡基礎不能忘了。
在Python中,socket子層位於TCP/IP協議棧的傳輸層和應用層的中間層,是一個提供向上向下接口的軟件抽象層。socket封裝了tcp和udp協議,所以遵循socket語法寫出的程序遵循tcp和udp協議
注:socket = IP + port,ip用來標識網絡中主機的位置,port用來標識主機的應用,所以ip + port能夠標識互聯網中的唯一一個應用,所以說socket其實就是IP和端口的組合
socket分類
網絡編程只需要關注AF_INET,這種是應用最廣泛的,如果是用於ipv6平台需要用AF_INET6。
其他:AF_UNIX,用於UNIX文件系統間通信、還有很多其他的平台使用的。
socket通信原理

上圖為sockettcp通信過程:
1.服務器先初始化socket,然后與端口綁定(bind),對端口進行監聽(listen)並調用accept阻塞等待
2.客戶端連接先初始化一個socket,然后連接服務器(connect),如果正常訪問到了服務器端,服務器端阻塞結束,連接成功,這時客戶端與服務器端的連接建立。
3.客戶端發送數據請求,服務器端接收請求並處理請求,然后服務器把回應數據發送給客戶端,客戶端讀取數據,循環。
4.最后客戶端或者服務端關閉連接,一次交互結束。
socket模塊
如:基於本地環回地址的一次性套接字通信
服務端:
#導入socket模塊
import socket
#創建socket,類似於買手機
skt=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#綁定端口和ip,必須為元組類型,類似於手機插卡
#,如果元組為('',9000)表示本機所有的網卡,相當於0.0.0.0
skt.bind(('127.0.0.1',9000))
#偵聽訪問端口,類似於手機待機
#若括號中有值,則表示對TCP連接的優化
skt.listen()
#此處循環表示服務器持續提供服務
while True:
#conn表示接受的數據流,addr表示客戶端的地址
conn,addr=skt.accept()
#接受客戶端發送消息並打印
msg=conn.recv(1024)
print(msg.decode('utf-8'))
print(msg,type(msg))
#為客戶端返回消息,表示接受成功
conn.send(msg.upper())
#關閉本次通信
conn.close()
#關閉鏈接
skt.close()
客戶端
#導入socket模塊
import socket
#創建socket
skt = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#與服務端建立鏈接
skt.connect(('127.0.0.1',9000))
#發送消息
msg = b'Hello'
skt.send(msg)
#接受服務端返回值並打印
res = skt.recv(100)
print(res.decode('utf-8'))
#關閉會話鏈接
skt.close()
#相關值說明
1 socket.socket(socket_family,socket_type,protocal=0) 2 socket_family可以是 AF_UNIX 或 AF_INET 3 socket_type 可以是 SOCK_STREAM(面向連接的可靠數據傳輸,即TCP協議)或 SOCK_DGRAM(面向無連接的不可靠數據傳輸,即UDP) 4 protocol 一般不填,默認值為 0
#相關方法說明
1 服務端套接字函數
2 s.bind() 綁定(主機,端口號)到套接字
3 s.listen() 開始TCP監聽
4 s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
5
6 客戶端套接字函數
7 s.connect() 主動初始化TCP服務器連接
8 s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
9
10 公共用途的套接字函數
11 s.recv() 接收TCP數據
12 s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩余空間時,數據丟失,不會發完)
13 s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
14 s.recvfrom() 接收UDP數據
15 s.sendto() 發送UDP數據
16 s.getpeername() 連接到當前套接字的遠端的地址
17 s.getsockname() 當前套接字的地址
18 s.getsockopt() 返回指定套接字的參數
19 s.setsockopt() 設置指定套接字的參數
20 s.close() 關閉套接字
21
22 面向鎖的套接字方法
23 s.setblocking() 設置套接字的阻塞與非阻塞模式
24 s.settimeout() 設置阻塞套接字操作的超時時間
25 s.gettimeout() 得到阻塞套接字操作的超時時間
26
27 面向文件的套接字的函數
28 s.fileno() 套接字的文件描述符
29 s.makefile() 創建一個與該套接字相關的文件
#常見錯誤處理

由於服務端仍然存在四次揮手的time_wait狀態在占用地址(如果不懂,請深入研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高並發情況下會有大量的time_wait狀態的優化方法)
最直接的解決方法,更改端口號
更多解決方法:
window解決方法
1 #加入一條socket配置,重用ip和端口
2 phone=socket(AF_INET,SOCK_STREAM)
3 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
4 phone.bind(('127.0.0.1',9000))
Linux解決方法
1 發現系統存在大量TIME_WAIT狀態的連接,通過調整linux內核參數解決,
2 vi /etc/sysctl.conf
3
4 編輯文件,加入以下內容:
5 net.ipv4.tcp_syncookies = 1
6 net.ipv4.tcp_tw_reuse = 1
7 net.ipv4.tcp_tw_recycle = 1
8 net.ipv4.tcp_fin_timeout = 30
9
10 然后執行 /sbin/sysctl -p 讓參數生效。
11
12 net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
13
14 net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉;
15
16 net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。
17
18 net.ipv4.tcp_fin_timeout 修改系統默認的 TIMEOUT 時間
