返回顶部

2 - UDP

UDP介绍

UDP --- 用户数据报协议(User Datagram Protocol),是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中,"写信""

UDP特点:

UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。 UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

【适用情况】

UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如

  • 语音广播
  • 视频
  • QQ
  • TFTP(简单文件传送)
  • SNMP(简单网络管理协议)
  • RIP(路由信息协议,如报告股票市场,航空信息)
  • DNS(域名解释)

注重速度流畅

UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

 

socket简介

1. 什么是socket

我们已经了解了计算机网络通讯的过程,但是我们如何用程序来实现这个过程呢?实际上对于传输层及其一下的网络层的实现,操作系统的内核(即操作系统的最核心功能程序)已经帮我们实现了这些机制流程,并且封装成了一套工具供我们使用,这套工具我们称之为socket(套接字)。socket的中文意思是接插件。

 

 

 

2. 创建socket

在 Python 中 使用socket 模块的类 socket 就可以完成:

 

socket.socket(AddressFamily, Type)

说明:

函数 socket.socket 创建一个 socket,返回该 socket 的描述符,该函数带有两个参数:

  • Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
  • Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)

创建一个tcp socket(tcp套接字)

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print 'Socket Created'

创建一个udp socket(udp套接字)

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

print 'Socket Created'

3. 标识网络通信的参数

首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!

在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。

其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。

这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

udp程序流程

udp服务端

import socket

# 1. 创建套接字
server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

#2. bind绑定ip地址和端口,为元祖tuple类型
# ip如果不指明,表示本机的任何一个ip地址
server_addr = ("", 8080)
server_sock.bind(server_addr)

while True:
    # recv方法接收发送过来的数据
    # 返回值为接收到的数据,参数(这里为1024)表示本次收取数据的最大字节数
    # receive_data = server_sock.recv(1024)
    # recvfrom与recv方法类似,不同的是可以将发送数据的客户端的地址也返回
    receive_data, client_addr = server_sock.recvfrom(1024)
    # 注意python3中收到的数据receive_data是bytes类型
    # print(client_addr, ": ", receive_data)
    # 将bytes数据转换为字符串类型
    msg = receive_data.decode("utf-8")
    # 将收到的数据显示输出
    print(client_addr, ": ", msg)
    # 我们假定如果客户端发送了quit,我们就关闭服务端的套接字(即关闭服务端)
    if msg == "quit":
        server_sock.close()
        break
View Code

测试

我们暂时还没有写udp客户端,可以用nc命令来作为客户端进行测试。

# -u 表示使用udp协议
# nc -u 服务器ip 服务器端口
nc -u 127.0.0.1 8080

udp客户端

import socket

# 1. 创建套接字
client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 服务器地址
server_addr = ('127.0.0.1', 8080)

data = input("请输入要发送的内容:")
# 只要用户输入的数据不为空,就向服务器端发送
while data:
    # 2. 使用sendto方法向服务器发送数据
    # sendto(bytes类型要发送的数据, 对方的地址)
    client_sock.sendto(data.encode("utf-8"), server_addr)
    data = input("请输入要发送的内容:")

# 当用户输入的数据为空("")时, 关闭客户端套接字
client_sock.close()
View Code

测试

服务端与客户端的程序我们都已完成,可以同时开启进行测试。

我们也可以用nc充当udf服务端来单独测试客户端程序。

# -l 表示作为服务端开启,进行监听listen
# -u 表示使用udp协议
# nc -lu 绑定的服务器ip地址 端口
nc -lu 127.0.0.1 8080

udp客户端的端口

我们并没有为客户端bind绑定一个端口,操作系统可以为我们随机分配一个可用的端口用来发送数据,这样就不会因为需要绑定的端口被占用而导致程序无法运行的情况。

客户端服务端傻傻分不清

在双方的网络通讯中,客户端与服务端的角色并不是划分的很决定,任何一方都可以发送数据,也可以接收数据。

实现一个简易的DNS服务器模型

DNS服务器端

import socket

server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

server_addr = ("", 8053)
server_sock.bind(server_addr)

# 用来存放域名与ip对应关系的字典
domain_ip = {
    "www.itcast.cn": "192.168.1.2",
    "www.itheima.com": "192.168.1.3",
    "www.google.com": "192.168.1.4",
}

while True:
    # 接收用户要查询的域名
    receive_data, client_addr = server_sock.recvfrom(1024)
    domain = receive_data.decode("utf-8")
    print(client_addr, ": ", domain)
    # 从字典中获取对应域名的ip地址
    ip = domain_ip.get(domain, "i do not know")
    # 将ip地址返回给客户端
    server_sock.sendto(ip.encode("utf-8"), client_addr)
View Code

客户端

import socket

client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8053)
domain = input("请输入要查询的域名:")
while domain:
    # 向服务端发送要查询的域名
    client_sock.sendto(domain.encode("utf-8"), server_addr)
    # 接收服务端发送过来的ip信息
    ip = client_sock.recv(1024)
    print(ip.decode("utf-8"))
    domain = input("请输入要查询的域名:")

client_sock.close()
View Code

udp广播

现实生活中的广播

网络编程中的广播

import socket

# 创建udp套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 告诉系统内核刚创建的套接字用来进行广播
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
data = input("请输入要广播的内容:")
while data:
    # 注意对于广播对象地址的设置
    # <broadcast>表示广播地址
    s.sendto(data.encode("utf-8"), ("<broadcast>", 8080))
    data = input("请输入要广播的内容:")

s.close()
View Code

udp总结

1. udp是传输层的一种协议,不需要进行连接就可以用来发送和接收数据,但不保证数据的可靠传输。

2. udp服务器、客户端

  • udp的服务器和客户端的区分:往往是通过请求服务提供服务来进行区分
  • 请求服务的一方称为:客户端
  • 提供服务的一方称为:服务器

3. udp绑定问题

  • 一般情况下,服务器端,需要绑定端口,目的是为了让其他的客户端能够正确发送到此进程
  • 客户端,一般不需要绑定,而是让操作系统随机分配,这样就不会因为需要绑定的端口被占用而导致程序无法运行的情况

  

  

  

  

 

posted @ 2017-12-08 18:57  Crazymagic  阅读(156)  评论(0编辑  收藏  举报