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 = 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 = socket.recv(512).decode('utf-8') ##512是我們定義的bufsize.這個方法返回的是bytes。 print('服務器端發送過來的消息是:{}' .format(recv_data)) if recv_data == '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 = socket.recv(512).decode('utf-8') print('客戶端發送過來的消息是:{}' .format(recv_data)) if recv_data == '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=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=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