1.網路七層模型及主要協議

2.TCP的“三次握手”和四次揮手
三次握手

Step1:首先客戶端向服務器端發送一段TCP報文;
Step 2:服務器端接收到來自客戶端的TCP報文之后,結束LISTEN階段,并回傳一段TCP報文;
Step 3:客戶端接收到來自服務器端的確認收到資料的TCP報文之后,明確了從客戶端到服務器的資料傳輸是正常的,結束SYN-SENT階段,并回傳最后一段TCP報文,
此后客戶端和服務器端進行正常的資料傳輸,
四次揮手

Step 1:首先客戶端想要釋放連接,向服務器端發送一段TCP報文;
Step 2:服務器端接收到從客戶端發出的TCP報文之后,確認了客戶端想要釋放連接,隨后服務器端結束ESTABLISHED階段,進入CLOSE-WAIT階段(半關閉狀態)并回傳一段TCP報文;
Step 3:服務器端自從發出ACK確認報文之后,經過CLOSED-WAIT階段,做好了釋放服務器端到客戶端方向上的連接準備,再次向客戶端發出一段TCP報文;
Step 4:客戶端收到從服務器端發出的TCP報文,確認了服務器端已做好釋放連接的準備,結束FIN-WAIT-2階段,進入TIME-WAIT階段,并向服務器端發送一段報文,
服務器端收到從客戶端發出的TCP報文之后結束LAST-ACK階段,進入CLOSED階段,由此正式確認關閉服務器端到客戶端方向上的連接,
3.socket
socket(簡稱 套接字)是行程間通信的一種方式,它與其它行程間通信的一個主要不同是:socket 可以實作不同機器間的行程通信,
下面是簡單的Case,幫助理解,
客戶端
from socket import socket, AF_INET, SOCK_STREAM ##表示創建一個客戶端的socket client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP; ##定義一個連接的目標 con_address = ('IP地址',埠號) ##告訴客戶端要連接的服務器的地址和埠號 client.connect(con_address) ##發送data client.send('python學習筆記'.encode('utf-8')) ##關閉 socket.close()
服務端,創建socket服務器
from socket import socket, AF_INET, SOCK_STREAM ##創建一個socket物件 server = socket(AF_INET, SOCK_STREAM) ##系結埠號 server.bind('',埠號) ##第一個引數為空字串時,表示本機的IP ##開啟監聽狀態 server.listen(5) ###引數為整型,表示訊息可堆積的數量, while true: socket, addr_info = server.accept() ##阻塞的,表示沒有連接的時候,一直等待,回傳值為socket 和 addr_info,wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For IP sockets ,the address info is a pair (hostaddr,port). ##列印下 print(socket, addr_info) ##讀取接受到的資訊 recv_data = https://www.cnblogs.com/xuliuzai/archive/2021/11/06/socket.recv(512).decode('utf-8') ##512是我們定義的bufsize.這個方法回傳的是bytes, print('{}發送過來的訊息是:{}' .format(addr_info[0] , recv_data)) ##關閉 socket.close()
4.客戶端+服務器 互動通信(單資訊交叉)
客戶端
from socket import socket, AF_INET, SOCK_STREAM ##表示創建一個客戶端的socket client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP; ##定義一個連接的目標 con_address = ('IP地址',埠號) ##告訴客戶端要連接的服務器的地址和埠號 client.connect(con_address) while True: msg = input('客戶端輸入:') client.send(msg.encode('utf-8')) if msg == 'byebye': break ##接受服務器端的msg recv_data = https://www.cnblogs.com/xuliuzai/archive/2021/11/06/socket.recv(512).decode('utf-8') ##512是我們定義的bufsize.這個方法回傳的是bytes, print('服務器端發送過來的訊息是:{}' .format(recv_data)) if recv_data =https://www.cnblogs.com/xuliuzai/archive/2021/11/06/= 'byebye': break ##關閉 socket.close()
服務端
from socket import socket, AF_INET, SOCK_STREAM ##創建一個socket物件 server = socket(AF_INET, SOCK_STREAM) ##系結埠號 server.bind('',埠號) ##第一個引數為空字串時,表示本機的IP ##開啟監聽狀態 server.listen(5) ###引數為整型,表示訊息可堆積的數量, while True: socket, addr_info = server.accept() ##阻塞的, while True: ###保證可以對多個客戶端,不能因為一個客戶端,關閉所有, ##讀取接受到的資訊 recv_data = https://www.cnblogs.com/xuliuzai/archive/2021/11/06/socket.recv(512).decode('utf-8') print('客戶端發送過來的訊息是:{}' .format(recv_data)) if recv_data =https://www.cnblogs.com/xuliuzai/archive/2021/11/06/= 'byebye': ##如果客戶端說byebye,就退出 break msg = input('服務器端輸入:') socket.send(msg.encode('utf-8')) if msg == 'byebye' : ##如果我們說了byebye,退出 break ##關閉 socket.close() print(addr_info,'離開了!')
5.借助執行緒,一方可以發送多個訊息
即客戶端與服務端之間的通信不必要限制為一來一回,一問一答
服務端
##服務器端,創建socket服務器 from socket import socket, AF_INET, SOCK_STREAM from threading import Thread ##創建一個socket物件 server = socket(AF_INET, SOCK_STREAM) ##系結埠號 server.bind('',埠號)##第一個引數為空字串時,表示本機的IP ##開啟監聽狀態 server.listen(5) ###引數為整型,表示訊息可堆積的數量 ##任務 def send_msg(socket) while True: msg = input('輸入要發送的訊息:') socket.send(msg.encode('utf-8')) def recv_msg(socket) while Ture:##可以持續收訊息 data=https://www.cnblogs.com/xuliuzai/archive/2021/11/06/socket.recv(512).decode('utf-8') if len(data)==0: break print('收到客戶端的訊息是',data) while True: socket, addr_info = server.accept() ##阻塞的 t_send = Thread(target=send_msg, args=(socket,)) t_recv = Thread(target=recv_msg, args=(socket,)) t_send.start() t_recv.start()
客戶端
from socket import socket, AF_INET, SOCK_STREAM from threading import Thread ##表示創建一個客戶端的socket client = socket(AF_INET, SOCK_STREAM) ##SOCK_STREAM --表示TCP; SOCK_DGRAM--表示UDP; ##定義一個連接的目標 con_address = ('IP地址',埠號) ##告訴客戶端要連接的服務器的地址和埠號 client.connect(con_address) ##任務 def send_msg(socket) while True: msg = input('輸入要發送的訊息:') socket.send(msg.encode('utf-8')) def recv_msg(socket) while Ture:##可以持續收訊息 data=https://www.cnblogs.com/xuliuzai/archive/2021/11/06/socket.recv(512).decode('utf-8') if len(data)==0: break print('收到服務器端的訊息是',data) t_send = Thread(target=send_msg, args=(client,)) t_recv = Thread(target=recv_msg, args=(client,)) t_send.start() t_recv.start()
6.web客戶端訪問
不寫專門的客戶端,借助web進行訪問,并且支持多個web同時訪問(通過協程實作),
web Server 的代碼(服務端)
''' cilent:瀏覽器客戶端 瀏覽器發出請求(request),Server端回傳回應(response), request 包含:request 行(里面有請求方法--get,協議--HTTP/1.1)、請求頭【鍵值對】、請求體(POST請求時的資料), response 包含:response 行(里面有協議--HTTP/1.1,狀態--例如200 ok)、回應頭【鍵值對】、回應體(資料), ''' ##服務器端,創建socket服務器 import gevent ##匯入協程的包 import gevent import monkey ##注意此時的socket 一定要來自gevent 的包,進行了繼承和封裝 ##from socket import socket, AF_INET, SOCK_STREAM from gevent import socket from threading import Thread monkey.patch_all() ##創建一個socket物件 server = socket.socket() ##系結埠號 server.bind('',埠號)---第一個引數為空字串時,表示本機的IP ##開啟監聽狀態 server.listen(5) ###引數為整型,表示訊息可堆積的數量 ##處理客戶端的訪問 def handle_client(socket) recv_data = socket.recv(512).decode('utf-8') print(recv_data) ##每次訪問回傳的資訊是 msg ='歡迎來此訪問!' ##格式化response,需要有回應行、回應頭 resp_line = 'HTTP/1.1 200 OK\r\n' ##\r\n格式有換行要求 resp_header = 'Content-Type:text/html\r\ncharset=utf-8\r\nServer:testServer\r\n' resp= resp_line + resp_header +'\r\n'+msg ##拼湊完整的回傳體,注意回傳體結束結尾需是兩個換行,添加一個 socket.send(resp.encode('utf-8')) socket.colse() ##聊天結束 while True: socket, addr_info = server.accept() print(addr_info,'請求訪問!') gevent.spawn(handle_cilent,socket)
7.socket 常用函式和方法的梳理
| 型別 | 函式或方法 | 解釋 |
|
服務端套接字函式 |
s.bind() |
系結(主機,埠號)到套接字 |
s.listen() |
開始TCP監聽 |
|
s.accept() |
被動接受TCP客戶的連接,(阻塞式)等待連接的到來 |
|
| 客戶端套接字函式 | s.connect() | 主動初始化TCP服務器連接 |
| s.connect_ex() | connect()函式的擴展版本,出錯時回傳出錯碼,而不是拋出例外 | |
| 公共用途的套接字函式 |
s.recv( | 收TCP資料 |
| s.send() | 發送TCP資料(send在待發送資料量大于己端快取區剩余空間時,資料丟失,不會發完) | |
| s.sendall() |
發送完整的TCP資料(本質就是回圈呼叫send,sendall在待發送資料量大于己端快取區剩余空間時,資料不丟失,回圈呼叫send直到發完) |
|
| s.recvfrom() | 接收UDP資料 | |
| s.sendto() | 發送UDP資料 | |
| s.getpeername() | 連接到當前套接字的遠端的地址 | |
| s.getsockname() | 當前套接字的地址 | |
| s.getsockopt() | 回傳指定套接字的引數 | |
| s.setsockopt() | 設定指定套接字的引數 | |
| s.close() | 關閉套接字 | |
| 面向鎖的套接字方法 | s.setblocking() | 設定套接字的阻塞與非阻塞模式 |
| s.settimeout() | 置阻塞套接字操作的超時時間 | |
| s.gettimeout() | 獲取阻塞套接字操作的超時時間 | |
| 面向檔案的套接字方法 | s.fileno() | 套接字的檔案描述符 |
| s.makefile() | 創建一個與該套接字相關的檔案 |
參考
1.TCP/IP協議(一)網路基礎知識 網路七層協議
https://www.cnblogs.com/wanghuaijun/p/10092930.html
2.計算機各層網路協議
https://www.cnblogs.com/weiliuyby/p/8030175.html
3.Python3之socket編程
https://www.cnblogs.com/zhangyingai/p/7097922.html
4.詳解 TCP 連接的“ 三次握手 ”與“ 四次揮手 ”
https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/350694.html
標籤:其他
