当前位置:   article > 正文

计算机网络,python语言实现基于socket的网络编程(三)_基于socket的网络程序设计

基于socket的网络程序设计

目录

演示

检查文件上传功能

检查文件下载功能

传输模式

app中的客户端

接收网页数据

客户端发送数据

客户端接受数据

服务器部分

服务器的启动

服务器的发送文件        

写在后面的一些思考        


**********源代码在文末**********        

 这是计网期末大作业——Python socket编程系列的最后一篇文章,接着上一篇的内容这一篇介绍如何将前后端串联起来,实现基于TCP连接的网页版文件传输。

         这是代码结构,可以看到一共有三个数据库,分别是客户端用来发送文件的client_db,接受文件的client_db_download和服务器用于保存接受的文件serveer_db(因为这里是同一台电脑,服务器发送的数据就从客户端发送文件的数据库中取就可以了)

下面进行演示:

首先是初始状态,网页APP和服务器都没启动,三个数据库也都是空的

启动服务器并打开网页 

 

可以看到网页中也显示服务器的数据是空的,那么接下来就先上传一张图片,一个视频 

检查文件上传功能

 然后点击上传

 网页显示上传成功

 再看后端消息,显示客户端已经接收保存到client_db并发送给服务器了

 另一边服务器也已经成功接收文件了

 最后再查看client_db和server_db中是否有文件

 

检查文件下载功能

首先检查网页的文件下载,下面我指定了等会要保存的路径,里面没有我之前上传的文件。

 

现在点击网页下载

下载图片

 图片下载成功

 下载视频

 视频也下载成功

 

那么上面就是网页下载的功能没有问题,下面检查服务器下载功能。

 然后我们点击服务器下载,先下载图片,再下载视频

 再是视频

 

然后检查一下后端的情况,先看服务器,没有问题

 再看看客户端接收情况,也没有问题

 最后检查client_download_db中是否下载了相应的图片和视频

         可以看到整个运行过程非常流畅,只要服务器开着,那么客户端可以一直上传文件并下载。那么接下来就介绍如何实现前后端的通信。

传输模式

        我采用的文件传输模式是:首先开启服务器,然后再打开app的网页,此时就可以在网页中进行文件的传输和下载了。而在后端的逻辑如下:

        1. 先接收网页传过来的文件并保存到客户端的数据库。同时,利用一个info列表存储一共需要传输哪些文件,需要传输的用True,不传输的用False。然后开启客户端,连接服务器,并首先将info传输给服务器告知等会服务器需要接收哪些文件。随后再依次判断是否要传输图片、TXT文件和视频,有的话就传输,没有就不传输。在一切文件传输完毕后,就关闭该客户端的套接字。

        2. 服务器端在开启监听后,等待客户端发送来的连接请求并建立用于传输数据的连接。然后第一个接收的就是info变量,并从中得知需要接收哪些文件或者传送哪些文件。随后根据需要接收的文件调用相应的保存方法或发送方法,把客户端传输过来的数据保存到服务器的数据库或者从对应的数据库取数据发送给客户端。在所有数据接收完毕或发送完毕后,服务器会依次关闭传输数据的连接和服务器自身。然后再重新创建新的服务器,开启监听......(整体被嵌套在一个循环中)

app中的客户端

        在app.py的处理传输过来的文件的视图函数process()中有接收网页传过来的文件的部分,以及创建客户端并传输文件给服务器的部分。

接收网页数据

  1. @app.route('/process', methods=['POST'])
  2. def process():
  3. # 接收上传来的文件并保存
  4. is_img, is_video, is_file = False, False, False
  5. if request.method == 'POST':
  6. img = request.files['img']
  7. if img:
  8. is_img = True
  9. img_name = img.filename # 接收图片名称
  10. img_type = img_name.split('.')[-1] # 图片类型
  11. img_path = os.path.join(r'E:\PycharmProjects\Web项目\ComputerNetworkProgramming\app\static\client_db\img',
  12. img_name)
  13. print(img_name)
  14. store(img_name, img_type, img_path) # 存到数据库
  15. img.save(img_path)
  16. print(f'{img_name}已保存至客户端数据库!')
  17. if request.method == 'POST':
  18. video = request.files['video']
  19. if video:
  20. is_video = True
  21. video_name = video.filename # 接收图片名称
  22. video_type = video_name.split('.')[-1]
  23. video_path = os.path.join(r'E:\PycharmProjects\Web项目\ComputerNetworkProgramming\app\static\client_db\video',
  24. video_name)
  25. store(video_name, video_type, video_path)
  26. video.save(video_path)
  27. print(f"{video_name}已保存至客户端数据库!")
  28. if request.method == 'POST':
  29. file = request.files['file']
  30. if file:
  31. is_file = True
  32. file_name = file.filename # 接收图片名称
  33. file_type = file_name.split('.')[-1]
  34. file_path = os.path.join(r'E:\PycharmProjects\Web项目\ComputerNetworkProgramming\app\static\client_db\file',
  35. file_name)
  36. store(file_name, file_type, file_path)
  37. file.save(file_path)
  38. print(f"{file_name}已保存至客户端数据库")

        在视图函数process()中,每次有文件被传输后,这边就会进行判断网页一共传输了哪些类型的文件(图片、TXT文件、视频),如果有文件传输过来,就会保存文件的名称,类型和要被保存的路径,并把这些信息写入到MySQL中(用于网页上的文件下载),同时把文件本身保存到客户端的数据库client_db中。

        另外,每次判断是否有文件时,都会保存一个变量is_xxx,这是后面客户端要传输的info的内容,指明该类型的文件是否需要被传输。

客户端发送数据

  1. # 启动客户端,向服务器发送连接请求
  2. client = SendMessage()
  3. # 客户端发送数据
  4. info = [is_img, is_file, is_video]
  5. client.send_info(info)
  6. if is_img:
  7. client.send_img(img_path)
  8. if is_file:
  9. client.send_txt(file_path)
  10. if is_video:
  11. client.send_video(video_path)
  12. client.close()
  13. return "上传成功"

        完成网页数据的接收后,就该启动客户端并传输文件了。我们先创建一个客户端的套接字用于连接服务器,然后发送info告知服务器准备接收哪些类型的文件。随后再把数据发送过去,最后关闭客户端,完成一次客户端的文件上传。

客户端接受数据

        客户端接受文件的方法和服务器接收文件的方法大同小异,因为两者采用的发送和接收规则都一样,所以具有很强的适应性和灵活性。

        这里客户端也是先接收文件的大小msg_length,然后把它作为参数传入recv()方法,从而将整个文件完整的接收。之后再把文件的信息fileinfo接收过来,判断是哪种类型的文件以及文件的名称,从而保存到对应的位置中去。

        当然,如果没有收到数据,则反馈下载文件失败。

服务器部分

        服务器包括接受文件(之前讲过了就不说了)、服务器发送文件(重点)、服务器的启动(和之前比有改变)

服务器的启动

        首先需要对之前的服务器代码进行部分修改,主要是构造器和start()方法的修改

  1. def __init__(self):
  2. """启动服务器"""
  3. self.FORMAT = 'utf-8'
  4. self.HEADER = 64 # 与客户端约定好的,第一条信息(会告知数据的总长)的长度
  5. self.PORT = 8888
  6. self.SERVER = socket.gethostbyname(socket.gethostname())
  7. self.ADDR = (self.SERVER, self.PORT)

        在构造器中没有直接创建服务器,而是先定义了创建服务器所需要的一些信息,包括服务器的ip地址和端口,然后将它们合并成一个元组,方便后面创建服务器的套接字时使用。

  1. def start(self):
  2. """接收一轮数据"""
  3. self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建服务器的套接字
  4. self.server.bind(self.ADDR) # 将套接字绑定到某一个ip地址和端口
  5. self.server.listen() # 在指定端口上进行监听,接收来自客户端的套接字的连接请求
  6. print(f"[LISTENING] Listing on {self.SERVER}")
  7. # 接收请求,同客户端建立连接
  8. conn, addr = self.server.accept() # conn为新的套接字,用于同客户端之间传输数据
  9. print("**********服务器已接收请求,TCP连接建立成功!**********")
  10. info_len = int(conn.recv(self.HEADER))
  11. content = conn.recv(info_len).decode(self.FORMAT)
  12. info = content.split(' ')
  13. # 服务器接收数据
  14. if "True" in info or "False" in info:
  15. # 判断要接收哪些数据
  16. is_img = True if info[0] == "True" else False
  17. is_file = True if info[1] == 'True' else False
  18. is_video = True if info[2] == 'True' else False
  19. # 接收数据
  20. if is_img:
  21. self.handle_img(conn, addr)
  22. print("图片接收完毕 ,已保存到服务器数据库...")
  23. if is_file:
  24. self.handle_txt(conn, addr)
  25. print("文件接收完毕,已保存到服务器数据库...")
  26. if is_video:
  27. self.handle_video(conn, addr)
  28. print("视频接收完毕,已保存到服务器数据库...")
  29. conn.close()
  30. self.server.close() # 关闭服务器
  31. print('完成一次文件接收')
  32. # 服务器传送数据
  33. else:
  34. path = content
  35. data = path.split(os.path.sep)
  36. filename = data[-1]
  37. filetype = data[-2]
  38. print(filetype)
  39. print(filename)
  40. self.send(conn, path, filetype + ' ' + filename)
  41. conn.close()
  42. self.server.close() # 关闭服务器
  43. print('完成一次文件传送')

        首先我创建服务器的套接字server,并且指定网络类型的套接字AF_INET,再指定面向连接的稳定数据传输,即TCP协议。随后调用bind()函数将套接字绑定在前面指定的地址ADDR上,并开启监听把该套接字设置为被动方式以便接收客户端的连接请求,也就使得这个套接字称为了服务器的套接字。

        开启监听以后,就调用 accept()方法,以便把远地客户进程(客户端)发来的连接请求提取出来,这里返回值包括两个,一个是用于传输数据的套接字conn,另一个则是客户端的IP地址和熟知端口号addr。

        当接收到来自客户端的连接并成功建立连接以后,首先调用recv()方法提取客户端发送的info,这里的info是我为了让服务器区分是要接收客户端的文件,还是要向客户端传送文件。如果info的长度为1则说明是客户端要下载文件;如果info全是布尔值“True”“False”,则表明客户端要传输文件了。随后根据判断的结果执行不同的操作:

  1. 如果是客户端传输文件给服务器,则服务器根据布尔值判断需要接收哪些类型的文件并调用对应的方法进行接收,当所有类型的文件都处理完毕后,服务器关闭conn和自己的server套接字。表示完成了一次数据的接收工作
  2. 如果是客户端要下载服务器中的文件,则服务器根据文件的地址获得文件的信息fileinfo,并把地址和fileinfo作为另外两个参数传给send()方法,由服务器调用send()方法将文件传给客户端。之后再关闭conn和server套接字,表示完成一次数据的传送工作。

服务器的发送文件        

        这里服务器的send()方法一共需要三个参数,分别是用于传输数据的连接的套接字conn,和前面发送文件的conn是一样的,另外两个参数path和fileinfo则是由客户端发送过来的,具体如何传输会在后面介绍客户端时进行讲解。这里只需要知道path是数据库要传输给客户端的文件路径,fileinfo是文件的信息(包括文件名称和文件类型)。

        然后先将文件的信息转换成二进制并获得其长度,将长度也转换成二进制。之后根据文件的类型采用不同的打开方式,如果是图片或者视频的话则需要按照二进制文件的方式打开,之后按照和上面发送文件的方法一样把文件和文件长度,文件信息和文件信息的长度发送给客户端。


写在后面的一些思考        

        好啦,Python版本的基于socket的网络编程就此告一段落,代码部分后端其实还好,服务器和客户端的发送与接收方法都是一样的,所以写好了一个其他的只需要稍稍修改甚至照搬即可。

        需要思考的可能是什么时刻开启服务器,什么时候调用接收的方法什么时候调用发送的方法,这些是需要自己去思考的。怎么样模拟服务器与客户端之间的数据传输流程,这个其实不同的同学有不一样的理解,只要找到适合自己的思路并且可以实现就OK了。     

        最困惑我的是前端的一些设计,以及前后端的数据交互。这些才是最最耗费我时间的环节,不过通过这次作业,我也有了很多的收获,前后端的开发都积累了不少经验,所以说通过项目来学习确实是一个不错的方法,一定要把知识应用到实战中去,最好可以结合自己的兴趣,这样做起来会更有动力~

        这个系列的其他两篇文章在这里:

计算机网络,python语言实现基于socket的网络编程(一)_榕城候佳人的博客-CSDN博客计算机网络,python语言实现基于socket的网络编程(一)https://blog.csdn.net/weixin_62588253/article/details/128300228计算机网络,python语言实现基于socket的网络编程(二)_榕城候佳人的博客-CSDN博客计算机网络,python语言实现基于socket的网络编程(二)。上次是将服务器和客户端搭建好,实现了本地的文件传输。但是身份验证却没有实现,另外数据也缺少存储的地方,因此这次计划完成前端的登录验证功能和网页的文件传输下载以及连接数据库。https://blog.csdn.net/weixin_62588253/article/details/128434504想要源代码的小伙伴可以去这里取:

https://github.com/Messimeimei/ComputerNetworkProgramming

如果你喜欢这篇文章,或者觉得它对你有帮助的话,麻烦点个赞谢谢~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/143976
推荐阅读
相关标签
  

闽ICP备14008679号