使用mysql數據庫的第一步必然是創建鏈接登陸,而後在上面執行SQL命令。不管是經過mysql的客戶端,仍是經過C-API,JDBC標準接口鏈接數據庫,這個過程必定少不了。今天咱們聊一聊mysql登錄具體過程,裏面會涉及client與server的交互,並經過tcpdump抓包給你們展示這一過程。mysql
TCP握手協議sql
遠程鏈接數據庫,mysql採用TCP協議通訊,第一步是創建鏈接,即TCP的3次握手。mysql server端有一個監聽線程等待client請求,client發起請求後,首先發一個sync包到服務端,服務端發一個ack包做爲對客戶端sync包的響應,同時發一個sync包到客戶端,最後客戶端再發一個ack包做爲對服務端的響應。經過3次握手,TCP鏈接纔算真正創建起來,這個時候mysql服務端會分配一個鏈接供客戶端使用。記住,TCP的3次握手都是由TCP的協議棧完成,應用程序並沒有感知。可是,到目前爲止,整個mysql鏈接過程還只完成了第一步--創建TCP鏈接。數據庫
下面附上TCP創建鏈接和斷開鏈接的原理圖。服務器
MYSQL握手協議網絡
TCP鏈接創建成功後,mysql客戶端與mysql服務端開始進行通信,進行mysql認證過程。(1)服務端首先會發一個握手包到客戶端,(2)而後客戶端向服務端發送認證信息(用戶名,密碼等),(3)服務端收到認證包後,會檢查用戶名與密碼是否合法,併發送包告知客戶端認證信息。若是合法,則登錄成功,不然,登錄失敗。鏈接報錯。有時候,咱們經過show processlist看到User處於unauthenticated user ,這說明此時鏈接過程正處於第1步和第2步之間,服務端等待客戶端發認證信息的過程當中。併發
15922528 unauthenticated user 10.xx.2.74:53216 NULL Connect NULL Reading from net NULL
15923418 unauthenticated user connecting host NULL Connect NULL login NULL
tcpdump抓包驗證socket
下面咱們經過tcpdump抓網絡包來驗證咱們的原理。因爲測試在生產環境中進行,爲了不生產網段的IP泄露,對IP做了替換,但不影響分析過程。具體而言,10.aa.zz.142.10556表明客戶端,10.bb.yy.104.3306表明服務器端,3306是服務器的監聽端口號。tcp
(1).在客戶端上打開tcpdump命令,監聽與10.bb.yy.104.3306的通訊網絡包,命令以下:函數
tcpdump -S -nn -tttt -i eth0 host 10.bb.yy.104 and port 3306 and tcp -c 100 -S 將tcp的序列號以絕對值形式輸出,而不是相對值。 -nn 不進行端口名稱的轉換。 -tttt 在每一行中輸出由date處理的默認格式的時間戳。 -i eth0 指定監聽的網絡接口 host 10.bb.yy.104 and port 3306 設置監聽10.bb.yy.104:3306的網絡包 -c 100 表示監聽100包就結束。
(2).在客戶端上,利用mysql命令遠程鏈接服務端10.bb.yy.104,測試
mysql –h10.bb.yy.104 –P3306 –uxxx –pxxx
登錄成功後,而後直接執行exit,退出
(3)分析tcpdump抓取的網絡包,重點分析創建TCP鏈接,MYSQL認證和TCP斷開鏈接的過程。以下圖,圖中第1部分是TCP鏈接創建的過程,第2部分是MYSQL認證的過程,第3部分是登錄成功後,發送基本元數據信息的過程,第4部分是斷開鏈接的過程。經過圖中的標示,咱們能夠清晰的看到TCP創建鏈接的3次握手,MYSQL認證以及TCP斷開鏈接的4次揮手過程。
創建鏈接
這個過程主要體如今第一部分,客戶端10.aa.zz.142.10556,首先發一個編號爲1491894492的SYN包,服務端收到後,發送了1491894492+1的ACK包,併發送了一個2727774925的SYN包,最後客戶端再發送一個2727774925+1的包進行應答。
MYSQL認證
這個過程主要體如今第二部分,服務端10.bb.yy.104:3306首先發一個認證包給客戶端,而後客戶端再發送包含用戶密碼的認證包給服務器,驗證成功後,服務端最後給客戶端一個應答,那麼整個認證過程就結束了,至於第3部分是服務端與客戶端相互發送的一些元數據信息,好比版本信息之類的。
斷開鏈接
這個過程主要體如今第四部分,客戶端發起exit命令時,開始觸發這個動做。客戶端首先發一個編號爲1491894724的FIN包,而後服務器發送一個1491894724+1的ACK包做爲應答,併發送一個編號爲2727775120的FIN包,最後客戶端發送2727775120+1做爲應答,整個過程結束。
數據包標記解析
S=SYN 發起鏈接標誌,通常用於創建TCP鏈接
P=PUSH 傳送數據標誌,通常用於傳輸數據
F=FIN 關閉鏈接標誌,通常用於關閉TCP鏈接
ack 表示應答包
RST= RESET 異常關閉鏈接
.表示沒有任何標誌
源碼實現
用於MYSQL認證代碼主要集中在函數native_password_authenticate中,具體調用層次爲:login_connection->check_connection->acl_authenticate->do_auth_once->native_password_authenticate,函數邏輯很簡單,就是調用write_packet往客戶端發一個認證包,而後調用read_packet等待客戶端返回包含用戶名、密碼等信息的包,最後解析包中的信息進行密碼驗證,成功後,會在調用Protocol::send_ok發一個認證成功網絡包,這個過程能夠在圖中的第二步所有體現。底層socket通訊代碼主要集中在sql/net_serv.cc中,具體而言讀採用接口my_net_read,寫採用接口my_net_write。
問題
(1).Unix socket方式登錄與TCP方式登錄有什麼區別和聯繫?
Unix socket是實現進程間通訊的一種方式,mysql支持利用Unix socket來實現客戶端-服務端的通訊,但要求客戶端和服務端在同一臺機器上。對於unix socket而言,一樣也是一種套接字,監聽線程會同時監聽TCP socket和Unix socket,接受到請求而後處理,後續的處理邏輯都是一致的,只不過底層通訊方式不同罷了。
mysql -h127.0.0.1 –P3306 –uxxx –pxxx [TCP通信方式] mysql -uxxx –pxxx –S/usr/mysql/mysql.sock [unix socket通訊方式]
(2).監聽socket是否與通訊socket公用一個端口?
咱們知道,服務端一直有一個監聽socket在3306端口監聽,等待新進來的客戶請求,一旦一個請求過來,服務端會從新建立一個新的通訊socket,這個新的socket專門用於與這個客戶通訊,而監聽socket則繼續監聽。雖然是2個套接字,但監聽socket和通訊socket都是同一個端口,經過netstat能夠確認這個問題。
(3).鏈接超時參數connect_timeout在什麼時候做用?
這個參數實質就是在MYSQL認證過程起做用,若是在這個過程當中,客戶端超過connect_timeout時間仍然沒有發送密碼認證包過來,則會主動斷開鏈接。
參考文檔
http://www.pythian.com/blog/what-is-an-unauthenticated-user/