TCP介紹:
TCP協定:傳輸控制協定,是一種面向連接配接的、可靠、基于位元組流的傳輸層的通信協定,有IETF的RFC 793定義
TCP通信需要經過建立連接配接、資料傳送、終止連接配接三個步驟:
TCP通信模型中,在通信開始之前,一定要先建立相關連結,才能發送資料,類似于生活中的“打電話”
特點:面向連接配接
通信雙方必須先建立連接配接才能進行資料的傳輸,雙方都必須為該連接配接配置設定必須的系統核心資源,以管理連接配接的狀态和連接配接上的傳輸
雙方間的資料傳輸都可以通過這一個連接配接進行
完成資料交換後,雙方必須斷開此連結,已釋放資源
UDP和TCP差別:
- udp不區分用戶端伺服器,tcp嚴格區分
- udp不能确定資料是否到達接收方,
- tcp會有一個逾時重傳,當給接受方發送消息消息,對方如果收到會給發送方回,表示以收到,發送方在一定的時間内,如果沒有收到回複就會逾時重傳

TCP采用發送應答機制:
TCP發送的每個封包段都必須得到接收方的應答才能認為這個TCP封包段傳輸成功
逾時重傳:
發送端發出一個封包段之後就啟動定時器,如果在定時器時間内沒有收到應答就會重新發送
錯誤校驗:
TCP用一個校驗和函數來檢驗資料是否有錯誤:在發送和接受時都要計算校驗和
浏覽控制和阻塞管理:
流量控制用來避免主機發送得過快而使接收方來不及完全收下
tcp伺服器:
import socket
def main():
# 建立套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 綁定端口bind
tcp_socket.bind(("",8899))
# 設定套接字預設主動為被動listen
tcp_socket.listen(128) # 表示同一時間可以有128個用戶端連結
while True:
print("等待伺服器連結.....")
# 等待用戶端連結accept(),然後傳回一個元組,元組第一個值是一個新的套接字,第二個值是用戶端的ip和端口
new_socket,client_addr = tcp_socket.accept()
print("%s伺服器鍊成功" % str(client_addr))
while True:
# 接受消息
recv_Data = new_socket.recv(1024)
print("ip位址是%s的用戶端,發來消息:%s" %(client_addr,recv_Data.decode('gbk')))
# 如果recv解堵塞,那麼有2中方式
# 用戶端發送過來資料
# 用戶端調用close導緻
# 是以這裡我們判斷用戶端發過來的資料要是為空表示用戶端調用close是以結束服務
if not recv_Data :
break
# 回複消息
new_socket.send("你好我是你爸爸".encode("gbk"))
print("結束服務")
# 關閉套接字
new_socket.close()
tcp_socket.close()
if __name__ == "__main__":
main()
tcp用戶端:
import socket
def main():
# 建立套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip = input("請輸入人ip位址:")
port = int(input("請輸入端口:"))
# 連結伺服器
tcp_socket.connect((ip,port))
while True:
# 發送消息
send_Data = input("輸入你需要發送的消息:")
if send_Data == "exit":
break
tcp_socket.send(send_Data.encode("gbk"))
# 接受消息
recv_data = tcp_socket.recv(1024)
print("接受伺服器發來的消息:%s" % recv_data.decode('gbk'))
# 關閉套接字
tcp_socket.close()
if __name__ == "__main__":
main()
tcp注意點
1.tcp伺服器一般情況下都需要綁定,否則用戶端找不到這個伺服器:
伺服器一般适用于接受請求,給人回複,如果不綁定用戶端連接配接不到
2.tcp用戶端一般不綁定,因為是主動連結伺服器,是以隻需要确定伺服器ip,port等資訊就好了,本地用戶端可以随機
3.tcp伺服器中通過listen可以将socket建立出來的主動套接字變為被動的,這是左tcp伺服器時必須需要做的
4.當用戶端需要連結伺服器是,就需要使用connect進行連結,udp是不需要連結的而是直接發送,但是tcp需要先,隻有連結成功才可以通信
5.當一個tcp用戶端連接配接伺服器時,伺服器端會有一個新的套接字,這個套接字用來标記這個用戶端,單獨為這個客戶服務
6.listen後的套接字是被動套接字,用來接收新用戶端連結請求的,而accept傳回的新套接字是标記這個新用戶端的
7.關閉listen後的套接字以為着被動套接字關閉了,會導緻新的用戶端不能連結伺服器,但是之前已經連結成功的用戶端正常通信
8.關閉accept傳回的套接字以為這個用戶端已經服務完畢
9.當用戶端的套接字調用colse後,服務端會recv解堵塞,并且傳回的長度為0,是以伺服器可以通過傳回的資料長度來差別伺服器是否已經下線
小案例:
模拟檔案下載下傳:
用戶端:
import socket
def main():
# 建立套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 請輸入下載下傳伺服器ip以及端口
ip = input("請輸入目标伺服器IP位址:")
port = int(input("請輸入伺服器端口:"))
# 連結伺服器
tcp_socket.connect((ip,port))
# 輸入檔案名
file_name = input('請輸入需要下載下傳的檔案:')
# 檔案名發給伺服器
tcp_socket.send(file_name.encode('gbk'))
# 接受伺服器發來的消息
recv_Data = tcp_socket.recv(1024)
if recv_Data:
# 儲存檔案
with open('[新]'+file_name,'wb') as f:
f.write(recv_Data)
# 關閉他套接字
tcp_socket.close()
if __name__ == "__main__":
main()
服務端:
import socket
def send_file_clinet(new_socket,client_addr):
# 接受用戶端需要下載下傳的檔案名:
recv_Data = new_socket.recv(1024)
print("ip位址是%s的用戶端,需要下載下傳的檔案:%s" %(client_addr,recv_Data.decode('gbk')))
file_content = None
# 打開這個檔案,讀取資料
try:
f = open(recv_Data,'rb')
file_content = f.read()
f.close()
except Exception as ret:
print("沒有要下載下傳的檔案(%s)" % ret)
if file_content:
new_socket.send(file_content)
def main():
# 建立套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 綁定端口bind
tcp_socket.bind(("",8890))
# 設定套接字預設主動為被動listen
tcp_socket.listen(128) # 表示同一時間可以有128個用戶端連結
while True:
print("等待伺服器連結.....")
# 等待用戶端連結accept(),然後傳回一個元組,元組第一個值是一個新的套接字,第二個值是用戶端的ip和端口
new_socket,client_addr = tcp_socket.accept()
print("%s伺服器鍊成功" % str(client_addr))
send_file_clinet(new_socket,client_addr)
print("結束服務")
# 關閉套接字
new_socket.close()
tcp_socket.close()
if __name__ == "__main__":
main()