TCP 客户端与服务器之间建立连接需要进行三次握手 客户端--->服务器 服务器--->客户端 客户端--->服务器,这样做的好处是可以保证数据的完整缺点是慢.
- UDP 是用户数据报协议,传输模式是数据报,
- UDP 是无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
- UDP 是简单不可靠的协议,它不提供可靠性,只是把数据包发送出去,并不保证能够到达目的地。由于它不需要在客户端和服务端之间建立连接,也没有超时重发机制,所以传输速度很快。
UDP 不需要进行三次握手,客户端与服务器之间直接往对方脸上丢数据不管有没有接到,优点是快缺点是无法保证数据的完整.
UDP数据包一般包含512个字节,当大于512字节时,可能会出现传输不稳定的情况。
- UDP每个发送的数据都含所有自身的报头,不像TCP那样是流式的数据,所以不存在粘包现象。
- udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠。
- tcp 协议的数据不会丢,没有收完的包下一次会接着收取上一次没收完的数据,本端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
什么是socket:
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。socket只是一种连接模式,不是协议,tcp,udp,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
UDP 传输的流程图
第一步导入网络编程的库,自带的
代码语言:javascript复制import socket
接着就是要调用一个方法
代码语言:javascript复制socket.socket(AddressFamily, Type)
就解释一下参数,别的就不说了
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
前面的名字,按照自己的喜好来
代码语言:javascript复制import socket
# 创建udp socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ...
# 使用套接字收/发数据
# 关闭socket
udp_socket.close()
上面的代码就是对于UDP的一次完整的调用
套接字使用流程 与 文件的使用流程很类似:
1.创建套接字
2.使用套接字收/发数据
3.关闭套接字
代码语言:javascript复制#
# Tello Python3 Control Demo
#
import threading
import socket
import sys
import time
host = ''
port = 9000
locaddr = (host, port)
# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tello_address = ('192.168.10.1', 8889)
sock.bind(locaddr)
def recv():
count = 0
while True:
try:
data, server = sock.recvfrom(1518)
print(data.decode(encoding="utf-8"))
except Exception:
print('nExit . . .n')
break
print('rnrnTello Python3 Demo.rn')
print('Tello: command takeoff land flip forward back left right rn up down cw ccw speed speed?rn')
print('end -- quit demo.rn')
# recvThread create
recvThread = threading.Thread(target=recv)
recvThread.start()
while True:
try:
msg = input("")
if not msg:
break
if 'end' in msg:
print('...')
sock.close()
break
# Send data
msg = msg.encode(encoding="utf-8")
sent = sock.sendto(msg, tello_address)
except KeyboardInterrupt:
print('n . . .n')
sock.close()
break
我直接给出一个实例,这是连接Dji教育无人机的一段代码,很完整了已经
代码语言:javascript复制sock.bind(locaddr)
使用bind公开一个端口,使得client可以方便连接
给
创建了一个无人机的地址,IP 端口
可以看到上面的tello_address是作为一个参数的函数使用.
代码语言:javascript复制 # 发送数据
send_addr = ('192.168.92.190', 7878)
udp_socket.sendto(send_data.encode('GBK'), send_addr)
如果是字符集在两个目标上面不一样,还可以去更改编码模式
此时先下载一个调试工具,随便下载
代码语言:javascript复制import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 读取输入的数据
send_data = input('请输入要发送的数据:')
# 发送数据
send_addr = ('192.168.92.190', 7878)
udp_socket.sendto(send_data.encode('GBK'), send_addr)
# 关闭
udp_socket.close()
if __name__ == '__main__':
main()
代码语言:javascript复制http://www.zlmcu.com/document/tcp_debug_tools.html
我上面的软件没有调试出来,我换了一个软件,调试成功
这个地方用wireshark看一下
选择内部的回环接口
设置这里看见是BSD
这个地方输入三个1,a,b
位置信息
可以看到是这个地址确实发了三次信息
打开看看
数据31,长度1
可以用ifconfig看看自己的ip
代码语言:javascript复制import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定一个本地信息
localaddr = ("", 8080)
udp_socket.bind(localaddr)
# 接收数据
recv_data = udp_socket.recvfrom(1024)
# recv_data是一个元组(接收到的数据, (发送方的IP, port))
recv_msg = recv_data[0] # 存储接收到的数据
send_addr = recv_data[1] # 存储发送方的地址
# 打印收到的消息
# print(recv_data)
print("%s:%s" % (str(send_addr), recv_msg.decode('GBK')))
# 关闭
udp_socket.close()
if __name__ == '__main__':
main()
这段代码是接收代码,写的很明白
udp端口绑定
- 一个udp网络程序,运行时没有绑定端口,系统会给它分配一个随机的端口,如果再次运行,端口可能发生变化
- 一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
什么时候需要绑定端口呢?
一般来说,如果要做成一个服务端的应用程序的话,是需要绑定固定端口的。
python3中的编码转换
- 使用socket发送的是数据的二进制,需要将字符串转换成bytes
str.encode()
- 使用socket接收的是数据的二进制,需要将bytes转换成字符
bytes.decode()