Python网络编程笔记(一):UDP

2018-05-31  本文已影响207人  郝开心信札

IP层需要解决两个问题:

  1. 通过多路复用,区分不同应用程序的数据包
  2. 通过可靠传输,修复错误

两个主要协议:用户数据包协议(UDP)、传输控制协议(TCP),UDP用端口多路复用,解决问题一。TCP可以同时解决这两个问题。

UDP

不得不坦率的承认,我们不太可能在自己的任何一个应用程序中使用UDP,如果认为UDP适用于某个应用,不妨了解一下消息队列。

端口号

一共16位,0-65536之间

套接字socket

UDP丢包可能由于网络媒介,或者网络繁忙,重发请求应该采取指数退避

请求ID是解决重复响应的好方法。重复响应问题是我们收到所有数据包后又收到一个被认为丢失的响应,此时可能误解这个是当前请求的响应。随机选择请求ID可以预防最简单的电子欺骗(spoofing)

区分服务端绑定bind()和客户端连接connnec(),绑定指定要使用的端口,而连接限制了客户端可以接受的响应。

运行在不同服务器上的UDP代码

argparse是用来处理arg的包

#!/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)
    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 as exc:
            delay *= 2  # wait even longer for the next request
            if delay > 2.0:
                raise RuntimeError('I think the server is down') from exc
        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)

绑定接口

服务器bind()绑定接口时,可以使用:

UDP分组

MTU:最大传输单元、最大数据包容量,以太网和无线网卡一般MTU为1500B

较大数据包会被分解,可以查看系统MTU

// import IN 已经被py3.6弃用了。。更新为手动写
class IN:
    IP_MTU = 14
    IP_MTU_DISCOVER = 10
    IP_PMTUDISC_DO = 2

sock.setsockopt(socket.IPPRPTO_IP, IN.IP_MTU_DISCOVER, IN.IP_PMTUDISC_DO)

套接字选项

getsockopt()setsockopt()来获取和设置,第一个参数为所属选项组,第二个参数为要设置的选项名

比如设置广播:

s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

其他设置是否经过网关SO_DONTROUTE,判断套接字类型(UDP/TCP)SO_TYPE

广播

设置允许广播后,还需要客户端目标地址改为广播地址,用ifconfig查看,或者使用python中的特殊广播地址符<broadcast>, 如果使用普通地址,效果和未设置广播一样。

何时使用UDP

当我们发现有一种UDP协议能够适用于应用程序时,我们很可能已经重新实现了TCP。不过,实现的很糟糕。

上一篇下一篇

猜你喜欢

热点阅读