三次握手:
TCP 在中,提供可靠的連接服務,採用三次握手建立一個連接. 第一次握手:建立連接時,客戶端發送syn包(syn=j)到,並進入SYN_SEND狀態,等待服務器確認; SYN:同步序列編號(Synchronize Sequence Numbers) 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入狀態; 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手.
完成三次握手,客戶端與服務器開始傳送數據
(1)第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
(2)第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求建立連接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
(3)第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,如果正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸數據了。
SYN***:
在三次握手過程中,Server發送SYN-ACK之後,收到Client的ACK之前的TCP連接稱爲半連接(half-open connect),此時Server處於SYN_RCVD狀態,當收到ACK後,Server轉入ESTABLISHED狀態。SYN***就是Client在短時間內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server回覆確認包,並等待Client的確認,由於源地址是不存在的,因此,Server需要不斷重發直至超時,這些僞造的SYN包將產時間佔用未連接隊列,導致正常的SYN請求因爲隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱瘓。SYN***時一種典型的DDOS***,檢測SYN***的方式非常簡單,即當Server上有大量半連接狀態且源IP地址是隨機的,則可以斷定遭到SYN***了,使用如下命令可以讓之現行: #netstat -nap | grep SYN_RECV
四次揮手
所謂四次揮手(Four-Way Wavehand)即終止TCP連接,就是指斷開一個TCP連接時,需要客戶端和服務端總共發送4個包以確認連接的斷開。在socket編程中,這一過程由客戶端或服務端任一方執行close來觸發
由於TCP連接時全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成數據發送任務後,發送一個FIN來終止這一方向的連接,收到一個FIN只是意味着這一方向上沒有數據流動了,即不會再收到數據了,但是在這個TCP連接上仍然能夠發送數據,直到這一方向也發送了FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉,上圖描述的即是如此。
(1)第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
(2)第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
(3)第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
(4)第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。
任何一種通信協議都必須包含兩部分:
1 報頭:必須是固定長度(如果不固定長度,會有粘包現象) 2 數據: 數據可以用字典的形式來傳.比如 數據的名字,大小,內容,描述
標識地址的方式
ip+mac就能標識全世界範圍內獨一無二的一臺計算機 ip+mac+port就能標識全世界範圍內獨一無二的一個基於網絡通信的應用軟件 url地址:標識全世界範圍內獨一無二的一個資源 DHCP 默認端口是 67 DNS 默認端口 53
爲何建立連接要三次而斷開連接卻需要四次
三次握手是爲了建立連接,建立連接時並沒有數據產生 四次揮手斷開連接是因爲客戶端與服務端已經產生了數據交互, 這時客戶端發送請求只斷開了客戶端與服務端的連接, 而服務端說不定還有別的數據沒有傳送完畢,所有一定要四次
爲何tcp協議是可靠協議,而udp協議是不可靠協議
tcp調用的操作系統,操作系統發出數據,接受到對方傳來的確認信息時纔會清空數據 優點: 數據安全 缺點: 工作效率低 udp是直接發送, 發完就刪 優點: 效率高 缺點: 數據不安全
爲何tcp協議會有粘包問題?
因爲tcp想優化效率,裏面有個叫nagle算法.這個算法規定了tcp協議在傳輸數據的時候會將數據較小,傳輸間隔較短的多條數據合併成一條發送
而tcp是通過操作系統來發送數據的,操作系統想什麼時候發就什麼時候發,應用層管不到操作系統, tcp把數據交給操作系統是告訴了操作系統一件事,讓操作系統把數據較小,傳輸間隔較短的多條數據合併成一條發送.就造成了粘包現象
struct模塊
模擬ssh遠程執行命令
客戶端:
from socket import *
import struct
import json
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081))
# 通信循環
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1. 先收4bytes,解出報頭的長度
header_size=struct.unpack('i',client.recv(4))[0]
#2. 再接收報頭,拿到header_dic
header_bytes=client.recv(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic['total_size']
#3. 接收真正的數據
cmd_res=b''
recv_size=0
while recv_size < total_size:
data=client.recv(1024)
recv_size+=len(data)
cmd_res+=data
print(cmd_res.decode('gbk'))
client.close()
服務端:
# 服務端必須滿足至少三點:
# 1. 綁定一個固定的ip和port
# 2. 一直對外提供服務,穩定運行
# 3. 能夠支持併發
from socket import *
import subprocess
import struct
import json
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5)
# 鏈接循環
while True:
conn, client_addr = server.accept()
print(client_addr)
# 通信循環
while True:
try:
cmd = conn.recv(1024) # cmd=b'dir'
if len(cmd) == 0: break # 針對linux系統
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 1. 先製作報頭
header_dic = {
'filename': 'a.txt',
'md5': 'asdfasdf123123x1',
'total_size': len(stdout) + len(stderr)
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8')
# 2. 先發送4個bytes(包含報頭的長度)
conn.send(struct.pack('i', len(header_bytes)))
# 3 再發送報頭
conn.send(header_bytes)
# 4. 最後發送真實的數據
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
server.close()