思想:先模仿程序再创造
1. 02案例:udp聊天器.py
终端中:vim 02案例:udp聊天器.py
打开终端开始写代码,写代码的先后顺序如下代码块:
函数与函数直接空两行,函数与if之间空两行或者一行
def main():
if __name__ == "__main__":
main()
按下i开始进入编辑模式,main函数一般是完成主体功能
思路:大体要完成的功能:
1、发数据
2、收数据并打印出来
具体要完成多少次,不清楚,用while true,把收发数据放到里面去,即如下代码:
def main():
while True:
# 发数据
# 收数据并打印
if __name__ == "__main__":
main()
如果流程比较复杂的时候,用注释理思路就不用写序号了,因为可能会改来改去比较麻烦
思路:
写一段流程写一段代码,然后往下推,如果正推无法推进,再倒推
若要完成收发功能,需要套接字,此时需要创建套接字,并且要import
书写规范:
第一个函数和前面的内容也要空两个行
注释#后要空一格
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 用循环来处理事情
while True:
# 发数据
# 收数据并打印
if __name__ == "__main__":
main()
思路:
如果想要收数据,需要绑定一个固定端口,绑定信息是一个元组,本地ip用“”表示,指定绑定的端口号假设叫7788
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("",7788))
# 用循环来处理事情
while True:
# 发数据
# 收数据并打印
if __name__ == "__main__":
main()
思路:
接下来就是收发数据,若要发送数据,想要获取发送的内容,用input获取,用变量send_data来接收
发送数据的代码:udp_socket.sendto(内容要转码,(对方的ip即dest_ip,对方的端口即dest_port))
此时发现需要dest_ip和dest_port两个变量,再去发送数据代码上方定义变量
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("",7788))
# 用循环来处理事情
while True:
# 发数据
#获取发送的内容
send_data = input("请输入你要发送的内容:")
dest_ip = input("请输入对方的ip:")
dest_port = input("请输入对方的端口:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
# 收数据并打印
if __name__ == "__main__":
main()
思路:
按照正常运行起来的流程顺序,获取对方的ip和port是在获取发送内容前,去调整代码顺序。
命令:
Ese ,大V选中,按上下键调整选中区域,d 剪切 把光标挪到这行前面去,p 粘贴
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("",7788))
# 用循环来处理事情
while True:
# 发数据
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = input("请输入对方的端口:")
send_data = input("请输入你要发送的内容:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
# 收数据并打印
if __name__ == "__main__":
main()
思路:接下来完成收数据并打印的代码
书写规范:
print(“s%?%” % (str(recv_data[1]),recv_data[0].decode(“utf-8”)))
%和“”不要连在一起 ,中间加一个空格,%和后面的小括号之间也隔一个空格
如果是和windows之间通信,解码时应把utf-8换成gbk,与mac和linux通信都是utf-8
recv_data[0].decode,因为元组中的第一部分才是数据,所以要加[0]
import socket
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 绑定信息
udp_socket.bind(("",7788))
# 用循环来处理事情
while True:
# 发数据
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = input("请输入对方的端口:")
send_data = input("请输入你要发送的内容:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
# 收数据并打印
recv_data = udp_socket.recvfrom(1024)
print("s%:s%" % (str(recv_data[1]),recv_data[0].decode("utf-8")))
if __name__ == "__main__":
main()
至此,以上代码已经完成了收发功能
一般具有独立功能的代码块都要封装到一个函数中,下面将收、发代码块分别进行封装
书写规范:
函数下方“”“ ”“”,三个双引号,代表是对函数的说明文档
命令:esc 大V 上下箭头选中发送数据代码块,d 剪切,光标到新定义的函数下方,p 黏贴
新调整过去的代码,缩进的太多,V选中,小于号,向左调整
import socket
def send_msg():
"""发送消息"""
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = input("请输入对方的端口:")
send_data = input("请输入你要发送的内容:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
def recv_msg():
"""接受消息"""
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:
# 发数据
# 收数据并打印
if __name__ == "__main__":
main()
代码块封装完成后,接下来是调用函数
思路:
每次调用函数之前,都要想一下,函数是否需要参数;
虽然上面的def send_msg()中没有参数,但是代码还没进行测试
看一下def send_msg()中的代码,dest_ip、 dest_port 、send_data
都是定义的变量,但是udp_socket.sendto(send_data.encode(“utf-8”),(dest_ip,dest_port))这句代码中的udp_socket还没有定义,所以调用函数时要将udp_socket传进去,实参形参都要补进去,同理,接收数据的函数也需要传递形参和实参
命令:
编辑模式下,u是撤销
import socket
def send_msg(udp_socket):
"""发送消息"""
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = input("请输入对方的端口:")
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_data(udp_socket)
if __name__ == "__main__":
main()
接下来测试程序
程序运行时报错了
查看最后一行错误类型和最近出错的代码行数是第10行
命令:
调整报错代码,终端中vim 文件名.py后面跟一个空格 +10 回车 打开文件后光标直接定位到第10行
出错原因是第8行的端口忘记转成数字类型
import socket
def send_msg(udp_socket):
"""发送消息"""
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的端口:"))
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_data(udp_socket)
if __name__ == "__main__":
main()
2. 03案例:udp聊天器-升级-可以控制操作.py
因为现有程序必须是一收一发,如果对方不发数据过来,程序就会阻塞,所以要升级
终端中cp 02案例:udp聊天器.py 03案例:udp聊天器-升级-可以控制操作.py
import socket
def send_msg(udp_socket):
"""发送消息"""
#获取发送的内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的端口:"))
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(“---聊天器---”)
print(“1、发送消息”)
print(“2、接收消息”)
print(“0、退出”)
op = input("请输入你想进行的操作:")
if op == “1”:
# 发数据
send_msg(udp_socket)
elif op == "2":
# 收数据并打印
recv_data(udp_socket)
elif op == "0":
break
else:
print("你输入的指令有误,请重新输入:")
if __name__ == "__main__":
main()
再对代码进行测试时,会发现,只要每次输入2之后,才会把数据收进来,即使对方已经发送了数据,如果中断中不输入2,数据也不会收进来,说明,发送过来的数据会事先存在操作系统这个管家中,什么时候输入2,什么时候把数据取出来。
漏洞:
如果不用while True,操作系统收入太多数据时,程序有可能会卡死。如果知道对方电脑的端口号,一次性发很大的文件,有可能就会导致对方电脑崩掉,再有新的文件收不进去。