注:本系列的环境是VScode+WSL,使用说明可参考 官方文档和 wsl配置anaconda的步骤。
我们在这一章的目的是建立一个UDP套接字,并尝试发送数据,首先了解一下TCP/IP协议族的基本框架:
IP地址和端口号
IP地址的目的是在网络层标记设备,端口号是在传输层标记应用程序/进程。
Linux中可通过ifconfig
查看IP地址和端口号,Windows中通过ipconfig
查看。
IP地址分为ipv4和ipv6,分别对应上面的inet和inet6。ipv4地址分为网络地址和主机地址,按照划分的位数不同可分为A~E共5类。我们也定义了一些私有IP,他们不能在公网中使用,范围为:
10.0.0.0~255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
私有IP经过NAT(Network Address Translator)路由器转换为全局IP地址才能完成和外界的通信,NAT和NAPT的知识详见《图解TCP/IP》第五章第六节。
端口号
简单讲,IP标记设备,端口号标记设备上的进程。
0~1023都是特殊端口号,需要root权限才能使用,80端口分配给HTTP服务,21端口分配给FTP服务;其余端口都是动态端口,1024~65535。
socket的创建
创建第一个套接字
import socket
socket.socket(socket.AddressFamily,type)
第一个参数控制是ipv4还是ipv6,分别对应socket.AF_INET
和socket.AF_INET6
第二个参数控制tcp还是udp,分别对应socket.SOCK_STREAM
和socket.SOCK_DGRAM
创建并使用套接字的流程和创建并打开文件的流程很像:
import socket
# 创建udp_socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 使用socket
#关闭socket
s.close()
创建udp_socket,并收发数据
需求: 创建udp_socket,向(“192.168.163.24”,8800)发送"hahaha"
#!coding=utf-8
import socket
def main():
# 1. 创建udp套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2. 使用套接字收发数据
dest_addr = ("192.168.163.24",8800)
udp_socket.sendto(b'hahaha',dest_addr)
# 3. 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
注意两点:
-
发送地址为目标IP和目标端口的元组,缺一不可
-
发送内容必须是bytes类型,不能是string类型。
对于string类型的数据,要么像上文这样添加
b
前缀,要么加上.encode('utf-8')
的后缀,如果是windows,需要用gbk编码。
改进:循环发送数据
#!coding=utf-8
import socket
def main():
# 1. 创建udp套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
# 2. 使用套接字收发数据
# 接收方
dest_addr = ("192.168.163.24",8800)
# 发送内容
send_content = input("要发送的内容")
# 发送数据
udp_socket.sendto(send_content.encode('utf-8'),dest_addr)
# 判断是否停止发送数据
if send_content=="exit":
break
# 3. 关闭套接字
udp_socket.close()
if __name__ == "__main__":
main()
绑定端口
local_addr = ('',7788)
udp_socket.bind(local_addr)
UDP socket发送数据的完整流程
- 创建套接字
- 绑定本地端口
- 发送数据
- 关闭套接字
简易版代码:
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定端口
local_addr = ('',7788)
udp_socket.bind(local_addr)
while True:
# 发送数据
dest_addr = ('192.168.22.33',9900)
send_content = input("要发送的内容:")
udp_socket.sendto(send_content.encode('utf-8'),dest_addr)
# 判断是否停止
if send_content == "exit":
break
udp_socket.close()
if __name__ == "__main__":
main()
可以将接收信息和发送信息分别写成子函数,实现半双工的UDP聊天室(UDP支持全双工的工作方式)
import socket
def send_msg(udp_socket):
"""发送信息"""
dest_addr = ('127.0.0.1',9900)
send_content = input("要发送的内容:")
udp_socket.sendto(send_content.encode('utf-8'),dest_addr)
def recv_msg(udp_socket):
"""接收信息"""
recv_data = udp_socket.recvfrom(1024)
recv_msg = recv_data[0].decode('utf-8')
source = str(recv_data[1])
return recv_msg,source
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定端口
local_addr = ('',7788)
udp_socket.bind(local_addr)
while True:
# 发送数据
send_msg(udp_socket)
# 接收信息
recv_content,source_ip = recv_msg(udp_socket)
print("接收来自%s的信息:%s"%(source_ip,recv_content))
# 判断是否停止
if recv_content == "exit":
break
udp_socket.close()
if __name__ == "__main__":
main()
加入功能选项:
#coding=utf-8
import socket
def send_msg(udp_socket):
"""发送消息"""
send_data = input("输入要发送的内容")
dest_ip = input("请输入dest_ip: ")
dest_port = input("请输入dest_port: ")
send_addr = (dest_ip,int(dest_port))
udp_socket.sendto(send_data.encode('utf-8'),send_addr)
def recv_msg(udp_socket):
recv_data = udp_socket.recvfrom(1024)
recv_msg = recv_data[0].decode('utf-8')
source = str(recv_data[1])
return recv_msg,source
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定端口号
port_addr = ("",7999)
udp_socket.bind(port_addr)
while True:
print("----xxx聊天器-------")
print("1:发送数据\n2:接收消息\n3:退出系统")
op = int(input("输入功能选项"))
if op == 1:
# 发送数据
send_msg(udp_socket)
elif op == 2:
# 接收数据
recv_msg,source_ip = recv_msg(udp_socket)
print("接收来自%s的信息:%s" % (source_ip,recv_msg))
elif op ==3:
# 关闭套接字
print("关闭套接字")
udp_socket.close()
else:
print("输入有误,重新输入")
if __name__=="__main__":
main()