前言
本系列博文《网络通信与服务器》主要内容:udp、tcp、tcp下载文件案例、多任务、http协议、网络通信等 。本篇博文主要讲解网络中的udp。
概念介绍
网络中重要概念:
网络通信方式有两种方式:udp, tcp
IP目的:用来标记网络上的一台电脑或其他设备。
IP传输数据(局域网)的流程如下:
端口分类
知名端口:
范围是从0到1023。
80端口分配给HTTP服务
21端口分配给FTP服务
动态端口:
范围是从1024-65535。
手机连的3G4G得ip是全世界唯一得,是公有ip,在家通过路由器间接上的网,分得ip, 连的是私有IP.
ip在网络上来标记一台电脑,port标记一个程序
一个ip在一个网络里不可以相同,一个port在一个电脑里,不可以相同。
端口是为了识别不同的应用程序而分配给不同的应用的。
同一个端口,同一个时刻,不允许使用两次,也就是不会给两个程序分配同一个端口号;一个程序对应分配一个端口号。
Socket的简单使用
1.TCP/IP协议
TCP/IP协议是Transmission Control Protocol/Internet Protocol的简写,即传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。
TCP/IP定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
2.Socket
socket又称套接字,应用程序通常通过套接字向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
白话说,socket就是两个节点为了互相通信,而在各自家里装的一部电话。
TCP/IP各层之间传输数据的过程,如图:
增加Socket(在应用层和传输层之间)之后传输数据的过程, 如图:
family是协议族,默认AF_INET是IPv4,AF_INET6是IPv6;
type是套接字类型,包括TCP和UDP两种,默认SOCK_STREAM是流式套接字,属于TCP,SOCK_DGRAM是数据报套接字,属于UDP。
socket的简单使用:
socket的使用过程:
- 创建套接字
- 使用套接字收/发数据
- 关闭套接字
代码示例:
import socket
#创建套接字
s = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
#使用套接字收发数据
pass
#关闭套接字
s.close()
udp通信模型
udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中“写信”。(udp几乎不用)如下图所示:
代码示例讲解
01-socket的基本使用
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
02-发送任意数据给windows中调试助手
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
# udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
03-循环发送数据
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
# 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
# udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
04-带有退出功能的循环发送
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
# 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 如果输入的数据是exit,那么就退出程序
if send_data == "exit":
break
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
# udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
05-绑定端口用来接收数据
import socket
def main():
# 1. 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定一个本地信息,如果一个网络程序不绑定,则系统会随机分配
localaddr = ("", 7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip
udp_socket.bind(localaddr)
# 3. 接收数据
recv_data = udp_socket.recvfrom(1024)
# 4. 打印接收到的数据
print(recv_data)
# 5. 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
06-解析出接收到的数据
import socket
def main():
# 1. 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr) # 必须绑定自己电脑的ip以及port,其他的不行
# 3. 接收数据
recv_data = udp_socket.recvfrom(1024)
# recv_data这个变量中存储的是一个元组(接收到的数据,(发送方的ip, port))
recv_msg = recv_data[0] # 存储接收的数据
send_addr = recv_data[1] # 存储发送方的地址信息
# 4. 打印接收到的数据
# print(recv_data)
# print("%s:%s" % (str(send_addr), recv_msg.decode("utf-8")))
print("%s:%s" % (str(send_addr), recv_msg.decode("gbk")))
# 5. 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
07-循环接收并显示数据
import socket
def main():
# 1. 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定一个本地信息
localaddr = ("", 7788)
udp_socket.bind(localaddr) # 必须绑定自己电脑的ip以及port,其他的不行
# 3. 接收数据
while True:
recv_data = udp_socket.recvfrom(1024)
# recv_data这个变量中存储的是一个元组(接收到的数据,(发送方的ip, port))
recv_msg = recv_data[0] # 存储接收的数据
send_addr = recv_data[1] # 存储发送方的地址信息
# 4. 打印接收到的数据
# print(recv_data)
# print("%s:%s" % (str(send_addr), recv_msg.decode("utf-8")))
print("%s:%s" % (str(send_addr), recv_msg.decode("gbk")))
# 5. 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
08-先绑定端口然后在循环发送
udp_socket.bind((ip, port))
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地信息
udp_socket.bind(("", 7890))
while True:
# 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
# udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080))
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
09-使用同一个套接字进行收发数据
模板:
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地信息
# 端口绑定问题:如果程序运行时,没有绑定端口,那么操作系统会自动分配一个端口给程序;
udp_socket.bind((ip, port))
# 可以使用套接字收发数据
udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port)) # window "gbk"
# 接收回送过来的数据
recv_data = udp_socket.recvfrom(1024) # 如果recvfrom()的参数指定过小,超过发送的数据的大小,则会报错。记住一定要写数值
# 关闭套接字
udp_socket.close()
接收到的数据为元组形式,其中,
第一个值为接收到的数据;
第二个值为(发送方的IP,端口)。
import socket
def main():
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 获取对方的ip/port
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
# 从键盘获取数据
send_data = input("请输入要发送的数据:")
# 可以使用套接字收发数据
# udp_socket.sendto("hahahah", 对方的ip以及port)
# udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080))
# udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080))
udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
# 接收回送过来的数据
recv_data = udp_socket.recvfrom(1024)
# 套接字是一个可以同时 收发数据
print(recv_data)
# 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
应用–生成UDP聊天器
10-案例:udp聊天
import socket
def send_msg(udp_socket):
"""发送消息"""
# 获取要发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
send_data = input("请输入要发送的消息:")
udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
def recv_msg(udp_socket):
"""接收数据"""
recv_data = udp_socket.recvfrom(1024)
print("%s:%s" % (str(recv_data[1]), recv_data[0].decode("utf-8")))
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("", 7788))
# 循环循环来进行处理事情
while True:
# 发送
send_msg(udp_socket)
# 接收并显示
recv_msg(udp_socket)
if __name__ == "__main__":
main()
11-案例:udp聊天-升级-可以控制操作
终结版代码
import socket
def send_msg(udp_socket):
"""发送消息"""
# 获取要发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
send_data = input("请输入要发送的消息:")
udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
def recv_msg(udp_socket):
"""接收数据"""
recv_data = udp_socket.recvfrom(1024)
print("%s:%s" % (str(recv_data[1]), recv_data[0].decode("utf-8")))
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("", 7788))
# 循环循环来进行处理事情
while True:
print("-----xxx聊天器-----")
print("1:发送消息")
print("2:接收消息")
print("0:退出系统")
op = input("请输入功能:")
if op == "1":
# 发送
send_msg(udp_socket)
elif op == "2":
# 接收并显示
recv_msg(udp_socket)
elif op == "0":
break
else:
print("输入有误请重新输入...")
if __name__ == "__main__":
main()
总结
步骤:
- 创建套接字 套接字是可以同时收发数据的
- 发送数据
- 接收数据
接收到的数据为元组形式,其中,
第一个值为接收到的数据;
第二个值为(发送方的IP,端口)。
端口绑定问题:
如果程序运行时,没有绑定端口,那么操作系统会自动分配一个端口给程序;而且同一端口,不能用两次。
UDP收发数据的过程:
- 创建套接字
- 绑定本地信息(IP和端口)
- 接收数据(接受数据的时候,注意解码-将二进制转换过来)
- 显示或处理数据
- 发送数据(发送数据时,注意编码(变成二进制))
- 关闭套接字