TCP
- TCP協(xié)議:傳輸控制協(xié)議是一種面向連接的宛裕、可靠的、基于字節(jié)流的傳輸層通信協(xié)議
TCP通信面向連接需要經(jīng)過創(chuàng)建連接续滋、數(shù)據(jù)傳送、終止連接三個步驟疲酌。
TCP可靠傳輸
- 采用發(fā)送應(yīng)答機制
- 超時重傳
- 錯誤校驗
- 流量控制和阻塞管理
TCP與UDP的不同點
- tcp面向連接(確認有創(chuàng)建三次握手,連接已創(chuàng)建才作操作)朗恳,udp面向數(shù)據(jù)報
- tcp有序數(shù)據(jù)傳輸
- 重發(fā)丟失的數(shù)據(jù)包
- 舍棄重復(fù)的數(shù)據(jù)包
- 無差錯的數(shù)據(jù)傳輸
- 阻塞/流量控制
TCP與UDP通信模型應(yīng)用
- UDP:在通信開始之前,不需要建立相關(guān)的鏈接粥诫,只需要發(fā)送數(shù)據(jù)即可
- TCP:在通信開始之前,一定要先建立相關(guān)的鏈接怀浆,才能發(fā)送數(shù)據(jù)
TCP客戶端及服務(wù)端構(gòu)建
所謂的服務(wù)器端:就是提供服務(wù)的一方,?客?端怕享,就是需要被服務(wù)的一方
# 客戶端
1.創(chuàng)建socket套接字
2.使用connect連接到服務(wù)器
3.使用send/recv發(fā)送和接收信息
4.關(guān)閉socket套接字
# 服務(wù)器
1.創(chuàng)建socket套接字
2.bind綁定ip和端口
3.使用listen使套接字變?yōu)榭梢员粍犹捉幼?4.accept取出一個客戶端連接用以服務(wù)
5.recv/send接收發(fā)送數(shù)據(jù)
6.關(guān)閉客戶端套接字client
7.關(guān)閉server套接字(服務(wù)器一般不關(guān)閉)
tcp注意點
- tcp服務(wù)器一般情況下都需要綁定,否則客戶端找不到這個服務(wù)器
- tcp客戶端一般不綁定函筋,因為是主動鏈接服務(wù)器,所以只要確定好服務(wù)器的ip跌帐、port等信息就好绊率,本地客戶端可以隨 機
- tcp服務(wù)器中通過listen可以將socket創(chuàng)建出來的主動套接字變?yōu)楸粍拥木柯模@是做tcp服務(wù)器時必須要做的
- 當(dāng)客戶端需要鏈接服務(wù)器時,就需要使用connect進行鏈接挎袜,udp是不需要鏈接的而是直接發(fā)送顽聂,但是tcp必須先鏈 接盯仪,只有鏈接成功才能通信
- 當(dāng)一個tcp客戶端連接服務(wù)器時,服務(wù)器端會有1個新的套接字全景,這個套接字用來標(biāo)記這個客戶端,單獨為這個客戶 端服務(wù)
- listen后的套接字是被動套接字爸黄,用來接收新的客戶端的連接請求的,而accept返回的新套接字是標(biāo)識這個新客戶端 的
- 關(guān)閉listen后的套接字意味著被動套接字關(guān)閉了炕贵,會導(dǎo)致新的客戶端不能夠鏈接服務(wù)器,但是之前已經(jīng)鏈接成功的客 戶端正常通信
- 關(guān)閉accept返回的套接字意味著這個客戶端已經(jīng)服務(wù)完畢
- 當(dāng)客戶端的套接字調(diào)用close后称开,服務(wù)器端會recv解阻塞,并且返回的長度為0鳖轰,因此服務(wù)器可以通過返回數(shù)據(jù)的長 度來區(qū)別客戶端是否已經(jīng)下線;同理 當(dāng)服務(wù)器斷開tcp連接的時候 客戶端同樣也會收到0字節(jié)數(shù)據(jù)
三次握手和四次揮手
三次握手原理
客戶端(client) 服務(wù)器(server)
SYN seq=x
SYN_SENT(connect()) -------------------------------------> LISTEN(listen())
SYN seq=y,ACK=x+1
ESTABLISHED <-------------------------------------------- SYN_RCVD
ACK=y+1
-------------------------------------------------> ESTABLISHED
- connect()本質(zhì)就是發(fā)起與服務(wù)器的三次握手
- 調(diào)用lisent之后的套接字蕴侣,才能收到客戶端的請求
- listen函數(shù)的參數(shù):不同系統(tǒng)意義不同;Linux2.6之后listen函數(shù)表示已完成三次握手隊列常去(隊列常去表示存放的客戶端數(shù)量)昆雀,其他系統(tǒng)一般表示已完成和未完成隊列的總和
四次揮手原理
客戶端(client) 服務(wù)器(server)
FIN seq=x+2,ACK=y+1
FIN_WAIT_1(close())-------------------------------------------------> CLOSE_WAIT
ACK x+3
FIN_WAIT_2 <---------------------------------------------------------- CLOSE_WAIT
FIN seq= y+1
TIME_WAIT <-----------------------------------------------------------LAST_ACK(close())
ACK=y+2
TIME_WAIT -------------------------------------------------------------->
地址重用問題
server_socket.setsockopt(socket.SOl_SOCKET, socket.SO_REUSEADDR,1)(1.代表設(shè)置, 0.代表取消)
協(xié)議
- 定義:解決不同種族人之間的語言溝通障礙狞膘,而做出的規(guī)定,就是協(xié)議
- tcp/ip協(xié)議(簇):互聯(lián)網(wǎng)協(xié)議包含了上百種協(xié)議標(biāo)準客冈,但是最重要的兩個協(xié)議TCP協(xié)議和IP協(xié)議稳强,所以大家把互聯(lián)網(wǎng)的協(xié)議簡稱為TCP/IP協(xié)議(簇)
TCP實現(xiàn)客戶端和服務(wù)端文件下載器
# 客戶端
import socket
import os
# 創(chuàng)建tcp套接字
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 創(chuàng)建和服務(wù)器的連接
ip = input("文件下載服務(wù)器ip:")
port = int(input("輸入文件下載服務(wù)器對應(yīng)端口:"))
tcp_socket.connect((ip, port))
# 發(fā)送下載文件名
file_name = input("請輸入需要下載的文件名:")
tcp_socket.send(file_name.encode("gbk"))
# 結(jié)合文件數(shù)據(jù) --> 保存在本地寫入文件-->二進制模式就是原封不動的寫入
file = open("new"+file_name, "wb")
write_len = 0
while True:
data = tcp_socket.recv(4096)
if data:
file.write(data)
write_len += len(data)
else:
file.close()
# 關(guān)閉套接字
tcp_socket.close()
if write_len > 0:
print("文件傳輸完成")
else:
# 空文件
os.remove("new"+file_name)
print("文件不存在")
break
# 服務(wù)端
import socket
def read_data(file_name):
"""獲取文件內(nèi)容"""
try:
f = open(file_name, "rb")
except Exception as e:
print("%s文件不存在" % file_name)
else:
# 文件太大回報錯
ret = f.read()
return ret
# 創(chuàng)建一個服務(wù)器套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 設(shè)置套接字選項 地址重用 # 重用地址 1.代表設(shè)置 0.代表取消設(shè)置
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定端口
server_socket.bind(("", 8888))
# 將套接字設(shè)置為被動套接字 監(jiān)聽
server_socket.listen(128)
while True:
# 接收一個客戶端 開始對他進行服務(wù)
client_socket, client_address = server_socket.accept()
print("接收到%s的請求" % str(client_address))
# 取出文件名
file_name = client_socket.recv(4096)
# 根據(jù)文件名讀取文件數(shù)據(jù)-->通過客戶端關(guān)聯(lián)的套接字發(fā)送文件數(shù)據(jù)
file_ct = read_data(file_name.decode("gbk"))
# 發(fā)送完成 應(yīng)該主動斷開客戶端關(guān)聯(lián)套接字
if file_ct:
client_socket.send(file_ct)
# 關(guān)閉套接字
client_socket.close()
# server_socket.close()