参考于: python cs 架构实现简单文件传输 https://www.jb51.net/article/144636.htm
原文中程序运行有误, 在此做修改与解释, 如下:
功能说明:
共 2 个 py 文件分别为 server 和 client 端, 采用 socket 进行通信. 提供两种功能:"dir" 命令用于显示服务器端可下载文件名;"get/put + 文件名" 命令用于与服务器端进行下载或发送数据交换.
Socket 解释
服务器上 socket 编写步骤:
1. 调用 socket 函数创建 socket 对象, 如:
socket_server = socket.socket(family,type)
family 参数代表地址家族, 可为 AF_INET(Internet 通信)或 AF_UNIX(同一台机器上进程间通信).
type 参数代表套接字类型, 可为 SOCK_STREAM(流套接字)和 SOCK_DGRAM(数据报套接字).
2. 使用 socket 对象的 bind 方法, 将 socket 绑定到指定地址, 如:
socket_server.bind(address)
由 AF_INET 所创建的套接字, address 必须是一组双元素元组, 格式为(host,port).
3. 使用 socket 对象的 listen 方法接受连接请求, 如:
socket_server.listen(backlog)
backlog 指定最多允许多少个客户连接到服务器, 至少为 1, 如果达到设定值, 则拒绝接受新请求.
4. 服务器套接字通过 socket 对象的 accept 方法等待客户请求一个连接, 如:
connection,address = socket_server.accept()
运行 accept 方法后, socket 进入 "waiting" 状态, 等待客户请求连接. 当客户请求连接时, accept 方法建立连接并返回服务器, 返回一组含两个元素的元组(connection,address).connection 是新的 socket 对象, 服务器必须通过这个新的对象与客户端进行通信; address 是客户端的 Internet 地址.
5. 处理阶段, 服务器和客户端通过 "send" 和 "recv" 方法通信:
服务器调用 send, 并采用 二进制形式向客户发送信息. send 方法返回已发送的字符个数.
服务器使用 recv 方法从客户端接受信息, 调用 recv 时, 服务器必须指定一个整数来表示可通过本次方法调用来接受的最大数据量. recv 方法在接收数据时会进入 "blocked" 状态, 最后返回一个字符串, 它表示收到的数据. 如果发送数据量超过 recv 所允许, 数据会被截断. 多余的数据将缓冲到接收端. 以后调用 recv 时, 多余的数据会从缓冲区删除.
6. 传输结束, 调用 socket 对象的 close 方法关闭连接, 如:
- connection.close()
- socket_server.close()
注意: 这里先关闭 accept 创建的新 socket(connection)然后关闭服务器端 socket(socket_server)
客户端上 socket 编写步骤:
1. 创建一个 socket 以连接服务器, 如:
socket_client = socket.socket(family,type)
2. 使用 connect 方法连接服务器, 对于 AF_INET 而言链接格式如:
socket_client.connect((host,port))
host 代表主服务器主机名或 IP,port 为服务器进程所绑定的端口号.
3. 处理阶段, 客户端和服务器端通过 send 与 recv 方法进行通信
4. 传输结束, 调用 close 方法关闭连接, 如:
socket_client.close()
注意这里的 socket_client 是第一步创建的对象, 而服务器端使用的是 accept 创建的新 socket(connection)
关于 TCP 的三次握手建立连接和四次挥手断开连接这里不做叙述(彻底讲解我功夫不到家, 不敢乱说), 把图转来大家看一下(这个感觉很基础, 但很重要):
三次握手
四次挥手
程序如下:
服务器端:
环境与服务器配置信息
接收文件方法
发送文件方法
处理命令 1(接收, 发送)方法
处理命令 2(显示可下载文件)方法
服务器端运行主体
客户端:
环境与服务器配置
接收文件方法
发送文件方法
确认服务器端消息方法
处理命令 1(接收, 发送)方法
处理命令 2(显示可下载文件)方法
客户端运行主体
运行说明:
1. 首先建立 server.py 和 client.py 文件, 并分别置于两个不同的位置, 如图:
Server 文件夹及其 py 文件
Client 文件夹及其 py 文件
在 Server 文件夹下放一个或多个文件用于下载(我这里是 4 个不同类型的文件以供下文 dir 指令调用)
在 Client 文件下放一个或多个文件用于上传(我这里是一个 Word 文本)
2. 在 Server 目录下运行 server.py, 等待客户端发送连接请求:
服务器端等待连接请求
3. 在 Client 目录下运行 client.py, 向服务器端发送连接请求:
客户端发送请求 & 服务器端连接成功
注意: 先启动服务器, 再启动客户端, 这个为什么不用多说吧?
4. 在客户端输入 "dir" 指令, 查看可下载目录文件:
"dir" 指令
注: 这里我用 "\t" 即制表符来控制显示格式, 但不知道为啥第一个文件后会自动换行, 暂且放着不去管它(不影响通信功能)
5. 在客户端输入 "get" 指令, 下载服务器中指定文件:
"get 射频仿真. pptx" 指令
"射频仿真. pptx" 下载成功
6. 在客户端输入 "put" 指令, 上传指定文件到服务器:
"put 天线 15.doc" 指令
"天线 15.doc" 上传成功
7. 在客户端输入 "close" 指令, 关闭连接, 服务器进入监听状态:
"close" 指令
注意几点:
1.send 方法的第一个参数是二进制数据, 使用 bytes(字符串, encoding='utf-8')将字符串改为二进制进行发送
2. 在做指令判断的时候, 需要将 "get","put" 等指令转为二进制, 才能与接收到的指令进行判断, 这里不将二进制转为字符串 (这样易于理解) 的原因是, 我没找到一个简单的方法让二进制转 str(我不会)
3. 理清楚创建流程后, 主要的困难就是转换数据格式, 哪里需要转哪里不需要, 自己写一遍, 再 debug 一会儿就会弄明白了
我们没能力发现知识, 我们只是知识的寄生虫
来源: http://www.jianshu.com/p/eff5ea9d9f71