Không đáng tin cậy, backoff, phong toả và timeouts
Vì client và server trong học phần trước cả hai chạy trên cùng một máy và nói chuyện qua
loopback interface của nó – cái không phải là một card mạng vật lí cái có thể trải qua trục
trặc tín hiệu – không có cách thức thực cái các gói dữ liệu có thể bị mất, và vì vậy bạn
không thực sự thấy sự không thuận tiện của UDP trong phần code học phần trước. Code trở
nên phức tạp hơn như thế nào khi các gói dữ liệu có thể thực sự bị mất?
Nhìn vào phần code dưới. Thay vì luôn trả lời các client requests, server này tuỳ ý chọn trả
lời chỉ một nửa các requests đi vào từ máy khách, cái sẽ cho phép bạn xem làm cách nào xây
dựng tính tin cậy vào client code mà không chờ cái gì có thể là hàng giờ cho một gói dữ liệu
bị bỏ thực xảy ra trên mạng của bạn!
UDP Server and Client on Different Machines
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter02/udp_remote.py
# UDP client and server for talking over the network
import argparse, random, socket, sys
MAX_BYTES = 65535
def server(interface, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((interface, port))
print(‘Listening at’, sock.getsockname())
while True:
data, address = sock.recvfrom(MAX_BYTES)
if random.random() < 0.5:
print(‘Pretending to drop packet from {}’.format(address))
continue
text = data.decode(‘ascii’)
print(‘The client at {} says {!r}’.format(address, text))
message = ‘Your data was {} bytes long’.format(len(data))
sock.sendto(message.encode(‘ascii’), address)
def client(hostname, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
hostname = sys.argv[2]
sock.connect((hostname, port))
print(‘Client socket name is {}’.format(sock.getsockname()))
delay = 0.1 # seconds
text = ‘This is another message’
data = text.encode(‘ascii’)
while True:
sock.send(data)
print(‘Waiting up to {} seconds for a reply’.format(delay))
sock.settimeout(delay)
try:
data = sock.recv(MAX_BYTES)
except socket.timeout:
delay *= 2 # wait even longer for the next request
if delay > 2.0:
raise RuntimeError(‘I think the server is down’)
else:
break # we are done, and can stop looping
print(‘The server says {!r}’.format(data.decode(‘ascii’)))
if __name__ == ‘__main__’:
choices = {‘client’: client, ‘server’: server}
parser = argparse.ArgumentParser(description=’Send and receive UDP,’
‘ pretending packets are often dropped’)
parser.add_argument(‘role’, choices=choices, help=’which role to take’)
parser.add_argument(‘host’, help=’interface the server listens at;’
‘host the client sends to’)
parser.add_argument(‘-p’, metavar=’PORT’, type=int, default=1060,
help=’UDP port (default 1060)’)
args = parser.parse_args()
function = choices[args.role]
function(args.host, args.p)
Trong khi server trong ví dụ trước thông báo hệ điều hành rằng nó muốn chỉ các gói dữ liệu, cái đi
đến từ các quá trình khác trên cùng một máy qua giao diện 127.0.0.1 private, bạn có thể làm server này
chung hơn bằng cách chỉ ra địa chỉ IP server như một chuỗi rỗng. Cái này có nghĩa bất cứ giao diện địa
phương nào, cái Linux laptop của tôi có ý định hỏi hệ điều hành cho địa chỉ IP 0.0.0.0.
$ python udp_remote.py server “”
Listening at (‘0.0.0.0’, 1060)
Mỗi lần một request được nhận, server sẽ sử dụng random() tung đồng xu để quyết định xem liệu request này
sẽ được trả lời để rằng bạn không phải giữ chạy client cả ngày trong khi chờ cho một gói dữ liệu bị bỏ thực.
Bất cứ quyết định nào nó thực hiện, nó in ra một thông điệp tới màn hình để rằng bạn có thể giữ với hoạt động
của nó.