02_tcp_deadlock
# 這個程式我們是測試客戶端和服務端在進行通訊的過程中,可能會產生死鎖的情況。
# 這是因為緩衝區,和TCP協議的可靠性連接導致的。
# 在程式中我們可以看到,客戶端先向服務端發送數據,然後服務端就收之後再發送給客戶端。
# 注意這裡我們可以看到,程式設置的是不能緩衝區滿就立即發送出去。
# 那麼我們可以考慮一下,如果客戶端需要發送的位元組數比較小,那麼是能夠正常的通訊的,
# 因為小於緩衝區的大小,不會把緩衝區填滿。
# 再來考慮客戶端發送數據很大的情況比如說1個G。
# 從流程上邊來看,客戶端的一條資訊就會發送出去,而不是等著緩衝區滿。
# 服務端那邊也是這樣的,接收到數據之後直接就發送,此時就產生問題了,因為要發送的數據是很大的,
# 客戶端的數據沒有發送完成,就無法調用recv接收服務端穿過來的數據,那從服務端看,服務端發送的數據
# 客戶端沒有接收,TCP 就判斷網路不好了,就會不讓服務端發送位元組。同時服務端也不接受位元組,就會導致
# 客戶端也發送不出去。這樣就導致死鎖了。
# 導入模組
import argparse,socket,sys
def server(host,port,bytecount):
# 創建一個服務端的套接字。
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 設置套接字
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 套接字綁定IP,埠
sock.bind((host,port))
# 設置監聽的數量為1
sock.listen(1)
# 列印出服務端的IP,埠
print("Listening at ",sock.getsockname())
while True:
# 服務端的套接字連接上客戶端的套接字
sc,sockname = sock.accept()
print("Processing up to 1024 bytes at a time from",sockname)
# 定義一個變數,用來計算一共接收了多少位元組。
n = 0
# 死循環,一直接收客戶端發過來的字母
while True:
# 每次接收從最大客戶端傳過來的1024個位元組,然後賦給data變數
data = sc.recv(1024)
# 約定一個結束符
if data == b'efo' or data == b'':break
# 將接收的二進位位元組進行解碼,然後轉換成大寫字母,在進行編碼。
output = data.decode('ascii').upper().encode('ascii')
# 服務端發送消息給客戶端。
sc.sendall(output)
# 計算接收到的位元組長度
n += len(data)
print("接收的數據量為:",n)
# 強行刷新緩衝區。
sys.stdout.flush()
print('結束了')
# 回收套接字。
sc.close()
print(' Socket closed')
def client(host,port,bytecount):
# 定義一個套接字,同上邊服務端。
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
bytecount = (bytecount + 15) // 16 * 16
print("bytecount",bytecount)
# 定義一個十六個位元組的字元串
message = b'capitalize this'
print('Sending ',bytecount,'bytes of data,in chunks of 16 bytes')
# 連接服務端。
sock.connect((host,port))
# 定義發送數量
sent = 0
while sent <= bytecount :
# 客戶端發送消息
sock.sendall(message)
sent += len(message)
print("發送了多少數據:",sent)
# 強行刷新緩衝區
sys.stdout.flush()
# 最後發送一個結束符,告訴服務端發送結束了。
sock.sendall(b'efo')
print("結束了")
# 單向的socket便稱為半開放Socket。要實現半開放式,需要用到shutdown()函數。
sock.shutdown(socket.SHUT_WR)
print('Receving all the data the server sends back')
# 定義接收到的位元組變數
received = 0
while True:
# 用來接收服務端傳回來的消息
data = sock.recv(42)
if not received :
# 列印第一次進來,接收到的數據
print(" The firse data received says",repr(data))
if not data:
break
# 計算接收到位元組數的總和。
received += len(data)
print("接收的數據量為:",received)
print('結束了')
sock.close()
if __name__ == "__main__":
# 定義一個字典
choices = {'client':client,'server':server}
parser = argparse.ArgumentParser(description = 'Get deadlocked over TCP')
parser.add_argument('role',choices = choices,help = 'which role to play')
parser.add_argument('host',help = 'interface the server listens at;'
' host the client sends to')
parser.add_argument("bytecount",type = int,nargs ='?',default = 16,
help = 'number of bytes for client to send (default 16)')
parser.add_argument('-p',metavar = "PORT",type = int,default = 1060,help = "TCP port (default 1060")
args = parser.parse_args()
function = choices[args.role]
function(args.host,args.p,args.bytecount)