- 网络传输方式
- 什么是 Socket?
- 1、导入模块
- 2、创建套接字 TCP
- socket.SOCK_STREAM TCP传输方式
- socket.SOCK_DGRAM UDP传输方式
- 3、建立连接 connect()
- tcp_client_socket.connect(address)
- address —> (“ip”, 端口)
- 4、发送数据
- 接收数据
- recv_data 保存的是服务端回复的信息的二进制
- b’\xd4\xbc\xd4\xbc\xd4\xbc’
- 解码
- 5、关闭套接字
- 案例:
- 处理客户端的请求操作
- 案例-文件的上传和下载:
- 1、导入模块
- 2、创建套接字
- 设置套接字地址可以重用
- tcp_server_socket.setsockopt(当前套接字, 属性名, 属性值)
- socket.SO_REUSEADDR 地址是否可以重用 True可以重用 False 不可以重用
- 3、绑定端口
- 4、设置监听,设置套接字由主动为被动
- 5、接受客户端连接
- 10、关闭服务器
- tcp_server_socket.close()
- socket之send和recv原理剖析
网络传输方式
- 面向无连接两台计算机通信的时候,不需要建立连接(逻辑)就可以进行数据的收发,数据可能会丢
- 传输协议: UDP
- 面向有连接两台计算机通信的时候,需要先建立连接,再能通信
- 传输协议:TCP
Python 提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统 Socket 接口的全部方法。
高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
什么是 Socket?
Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
socket()函数
Python 中,我们用 socket()函数来创建套接字,语法格式如下:
socket.socket([family[, type[, proto]]])
参数
family: 套接字家族可以使 AF_UNIX 或者 AF_INET(使用IPv4) AF_INET6 使用IPv6。
- type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREA(使用TCP的传输方式(有连接))M 或 SOCK_DGRAM(使用UDP的传输方式(无连接)。
- protocol: 一般不填默认为 0。
Socket 对象(内建)方法
| 函数 | 描述 | | —- | —- | | 服务器端套接字 | | | s.bind() | 绑定地址(host,port)到套接字, 在 AF_INET下,以元组(host,port)的形式表示地址。 | | s.listen() | 开始 TCP 监听。backlog 指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为 1,大部分应用程序设为 5 就可以了。 | | s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 | | 客户端套接字 | | | s.connect() | 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 | | s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 | | 公共用途的套接字函数 | | | s.recv() | 接收 TCP 数据,数据以字符串形式返回,bufsize 指定要接收的最大数据量。flag 提供有关消息的其他信息,通常可以忽略。 | | s.send() | 发送 TCP 数据,将 string 中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于 string 的字节大小。 | | s.sendall() | 完整发送 TCP 数据。将 string 中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回 None,失败则抛出异常。 | | s.recvfrom() | 接收 UDP 数据,与 recv() 类似,但返回值是(data,address)。其中 data 是包含接收数据的字符串,address 是发送数据的套接字地址。 | | s.sendto() | 发送 UDP 数据,将数据发送到套接字,address 是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 | | s.close() | 关闭套接字 |
UDP网络程序
1.发送数据
"""
1、导入模块
2、创建套接字
3、设置广播权限
4、发送数据
5、关闭套接字
"""
# 1、导入模块
import socket
# 2、创建套接字
# IPv4 UDP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 3、设置广播权限(套接字默认不允许发送广播,需要开启相关权限)
# PermissionError: [Errno 13] Permission denied
# udp_socket.setsockopt(套接字,属性,属性值)
# socket.SOL_SOCKET 当前的套接字
# socket.SO_BROADCAST 广播属性
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
# 4、发送数据
udp_socket.sendto("哈哈,打不过我吧~".encode(), ("192.168.150.255", 8080))
# 5、关闭套接字
udp_socket.close()
2.接收数据
s.recvfrom()、
接收 UDP 数据,与 recv() 类似,但返回值是(data,address)。其中 data 是包含接收数据的字符串,address 是发送数据的套接字地址。
# 1、导入模块
import socket
# 2、创建套接字
# IPv4 UDP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 3、设置广播权限(套接字默认不允许发送广播,需要开启相关权限)
# PermissionError: [Errno 13] Permission denied
# udp_socket.setsockopt(套接字,属性,属性值)
# socket.SOL_SOCKET 当前的套接字
# socket.SO_BROADCAST 广播属性
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
# 4、发送数据
udp_socket.sendto("发送消息~".encode(), ("192.168.56.1 ", 8080))
# 5、每次接收1024个字节 会造成程序阻塞 等待接收发来的数据
recv_data = udp_socket.recvfrom(1024)
# 收到的数据是个元组
result = recv_data[0].decode("GBK")
print("接收来自:", recv_data[1], "消息:", result)
# 6、关闭套接字
udp_socket.close()
- 编码字符串.encode() 默认UTF-8字符串
- 解码二进制.decode() 默认UTF-8字符集
- 解码失败的处理decode(encoding=”字符集”, errors=”错误处理方式”)错误处理方式有两种:ignore 忽略, strict 严格

上面每次发送端口都是在变化,为了方便可以绑定端口。
| s.bind() | 绑定地址(host,port)到套接字, 在 AF_INET下,以元组(host,port)的形式表示地址。 |
|---|---|
# address ---> ("ip地址", 端口号) 端口号范围:0-65535
udp_socket.bind(("192.168.56.1", 8088))
UDP广播
广播地址:xxx.xxx.xxx.255 或者 255.255.255.255
思路:
# 1、导入模块
import socket
#创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#开启权限
# PermissionError: [Errno 13] Permission denied
# udp_socket.setsockopt(套接字,属性,属性值)
# socket.SOL_SOCKET 当前的套接字
# socket.SO_BROADCAST 广播属性
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,True)
#绑定端口
udp_socket.bind(("192.168.56.1", 8088))
#发送数据
udp_socket.sendto("哈哈,打不过我吧~".encode(), ("255.255.255.255", 8080))
#udp_socket.sendto("哈哈,打不过我吧~".encode(), ("192.168.150.255", 8080))
udp_socket.close()
udp聊天器
"""
一、功能
1、发送信息
2、接收信息
3、退出系统
二、框架的设计
1、发送信息 send_msg()
2、接收信息 recv_msg()
3、程序的主入口 main()
4、当程序独立运行的时候,才启动聊天器
三、实现步骤
1、发送信息 send_msg()
1) 定义变量接收用户与输入的接收方的IP地址
2)定义变量接收用户与输入的接收方的端口号
3)定义变量接收用户与输入的接收方的内容
4)使用socket的sendto() 发送信息
2、接收信息 recv_msg()
1) 使用socket 接收数据
2)解码数据
3)输出显示
3、主入口main()
1)创建套接字
2)绑定端口
3)打印菜单(循环)
4)接收用户输入的选项
5)判断用户的选择,并且调用对应的函数
6)关闭套接字
"""
import socket
def send_msg(udp_socket):
"""发送信息的函数"""
# 1) 定义变量接收用户与输入的接收方的IP地址
ipaddr = input("请输入接收方的IP地址:\n")
# 判断是否需要默认
if len(ipaddr) == 0:
ipaddr = "192.168.150.93"
print("当前接收方默认IP设置为[%s]" % ipaddr)
# 2)定义变量接收用户与输入的接收方的端口号
port = input("请输入接收方的端口号:\n")
if len(port) == 0:
port = "8080"
print("当前接收方默认端口设置为[%s]" % port)
# 3)定义变量接收用户与输入的接收方的内容
content = input("请输入要发送的内容:\n")
# 4)使用socket的sendto()发送信息
udp_socket.sendto(content.encode(), (ipaddr, int(port)))
def recv_msg(udp_socket):
"""接收信息的函数"""
# 1) 使用socket接收数据
# (b'\xe4\xbc\x91\xe6\x81\xaf\xe4\xbc\x91\xe6\x81\xaf\xe6\x89\x80', ('192.168.150.93', 8080))
recv_data, ip_port = udp_socket.recvfrom(1024)
# 2)解码数据
recv_text = recv_data.decode()
# 3)输出显示
print("接收到[%s]的消息:%s" % (str(ip_port), recv_text))
def main():
"""程序的主入口"""
# 1)创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2)绑定端口
# address ---> ("ip地址", 端口号)
# udp_socket.bind(address)
udp_socket.bind(("", 8080))
while True:
# 3)打印菜单(循环)
print("\n\n***************************")
print("****** 1、发送信息 *******")
print("****** 2、接收信息 *******")
print("****** 3、退出系统 *******")
print("***************************")
# 4)接收用户输入的选项
sel_num = int(input("请输入选项:\n"))
# 5)判断用户的选择,并且调用对应的函数
if sel_num == 1:
# print("您选择的是发送信息")
# 调用发送信息的函数
send_msg(udp_socket)
elif sel_num == 2:
# print("您选择的是接收信息")
recv_msg(udp_socket)
elif sel_num == 3:
print("系统正在退出中...")
print("系统退出完成!")
break
# 6)关闭套接字
udp_socket.close()
if __name__ == '__main__':
# 程序独立运行的时候,才去启动聊天器
main()
TCP网络程序
TCP 面向连接、可靠的、基于字节流的传输控制协议
- CP的特点
- 面向连接
- 可靠传输
- 应答机制
- 超时重传
- 错误校验
- 流量管控
- TCP通信模型TCP严格区分客户端、服务端
客户端

- 实现步骤
- 导入模块
- 创建套接字socket.SOCK_STREAM
- 建立连接tcp_client_socket.connect( (“服务端ip”, 服务端端口) )
- 发送数据tcp_client_socket.send(“内容”.encode())
- 接收数据recv_data = tcp_client_socket.recv(1024)recv_data 是接收到的数据的二进制
- 关闭连接 ```python “”” 1、导入模块 2、创建套接字 TCP 3、建立连接 connect() 4、发送数据 5、关闭套接字
“””
1、导入模块
import socket
2、创建套接字 TCP
socket.SOCK_STREAM TCP传输方式
socket.SOCK_DGRAM UDP传输方式
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3、建立连接 connect()
tcp_client_socket.connect(address)
address —> (“ip”, 端口)
tcp_client_socket.connect((“192.168.56.1”, 8080))
4、发送数据
tcp_client_socket.send(“约吗?”.encode())
接收数据
recv_data = tcp_client_socket.recv(1024)
recv_data 保存的是服务端回复的信息的二进制
b’\xd4\xbc\xd4\xbc\xd4\xbc’
解码
recv_text = recv_data.decode(“utf-8”) print(“收到数据:”, recv_text)
5、关闭套接字
tcp_client_socket.close()
<a name="FygxU"></a>
### 服务器

- 实现步骤
- 导入模块
- 创建套接字
- 绑定端口
- 开启监听(把套接字由主动设置为被动模式)tcp_server_socket.listen(128) 最大允许128个连接
- 等待客户端连接new_client_socket,client_ip_port = tcp_server_socket.accept()new_client_socket 新的套接字,只是服务当前的客户端client_ip_port 客户端端口和ip地址
- 使用新的套接字接收客户端发送的信息new_client_socket.recv(1024)
- 关闭新的套接字new_client_socket.close() 关闭和当前客户端的连接
- 关闭服务器套接字tcp_server_socket.close() 服务器不再接收新的客户端,老客户端可以继续服务
```python
"""
1、导入模块
2、创建套接字
3、绑定端口和ip
4、开启监听(设置套接字为被动模式)
5、等待客户端连接
6、收发数据
7、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、绑定端口和ip
tcp_server_socket.bind(("", 8080))
# 4、开启监听(设置套接字为被动模式)
# listen() 作用设置 tcp_server_socket 套接字为被动监听模式,不能在主动发送数据
# 128 允许接受的最大的连接数,在windows 128 有效,但是在linux 此数字无效
tcp_server_socket.listen(128)
# 5、等待客户端连接
# accept() 开始接受客户端连接,程序会默认进入阻塞状态(等待客户端连接),如果由客户端连接后,程序自动
# 自动解除阻塞
# recv_data 数据含有两部分
# 1)返回了一个新的套接字socket 对象
# 2) 客户端的ip地址和端口号 元组
new_client_socket, client_ip_port = tcp_server_socket.accept()
print("新客户端来了:%s" % str(client_ip_port))
# 6、收发数据
# recv() 会让程序再次阻塞,收到信息后再接阻塞
recv_data = new_client_socket.recv(1024)
recv_text = recv_data.decode("utf-8")
print("接收到[%s]的信息:%s" % (str(client_ip_port), recv_text))
# new_client_socket.close() 表示不能再和当前的客户端通信了
new_client_socket.close()
# 7、关闭连接
# tcp_server_socket.close() 表示程序不再接受新的客户端连接,已经连接的可以继续服务
tcp_server_socket.close()
服务器增强
循环接收多条信息
"""
1、导入模块
2、创建套接字
3、绑定端口和ip
4、开启监听(设置套接字为被动模式)
5、等待客户端连接
6、收发数据
7、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、绑定端口和ip
tcp_server_socket.bind(("", 8080))
# 4、开启监听(设置套接字为被动模式)
# listen() 作用设置 tcp_server_socket 套接字为被动监听模式,不能在主动发送数据
# 128 允许接受的最大的连接数,在windows 128 有效,但是在linux 此数字无效
tcp_server_socket.listen(128)
# 5、等待客户端连接
# accept() 开始接受客户端连接,程序会默认进入阻塞状态(等待客户端连接),如果由客户端连接后,程序自动
# 自动解除阻塞
# recv_data 数据含有两部分
# 1)返回了一个新的套接字socket 对象
# 2) 客户端的ip地址和端口号 元组
new_client_socket, client_ip_port = tcp_server_socket.accept()
print("新客户端来了:%s" % str(client_ip_port))
while True:
# 6、收发数据
# recv() 会让程序再次阻塞,收到信息后再接阻塞
recv_data = new_client_socket.recv(1024)
recv_text = recv_data.decode("utf-8")
print("接收到[%s]的信息:%s" % (str(client_ip_port), recv_text))
# new_client_socket.close() 表示不能再和当前的客户端通信了
new_client_socket.close()
# 7、关闭连接
# tcp_server_socket.close() 表示程序不再接受新的客户端连接,已经连接的可以继续服务
tcp_server_socket.close()
循环接受多个客户端连接
"""
1、导入模块
2、创建套接字
3、绑定端口和ip
4、开启监听(设置套接字为被动模式)
5、等待客户端连接
6、收发数据
7、关闭连接
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、绑定端口和ip
tcp_server_socket.bind(("", 8080))
# 4、开启监听(设置套接字为被动模式)
# listen() 作用设置 tcp_server_socket 套接字为被动监听模式,不能在主动发送数据
# 128 允许接受的最大的连接数,在windows 128 有效,但是在linux 此数字无效
tcp_server_socket.listen(128)
# 5、等待客户端连接
# accept() 开始接受客户端连接,程序会默认进入阻塞状态(等待客户端连接),如果由客户端连接后,程序自动
# 自动解除阻塞
# recv_data 数据含有两部分
# 1)返回了一个新的套接字socket 对象
# 2) 客户端的ip地址和端口号 元组
while True:
new_client_socket, client_ip_port = tcp_server_socket.accept()
print("新客户端来了:%s" % str(client_ip_port))
# 6、收发数据
while True:
# recv() 会让程序再次阻塞,收到信息后再接阻塞
recv_data = new_client_socket.recv(1024)
# 当接受到数据为 空 的时候,表示客户端已经断开连接了,服务端也要断开
# if len(recv_data)!= 0:
# b'xxxx'
# 如果recv_data 非空即为真,否则为假
if recv_data:
recv_text = recv_data.decode("utf-8")
print("接收到[%s]的信息:%s" % (str(client_ip_port), recv_text))
else:
print("客户端已经断开连接!")
break
# new_client_socket.close() 表示不能再和当前的客户端通信了
new_client_socket.close()
# 7、关闭连接
# tcp_server_socket.close() 表示程序不再接受新的客户端连接,已经连接的可以继续服务
tcp_server_socket.close()
TCP网络应用程序的注意点介绍
- 当 TCP 客户端程序想要和 TCP 服务端程序进行通信的时候必须要先建立连接
- TCP 客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。
- TCP 服务端程序必须绑定端口号,否则客户端找不到这个 TCP 服务端程序。
- listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。
- 当 TCP 客户端程序和 TCP 服务端程序连接成功后, TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。
- 关闭 accept 返回的套接字意味着和这个客户端已经通信完毕。
- 关闭 listen 后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信。
- 当客户端的套接字调用 close 后,服务器端的 recv 会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的 recv 也会解阻塞,返回的数据长度也为0。
案例:
案例-多任务版TCP服务端程序开发
1. 需求
目前我们开发的TCP服务端程序只能服务于一个客户端,如何开发一个多任务版的TCP服务端程序能够服务于多个客户端呢?
完成多任务,可以使用线程,比进程更加节省内存资源。
2. 具体实现步骤
- 编写一个TCP服务端程序,循环等待接受客户端的连接请求
- 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
- 把创建的子线程设置成为守护主线程,防止主线程无法退出。
3. 多任务版TCP服务端程序的示例代码:
```python import socket import threading
处理客户端的请求操作
def handle_client_request(service_client_socket, ip_port):
# 循环接收客户端发送的数据
while True:
# 接收客户端发送的数据
recv_data = service_client_socket.recv(1024)
# 容器类型判断是否有数据可以直接使用if语句进行判断,如果容器类型里面有数据表示条件成立,否则条件失败
# 容器类型: 列表、字典、元组、字符串、set、range、二进制数据
if recv_data:
print(recv_data.decode("gbk"), ip_port)
# 回复
service_client_socket.send("ok,问题正在处理中...".encode("gbk"))
else:
print("客户端下线了:", ip_port)
break
# 终止和客户端进行通信
service_client_socket.close()
if name == ‘main‘:
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", 9090))
# 设置监听, listen后的套接字是被动套接字,只负责接收客户端的连接请求
tcp_server_socket.listen(128)
# 循环等待接收客户端的连接请求
while True:
# 等待接收客户端的连接请求
service_client_socket, ip_port = tcp_server_socket.accept()
print("客户端连接成功:", ip_port)
# 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
# 设置守护主线程
sub_thread.setDaemon(True)
# 启动子线程
sub_thread.start()
# tcp服务端套接字可以不需要关闭,因为服务端程序需要一直运行
# tcp_server_socket.close()
**执行结果:**
```python
客户端连接成功: ('172.16.47.209', 51528)
客户端连接成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)
小结
- 编写一个TCP服务端程序,循环等待接受客户端的连接请求
2.当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞while True: service_client_socket, ip_port = tcp_server_socket.accept()
3.把创建的子线程设置成为守护主线程,防止主线程无法退出。while True: service_client_socket, ip_port = tcp_server_socket.accept() sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port)) sub_thread.start()while True: service_client_socket, ip_port = tcp_server_socket.accept() sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port)) sub_thread.setDaemon(True) sub_thread.start()案例-文件的上传和下载:
服务端 ```python “”” 1、导入模块 2、创建套接字 3、绑定端口 4、设置监听,设置套接字由主动为被动 5、接受客户端连接 6、接收客户端发送的文件名 7、根据文件名读取文件内容 8、把读取的内容发送给客户端(循环) 9、关闭和当前客户端的连接 10、关闭服务器
“””
1、导入模块
import socket
2、创建套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
设置套接字地址可以重用
tcp_server_socket.setsockopt(当前套接字, 属性名, 属性值)
socket.SO_REUSEADDR 地址是否可以重用 True可以重用 False 不可以重用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
3、绑定端口
tcp_server_socket.bind((“”, 8081))
4、设置监听,设置套接字由主动为被动
tcp_server_socket.listen(128)
5、接受客户端连接
while True: new_client_socket, ip_port = tcp_server_socket.accept() print(“欢迎新客户端:”, ip_port)
# 6、接收客户端发送的文件名
recv_data = new_client_socket.recv(1024)
file_name = recv_data.decode()
print(file_name)
try:
# 7、根据文件名读取文件内容
with open(file_name, "rb") as file:
# 8、把读取的内容发送给客户端(循环)
while True:
file_data = file.read(1024)
# 判断是否读取到了文件的末尾
if file_data:
# 发送文件
new_client_socket.send(file_data)
else:
break
except Exception as e:
print("文件%s下载失败!" % file_name)
else:
print("文件%s下载成功" % file_name)
# 9、关闭和当前客户端的连接
new_client_socket.close()
10、关闭服务器
tcp_server_socket.close()
客户端
```python
"""
1、导入模块
2、创建套接字
3、建立连接
4、接收用户输入的文件名
5、发送文件名到服务端
6、创建文件,并且准备保存
7、接收服务端发送的数据,保存到本地(循环)
8、关闭套接字
"""
# 1、导入模块
import socket
# 2、创建套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 3、建立连接
tcp_client_socket.connect(("192.168.150.111", 8081))
# 4、接收用户输入的文件名
file_name = input("请输入要下载的文件名:\n")
# 5、发送文件名到服务端
tcp_client_socket.send(file_name.encode())
# 6、创建文件,并且准备保存
with open("/home/demo/Desktop/"+file_name, "wb") as file:
# 7、接收服务端发送的数据,保存到本地(循环)
while True:
file_data = tcp_client_socket.recv(1024)
# 判断数据是否传送完毕
if file_data:
file.write(file_data)
else:
break
# 8、关闭套接字
tcp_client_socket.close()
socket之send和recv原理剖析
1. 认识TCP socket的发送和接收缓冲区
当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。
2. send原理剖析
send是不是直接把数据发给服务端?
不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡 。
3. recv原理剖析
recv是不是直接从客户端接收数据?
不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。
4. send和recv原理剖析图

说明:

