项目说明

现有系统是两台Windows 10的电脑,其中一台作为服务器,另一台作为客户端。服务器中有一个文件夹pics,里面有10张图片,如下图:

本文旨在利用Python中的Socket库将服务器中的图片传输到客户端。

实现步骤

服务器:

  • 1.套接字对象类型声明和连接协议生成;

  • 2.在服务器中绑定要监听的端口,开始监听;

  • 3.客户端上传请求后,建立服务器到客户端的连接实例;

  • 4.接收数据,获取图片名称命令,指定要传输的图片;

  • 5.将图片数据按照字节数分批下载到客户端,直到传输完成。

客户:

  • 1.声明协议类型,同时生成socket连接对象;

  • 2.连接服务器的相关端口;

  • 3.向服务器发送有关图像名称的命令;

  • 4.根据字节数批量获取从服务器下载到客户端的数据,并在一个文件中共存,直到传输完成。

代码实现

这里的代码实现只是为了说明一些重要功能的作用,并不完整。完整的代码贴在文末。

服务器

1.导入所需的库

进口插座

导入我们

2.声明协议类型,同时生成socket连接对象

服务器 u003d socket.socket()

3.在服务器中绑定需要监听的端口并开始监听

server.bind(('192.168.43.164', 1200)) # 绑定监听端口 u003d (服务器ip地址+任意端口)

server.listen(5) # 监听

这里的服务器ip地址可以在终端输入ipconfig找到。端口是任意指定的。如果它被占用了,最好换一个。

4.获取服务器中的图片信息

BASE_DIR u003d os.path.dirname(os.path.abspath(__file__)) # 获取当前目录

PIC\DIR u003d os.path.join(BASE_DIR, 'pics') # 图片文件夹路径

pic_names u003d sorted(os.listdir(PIC_DIR)) # 获取所有排序好的图片的名字,并放入一个列表中 ['1.jpg','2.jpg',...,'9 .jpg']

将图片名称放入一个大字符串中

pic_names u003d " ".join(i for i in pic_names) # '0.jpg 1.jpg 2.jpg 3.jpg 4.jpg 5.jpg 6.jpg 7.jpg 8.jpg 9.jpg '

5.客户端上传请求后,建立服务器到客户端的连接实例

conn, addr u003d server.accept()

print("接收自{}请求".format(addr))

这里生成的conn是客户端连接的,服务端为其生成的连接实例。

6.接收数据并指定图片名称

接收数据时,可以指定每次接收的字节数,防止阻塞。这里设置每次最多可以接收1024字节。

data u003d conn.recv(1024) # 接收数据,获取图片名称命令,指定要传输的图片

打印(“接收:”,数据)

7.上传相关图片信息给客户端

客户端和服务端的通信是基于bytes类型的信息进行的,所以每次传输后,都需要将bytes类型的信息解码成string类型的信息。

另外,需要将要下载的图片大小传输给客户端,以便客户端指定每次上传的字节数。

img_name u003d data.decode('utf-8') # 将客户端传来的图片名称(字节)解码成字符串

img_path u003d os.path.join(PIC_DIR, img_name) # 获取对应图片的绝对路径

file\size u003d os.stat(img\path).st_size # 获取图像文件的大小,以字节为单位。调用stat时,用于返回相关文件的系统状态信息

file_info u003d '%s|%s' % (img_name, file_size) # 发送图片信息给客户端(用于区分文档中的两个任务)

conn.sendall(字节(文件_info,'utf-8'))

8.打开相关图片文件,批量读取字节并下载到客户端

f u003d open(img_path, 'rb') # 以二进制格式打开文件为只读

has_sent u003d 0 #记录已发送的字节数

while has_sent !u003d file_size: # 如果发送的字节数不等于图片的大小,则发送

file u003d f.read(1024) # 一次读取 1024 个字节

conn.sendall(file) # 发送给客户端

has_sent +u003d len(file) # 更新发送的字节数

f.close() #发送后关闭文件

客户端

1.导入所需的库

进口插座

导入我们

2.声明协议类型,同时生成socket连接对象

服务器 u003d socket.socket()

3.连接到服务器的相关端口

client.connect(('192.168.43.28', 1200))

这里的ip地址就是服务器的ip地址。也就是说,这里的ip地址和端口号和服务器代码中设置的一样。

4.上传图片名称信息到服务器

msg u003d input(">>:").strip() # 获取要发送到服务器的字符串格式的信息

client.send(msg.encode("utf-8")) # 将字符串格式编码成字节发送

5.接收服务器返回的图片信息

data u003d client.recv(1024) # 接收服务器返回的内容

6.以二进制格式打开文件以存储服务器下载的字节

f u003d open(path, 'ab') # 打开二进制格式的文件进行追加。如果文件不存在,则创建一个新文件进行写入。

has_receive u003d 0 # 统计接收到的字节数

而有_receive !u003d 文件大小:

data1 u003d client.recv(1024) # 一次从服务器接收1024字节的数据

f.write(data1) # 写入

has_receive +u003d len(data1) # 更新接收到的字节

f.close() # 关闭文件

此处设置的一次接收的字节数不需要与服务器中的相同。

完整代码

服务器

进口插座

导入我们

server u003d socket.socket() # 1.声明协议类型,同时生成socket链接对象

server.bind(('192.168.43.28', 1200)) # 绑定监听端口 u003d (服务器ip地址+任意端口)

server.listen(5) # 监听

BASE_DIR u003d os.path.dirname(os.path.abspath(__file__)) # 获取当前目录

PIC\DIR u003d os.path.join(BASE_DIR, 'pics') # 图片文件夹路径

pic_names u003d sorted(os.listdir(PIC_DIR)) # 获取所有排序好的图片的名字,并放入一个列表中 ['1.jpg','2.jpg',...,'9 .jpg']

将图片名称放入一个大字符串中

pic_names u003d " ".join(i for i in pic_names) # '0.jpg 1.jpg 2.jpg 3.jpg 4.jpg 5.jpg 6.jpg 7.jpg 8.jpg 9.jpg '

print("我要开始等电话了")

当真:

conn, addr u003d server.accept() # 等待调用

conn 是客户端连接的连接实例,并在服务器上为其生成

print("接收自{}请求".format(addr))

当真:

data u003d conn.recv(1024) # 接收数据,获取图片名称命令,指定要传输的图片

打印(“接收:”,数据)

如果没有数据:

print("客户端丢失了...")

休息

if data.decode('utf-8') u003du003d 'get pics_names': # 获取图片名称的命令定义为get pics_names

conn.send(pic_names.encode('utf-8'))

elif data.decode('utf-8') u003du003d 'break':

休息

其他:

img_name u003d data.decode('utf-8') # 将客户端传来的图片名称(字节)解码成字符串

img_path u003d os.path.join(PIC_DIR, img_name) # 获取对应图片的绝对路径

file\size u003d os.stat(img\path).st_size # 获取图像文件的大小,以字节为单位。调用stat时,用于返回相关文件的系统状态信息

file_info u003d '%s|%s' % (img_name, file_size) # 发送图片信息给客户端(用于区分文档中的两个任务)

conn.sendall(字节(文件_info,'utf-8'))

f u003d open(img_path, 'rb') # 以二进制格式打开文件为只读

has_sent u003d 0 #记录已发送的字节数

while has_sent !u003d file_size: # 如果发送的字节数不等于图片的大小,则发送

file u003d f.read(1024) # 一次读取 1024 个字节

conn.sendall(file) # 发送给客户端

has_sent +u003d len(file) # 更新发送的字节数

f.close() #发送后关闭文件

print('上传成功')

休息

server.close()

客户端

进口插座

导入我们

client u003d socket.socket() # 声明socket类型,同时生成socket连接对象

client.connect(('192.168.43.28', 1200)) # ip + 链接服务器端口

BASE_DIR u003d os.path.dirname(os.path.abspath(__file__)) # 获取当前目录

当真:

msg u003d input(">>:").strip() # 获取要发送到服务器的字符串格式的信息

如果 len(msg) u003du003d 0:

继续

client.send(msg.encode("utf-8")) # 将字符串格式编码成字节发送

如果味精 u003du003d 'break':

休息

data u003d client.recv(1024) # 接收服务器返回的内容

if len(str(data, 'utf-8').split('|')) u003du003d 2: # 如果返回的字符串长度为2,表示从服务器返回一张图片用于任务2

filename, filesize u003d str(data, 'utf8').split('|') # 获取指定图片的名称和大小

path u003d os.path.join(BASE_DIR, filename) # 指定保存图片的路径

filesize u003d int(filesize) # 将图像大小转换为塑料

f u003d open(path, 'ab') # 打开二进制格式的文件进行追加。如果文件不存在,则创建一个新文件进行写入。

has_receive u003d 0 # 统计接收到的字节数

而有_receive !u003d 文件大小:

data1 u003d client.recv(1024) # 一次从服务器接收1024字节的数据

f.write(data1) # 写入

has_receive +u003d len(data1) # 更新接收到的字节

f.close() # 关闭文件

print("recv:", data.decode())

客户端.close()

报错

可能会出现以下错误消息:

连接尝试失败,因为连接方在一段时间后没有正确回复或连接主机没有响应!

这时候我们可以去两台电脑的终端输入:

ping 192.168.43.X

这个 X 指的是另一台计算机的 IP 地址。

应出现以下提示

是否 Ping 10.16.1.89 32 字节数据:

From 192.168.2.135 回复来自:无法访问目标主机。

From 192.168.2.135 回复来自:无法访问目标主机。

From 192.168.2.135 回复来自:无法访问目标主机。

From 192.168.2.135 回复来自:无法访问目标主机。

但是正常的提示应该是:

这个时候,很可能是网络问题。可以尝试使用手机热点,同时为这两台电脑开启wifi。个人测试很有用!

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐