一個男生在追一個女生,在女生未同意前,男生是不會做其它事情的,我們可以稱之為阻塞(就像本文的題圖一樣)

兩名舔狗同時碰面鞠躬以示舔功到位,兩位舔狗都在等對方先站起來自己再戰起來,結果就是誰都不會站起來,我們可以稱之為死鎖(就像下面這張圖一樣)

下面我們可以用代碼來實現阻塞

server.py

import socket
import time

def recvall(sock, length):
data = b
while len(data) < length:
more = sock.recv(length - len(data))
data += more #直到收到的數據達到length的值才會返回
return data

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((localhost, 5000))
sock.listen(1)
while True:
sc, sockname = sock.accept()
print(I have accepted a connection from, sockname)
print(Socket name: , sc.getsockname())
print(Socket peer: , sc.getpeername())
message = recvall(sc, 64)
print(Recevied info is , repr(message))
sc.sendall(bResponse from server)
sc.close()

邏輯上是沒有任何問題的,但是問題來了,如果客戶端發出的數據小於length的值怎麼辦?

block-client.py

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((localhost, 5000))
sock.send(bResquest from client) #長度小於64
response = sock.recv(64)
sock.close()
print(response)
print(Test for blocking)

執行結果

為什麼會發生這種事情?主要是因為server.py中的recvall函數

def recvall(sock, length):
data = b
while len(data) < length:
more = sock.recv(length - len(data))
data += more #直到收到的數據達到length的值才會返回
return data

這個循環代表著,如果收到的數據小於length那麼它就會一直等著,直到收到長度為length的數據,才會進行下一步操作

但是客戶端總共才送出了length < 30 的數據

那麼服務端會便會進入死循環狀態,在讀取數據未完成前,是不會進行寫操作的,即阻塞

在解釋死鎖前,先了解一下TCP建立連接後的工作流程

在建立連接後,分別在客戶端和服務端建立兩個緩衝區,分別為OutputInput

客服端傳送數據,服務端接收數據(執行完後緩衝區會被清理)

服務端處理數據,並發送數據,客戶端接收數據(執行完後緩衝區會被清理)

完成,進入下一次循環

那麼為什麼形成死鎖呢?

  1. 服務端或者客服端不能同時執行兩個操作,即寫的時候不能讀,讀的時候不能寫
  2. 緩衝區被佔滿

想像一下緩衝區被佔滿的情況

假定緩衝區設定為100KB,那麼假定客戶端會傳送220KB的數據,這時候clientOutput buffer會被佔滿,而serverInput Buffer是從 clientOutput buffer複製的數據因此也會被佔滿

server先從Input Buffer拿到10KB的數據,並返回220KB的數據給client,這時候serverOutput Buffer會被佔滿,而clientInput Buffer是從serverOutput Buffer複製的數據因此也會被佔滿

這時候server開始著急了,要趕緊把Output Buffer的值先送出去才能進行下一次請求,而client這時候不僅不幫忙,還對server說:Hi,哥們,我送出的數據你還沒處理呢

你看,server既想把數據先送出去,又要處理client的請求,那乾脆什麼都不做算了!同時也給client帶去了麻煩

與此同時,client也著急了,想趕緊把Output Buffer的值送出去,但是server想:哎呀,我可不能讓client這麼輕鬆,給它點數據玩玩吧

這樣client既想先把數據送出去,又要讀取server給的數據,太忙了!不幹了!

這樣就行成了死鎖

下面代碼來源於,因為在這裡只是當個示例,因此不會解釋代碼

Python網路編程第三版?

github.com

tcp-deadlock.py

import argparse, socket, sys

def server(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(1)
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:
data = sc.recv(1024)
if not data:
break
output = data.decode(ascii).upper().encode(ascii)
sc.sendall(output) # send it back uppercase
n += len(data)
print(
%d bytes processed so far % (n,), end= )
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 # round up to a multiple of 16
message = bcapitalize this! # 16-byte message to repeat over and over

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(
%d bytes sent % (sent,), end= )

print()

print(Receiving all the data the server sends back)

received = 0
while True:
data = sock.recv(42)
if not received:
print( The first data received says, repr(data))
if not data:
break
received += len(data)
print(
%d bytes received % (received,), end= )

print()
sock.close()

if __name__ == __main__:
roles = (client, server)
parser = argparse.ArgumentParser(description=Get deadlocked over TCP)
parser.add_argument(role, choices=roles, 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()
if args.role == client:
client(args.host, args.p, args.bytecount)#set data length
else:
server(args.host, args.p)

當我們傳輸的數據很小時

這時候很快就完成了交互工作

當我們傳輸的數據很大時

BOMB!死鎖工作完成

參考文獻:

[1]關於死鎖的圖就是根據該鏈接所畫,並且該鏈接對死鎖的解釋很清楚

TCP deadlock problems - OmniMark Concept?

florin.bjdean.id.au
圖標

[2]第三章TCP關於死鎖的章節

Python網路編程(第3版)?

book.douban.com

[3]書的源碼

brandon-rhodes/fopnp?

github.com圖標
推薦閱讀:

查看原文 >>
相关文章