Python基于Socket实现C/S架构文件传输详解
本文详细解析了基于Python Socket库构建C/S架构的文件传输系统。通过TCP协议实现客户端与服务端的双向通信,涵盖socket创建、绑定监听、三次握手及四次挥手等核心网络概念。服务端监听9500端口,支持ls查看、put上传及get下载指令,配合二进制读写实现稳定文件传输,适合初学者理解网络编程基础。
本文分享使用Python实现C/S架构进行简单文件传输的代码示例,供读者参考。
实现简单的文件传输功能,需要首先明确以下核心问题:
1. 什么是C/S架构?
C/S架构即客户端/服务器(Client/Server)架构。尽管不同人对它的理解可能有所差异,但其核心定义一致:服务器是一种软件或硬件实体,旨在向一个或多个客户端提供服务。服务器的主要职责是等待客户端请求,处理这些请求,随后继续等待其他请求。
2. 客户端与服务器如何建立通信?
本质上,这是互联网中两个主机之间的通信过程。首先,通过IP地址标识一台主机,但这不足以完成通信,还需要标识主机内的具体进程。此外,通信协议不可或缺,需根据需求选择UDP报文协议或TCP数据流协议。套接字(Socket)作为标识符,用于唯一标识网络进程。
厘清上述两个问题后,该任务的基础已解决三分之一。接下来,本例选择TCP协议进行数据传输。
服务端实现逻辑:
使用socket()函数定义Socket描述字。
调用bind()函数将地址族中的特定地址分配给Socket,即将IPv4或IPv6地址与端口号的组合绑定到Socket上。
使用listen()函数将Socket设置为被动模式,以等待客户端的连接请求。
当客户端发送消息时,服务端通过accept()函数接收请求,并返回客户端的Socket描述字。随后,服务端利用该描述字与客户端进行信息的发送和接收。
本程序旨在实现文件传输,设计思路类似于FTP程序,满足客户上传和下载文件的需求。服务端启动后监听端口(此处使用9500),等待客户端连接。客户端通过三次握手建立连接后,可输入命令向服务端发起请求:输入“ls”命令可查看服务端可供下载的文件列表;输入“put 文件名”可上传文件,输入“get 文件名”可下载文件。在上传文件时,客户端以二进制方式打开本地文件读取内容,并调用send()函数发送文件数据;服务端则以写入方式打开目标文件,将客户端上传的数据写入其中,从而完成文件上传。下载过程与此类似:客户端发送“get 文件名”命令,并以写入方式打开本地文件;服务端收到命令后以读取方式打开源文件,并将内容发送给客户端。
TCP三次握手建立连接过程:

当客户端调用connect()时,触发连接请求,向服务器发送SYN J包,此时connect()进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept()函数接收请求并向客户端发送SYN K和ACK J+1,此时accept()进入阻塞状态;客户端收到服务器的SYN K和ACK J+1后,connect()返回,并对SYN K进行确认;服务器收到ACK K+1时,accept()返回,至此三次握手完毕,连接建立。
TCP四次挥手断开连接过程:

某个应用进程首先调用close()主动关闭连接,此时TCP发送一个FIN M;另一端接收到FIN M后,执行被动关闭,并对该FIN进行确认。FIN的接收作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;一段时间之后,接收到文件结束符的应用进程调用close()关闭它的Socket。这导致其TCP也发送一个FIN N;接收到这个FIN的源发送端TCP对其进行确认。
服务端完整代码:
#!/usr/bin/env python
from socket import *
import time
import sys
import os
HOST = 'localhost'
PORT = 9500
BUFIZ = 1024
ADDR = (HOST,PORT)
def recvfile(filename):
print 'starting receive file...'
f = open(filename,'wb')
cliSockfd.send('no problem')
while True:
data = cliSockfd.recv(4096)
if data == 'EOF':
print 'recved file success!'
break
f.write(data)
f.close()
def sendfile(filename):
print 'starting send file...'
cliSockfd.send('no problem')
f = open(filename,'rb')
while True:
data = f.read(4096)
if not data:
break
cliSockfd.send(data)
f.close()
time.sleep(1)
cliSockfd.send('EOF')
print 'send file success!'
def handle1(act,filename):
if act == 'put':
print 'recving msg!'
recvfile(filename)
elif act == 'get':
print 'sending msg!'
sendfile(filename)
else:
print 'error!'
def handle2(act):
if act == 'ls':
path = sys.path[0]
every_file = os.listdir(path)
for filename in every_file:
cliSockfd.send(filename + ' ')
time.sleep(1)
cliSockfd.send('EOF')
print 'all filename has send to client success!'
else:
print 'command error'
sockfd = socket(AF_INET,SOCK_STREAM)
sockfd.bind(ADDR)
sockfd.listen(5)
while True:
print 'waiting for connection...'
cliSockfd,addr = sockfd.accept()
print '...connected from:',addr
while True:
msg = cliSockfd.recv(4096)
if msg == 'close':
print 'client closed'
break
info = msg.split()
if len(info) == 2:
handle1(*info)
elif len(info) == 1:
handle2(*info)
else:
print 'command error!'
break
客户端完整代码:
#!/usr/bin/env python
import sys
import socket
import time
HOST = 'localhost'
PORT = 9500
BUFSIZ = 1024
ADDR = (HOST,PORT)
cliSockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
def recvfile(filename):
f = open(filename,'wb')
while True:
msg = cliSockfd.recv(4096)
if msg == 'EOF':
print 'recv file success!'
break
f.write(msg)
f.close
def sendfile(filename):
f = open(filename,'rb')
while True:
msg = f.read(4096)
if not msg:
break
cliSockfd.sendall(msg)
f.close()
time.sleep(1)
cliSockfd.sendall('EOF')
print 'send file success'
def confirm(cliSockfd,client_command):
cliSockfd.send(client_command)
msg = cliSockfd.recv(4096)
if msg == 'no problem':
return True
def handle1(act,filename):
if act == 'put':
if confirm(cliSockfd,client_command):
sendfile(filename)
else:
print 'server error1!'
elif act == 'get':
if confirm(cliSockfd,client_command):
recvfile(filename)
else:
print 'server error2!'
else:
print 'command error!'
def handle2(act):
if act == 'ls':
cliSockfd.send('ls')
while True:
msg = cliSockfd.recv(1024)
if msg == 'EOF':
break
print msg
else:
print 'command error'
try:
cliSockfd.connect(ADDR)
print 'connect to ',ADDR
while True:
client_command = raw_input('>>>')
if not client_command:
continue
msg = client_command.split()
if len(msg) == 2:
handle1(*msg)
elif len(msg) == 1 and msg != ['close']:
handle2(*msg)
elif len(msg) == 1 and msg == ['close']:
cliSockfd.send('close')
break
else:
print 'command error'
except socket.error,e:
print 'error:',e
finally:
cliSockfd.close()
运行效果截图:
服务端运行界面:

客户端运行界面:

欢迎各位技术专家提出宝贵意见。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









