举例:用迅雷下载东西,下载前会在本地先建一个文件,下载完把这个文件改名。客户端下载东西时,从服务器上收进一部分数据write进刚才的文件中,收一部分再write进去,当把所有的数据都write完之后意味着下载完成。实质就是抄作业。
下载有两部分:
服务器是发送方,客户端是下载方。
.
客户端下载流程:
1、下载东西要有网络,肯定有socket套接字
2、从服务器下载要connet
3、如何让服务器知道下载的内容,把文件名传给服务器send
4、服务器把文件中的内容读出来发给客户端,客户端通过recv收入数据放到文件中完成下载
with知识讲解:
f = open(“xxx”)#此处不关心用什么方式打开
f.write()或者f.read()
f.close()
以上,如果打开的时候没有异常,但是在读取时出现异常,在except里面处理f.close(),需要用try把excpt扩起来,但文件读写比较常见,try except比较复杂。
即代码如下:
f = open("xxx") # 此处不关心用什么方式打开
try:
f.write()或者f.read()
except:
f.close()
以上代码可以换成一句话:
(with的一大特点,不用写close,即使读写时出现异常,也会调用close)
with open("xxxx") as f:
f.write()或者f.read()
流程搭建框架
def main():
#1、创建套接字
#2、绑定服务器ip port
#3、链接服务器
#4、获取下载的文件名字
#5、将文件名字发送给服务器
#6、接收文件中的数据
#7、保存接收到是数据到一个文件中
#8、关闭套接字
if __name__ == "__main__":
main()
补全代码
代码解析:
.
with open("[新]"+download_file_name,“wb”) as f:
… f.write(recv_data)
.
一个字符串+一个字符串表示拼接,即第一个参数是文件名
.
第二个“w”表示以w方式打开,"wb"表示二进制模式,因为recv到的数据是二进制的,直接加b 扔进来不用解码编码
.
f.write(recv_data)表示将recv_data内容写进来
import socket
def main():
#1、创建套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、获取服务器ip port
dest_ip = input("请输入要下载服务器的ip:")
dest_port = int(input("请输入要下载服务器的port:"))
#3、链接服务器
#connect后面的参数是一个元组
tcp_socket.connect((dest_ip,dest_port))
#4、获取下载的文件名字
download_file_name = input("请输入要下载的文件的名字:")
#5、将文件名字发送给服务器
tcp_socket.send(download_file_name.encode("utf-8"))
#6、接收文件中的数据
recv_data = tcp_socket.recv(1024) # 1024=1K,1024*1024=1M,1024*1024*1024=1G
#7、保存接收到是数据到一个文件中
with open("[新]"+download_file_name,"wb") as f:
f.write(recv_data)
#8、关闭套接字
tcp_socket.close()
if __name__ == "__main__":
main()
测试后没问题 ,编写服务器的代码。
vim 09-文件下载-server.py
复制之前服务器的代码直接改
cp 05-tcp服务器.py 09文件下载-server.py**
代码解析:
#5、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode(“utf-8”)
print(“客户端(s%)需要下载的文件是:s%” % (str(client_addr),file_name))
decode与recv连写进行解码,file_name此时就是一个真正的字符串了
对发过来的客户端地址和请求的文件名进行格式化打印出来
import socket
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
#5、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#6、给客户端发送文件数据
new_client_socket.send("hahahh".encode("utf-8"))
#7、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
然后可以用08-文件下载-client.py客户端程序和刚刚写的09-文件下载-server.py服务器程序进行测试,可以同时打开两个终端进行跑程序
刚刚服务器回传给客户端的下载文件数据是一个测试的字符串hahaha,下面要传送真正的文件过去
思路:
找一个变量file_content替代hahaha,只要file_content是从文件中读取出来的数据就可以了。
import socket
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
#5、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#6、给客户端发送文件数据
#new_client_socket.send("hahahh".encode("utf-8"))
new_client_socket.send(file_content)
#7、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
思路:
此时会发现代码变的有点复杂,所以新定义一个函数send_file_2_client,开发过程中to
一般用2来代替。
然后把接收客户发送过来的文件名和回送数据给客户端代码剪切到新定义的函数下面
观察新函数,用到了套接字,new_client_socket,所以最下方在调用send_file_2_client这个新函数的时候,要把new_client_socket传过去,同时client_addr也需要传过去,同时形参也要去定义的新函数中补充完整。
。
单独再新建一个函数的原因:具体独立的功能,获取要下载的文件名,发送下载数据给客户端
import socket
def send_file_2_client(new_client_socket,client_addr):
#5、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#6、给客户端发送文件数据
#new_client_socket.send("hahahh".encode("utf-8"))
new_client_socket.send(file_content)
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
send_file_2_client(new_client_socket,client_addr)
#7、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
对新建的send_file_2_client函数进一步完善
思路:
首先要明确有哪几个步骤,再完善代码:
1、接收客户端发过来的要下载的文件名
2、打开这个文件,读取数据
3、给客户端发送文件数据1和3代码已经有了,完善第2步代码打开这个文件,读取数据 ,此时用with就不合适了,with是保证打开文件的前提下,如果读写出了问题可以正常调用close,但是如果文件根本打不开,程序也会崩掉,with试用于当open中有w,w意味着写,新建一个文件,新建文件打开不会失败。之前用with是在客户端中,将从服务器读取的文件写到一个新文件中,现在是从服务器下载文件首先进行读取,读取的这个文件是有内容的,但这个文件不一定存在,所以打开的时候不一定成功,此时用try
第2步的代码解读:
try:
f = open(file_name,“rb”)
file_content = f.read()
f.close()
except Exception as ret:
.
.
f = open(file_name,“rb”),r是读取,rb读取出来直接转换成二进制,不用进行解码
f.read()是读取内容,然后用变量file_content来接收
except Exception as ret:正常写的时候应该先看一下错误类型,但此刻只有一种异常,就是文件打不开,所以直接写Exception
import socket
def send_file_2_client(new_client_socket,client_addr):
#1、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#2、打开这个文件,读取数据
try:
f = open(file_name,"rb")
file_content = f.read()
f.close()
except Exception as ret:
print("没有要下载的文件:s%" % file_name)
#3、给客户端发送文件数据
#new_client_socket.send("hahahh".encode("utf-8"))
new_client_socket.send(file_content)
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
send_file_2_client(new_client_socket,client_addr)
#7、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
如果服务器中能打开文件,那么就给客户端发送数据,如果文件打不开,就不发送,为了完美,增加一步判断服务器读取的文件中是否有内容。
进一步完善代码:
思路:
先定义一个变量file_content =None,在读取前定义,如果文件能打开,能读取出内容,file_content就会有内容,如果文件打不开,file_content =None;所以要在第3步发送数据前做一个判断 if file_content
import socket
def send_file_2_client(new_client_socket,client_addr):
#1、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#2、打开这个文件,读取数据
try:
f = open(file_name,"rb")
file_content = f.read()
f.close()
except Exception as ret:
print("没有要下载的文件:s%" % file_name)
#3、给客户端发送文件数据
if file_content:
#new_client_socket.send("hahahh".encode("utf-8"))
new_client_socket.send(file_content)
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
#5、调用函数给客户端发送文件数据
send_file_2_client(new_client_socket,client_addr)
#6、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
为了将来程序能为多个客户端服务,需要进行while True
import socket
def send_file_2_client(new_client_socket,client_addr):
#1、接收客户端发过来的要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端(s%)需要下载的文件是:s%" % (str(client_addr),file_name))
#2、打开这个文件,读取数据
try:
f = open(file_name,"rb")
file_content = f.read()
f.close()
except Exception as ret:
print("没有要下载的文件:s%" % file_name)
#3、给客户端发送文件数据
if file_content:
#new_client_socket.send("hahahh".encode("utf-8"))
new_client_socket.send(file_content)
def main():
#1、买手机(创建套接字)
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、插手机卡(绑定 bind ip port)
tcp_server_socket.bind(("",7890))
#3、设置成响铃状态(将套接字由主动变为被动 listen)
tcp_server_socket.listen(128)
while True:
#4、等待别人打进来(等待客户链接 accept)
new_client_socket,client_addr = tcp_server_socket.accept()
#5、调用函数给客户端发送文件数据
send_file_2_client(new_client_socket,client_addr)
#6、关闭套接字
new_client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
然后用刚刚写的客户端和服务器进行相互测试,发现服务器没有问题,但是如果客户端下载一个服务器中没有的文件时,客户端还是会下载一个【新】XXX.py文件,但是这个文件打开后是空的。所以要调试客户端的代码,
vim 08-文件下载-client.py 调试代码
vim 08-文件下载-client.py 增加if判断,如果有数据才执行第7步“保存接收到是数据到一个文件中”,即: with
open("[新]"+download_file_name,“wb”) as f: f.write(recv_data) 否则就不执行
import socket
def main():
#1、创建套接字
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、获取服务器ip port
dest_ip = input("请输入要下载服务器的ip:")
dest_port = int(input("请输入要下载服务器的port:"))
#3、链接服务器
#connect后面的参数是一个元组
tcp_socket.connect((dest_ip,dest_port))
#4、获取下载的文件名字
download_file_name = input("请输入要下载的文件的名字:")
#5、将文件名字发送给服务器
tcp_socket.send(download_file_name.encode("utf-8"))
#6、接收文件中的数据
recv_data = tcp_socket.recv(1024) # 1024=1K,1024*1024=1M,1024*1024*1024=1G
if recv_data:
#7、保存接收到是数据到一个文件中
with open("[新]"+download_file_name,"wb") as f:
f.write(recv_data)
#8、关闭套接字
tcp_socket.close()
if __name__ == "__main__":
main()