ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python之利用socket模块实现通信,模拟ftp部分功能

2021-03-24 17:51:52  阅读:199  来源: 互联网

标签:ftp utf socket python data buffsize client recv


环境:python3.8 

参考:

https://blog.csdn.net/qq_36119192/article/details/83662680(socket模块使用)

https://blog.csdn.net/jing16337305/article/details/79856116(send()和sendall())

https://www.cnblogs.com/linhaifeng/articles/6129246.html(python网络编程)

https://segmentfault.com/a/1190000008707682(Thread模块实现线程并发)

socket通信原理简介:

    许多人把socket默认当为ip+端口,之前我也是这么认为的,直到看了:https://www.cnblogs.com/linhaifeng/articles/6129246.html的介绍,摘抄如下

      Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    看起来比较抽象,其实socket就类似于一个市场,所谓的服务sever就类似于一个卖家,卖家不关心市场的建造和维护之类的事,他只需知道在市场可以等来买家,市场会保证他们的正常交易,所谓的客户client就类似于一个买家,一个卖家要卖东西,他要做四件事:

    1.走进市场(申请 socket)

    2.租一个摊位(bind(“IP+端口”))【ip+端口就相当于摊位号】

    3.摆好商品(listen(backlog))【backlog相当于有多少商品】

    4.等待客户(accept)

所以服务端的代码如下:

from socket import *
backlog=10
buffsize=1024
server=socket(AF_INET,SOCK_STREAM)
server.bind(("127.0.0.1",5666))
server.listen(backlog)
clientsocket, clientaddr = server.accept()
while True:
    msg=clientsocket.recv(buffsize)
    print('client msg:', msg.decode('utf-8'))
    clientsocket.sendall(input('your msg:').encode('utf-8'))

客户端就相对简单,它只要做两件事:

    1.走进市场(socket)

    2.找到对应摊位(connect)

如此就可以进行交流,所以客户端代码如下:

from socket import *
backlog=10
buffsize=1024
client=socket(AF_INET,SOCK_STREAM)
client.connect(("127.0.0.1",5666))
while True:
    client.sendall(input('your msg:').encode('utf-8'))
    print('server msg:',client.recv(buffsize).decode('utf-8'))

debug:

1.要想一次接受多行,必须用socket.sendall()方法,和send()的差别参考:https://blog.csdn.net/jing16337305/article/details/79856116

2.socket建通信必须用encode和decode方法编解码,否则会报错:

    TypeError: a bytes-like object is required, not 'str'

这大概是网络传输要求二进制的缘故(个人猜测,未证实)



实现了通信功能后,模拟ftp思路其实很简单,既然利用socket可以互相通信,那么ftp的功能,无非是在本地执行一些操作,把结果发送给对端

比如ls命令,思路就是利用os模块执行命令,把命令返回结果保存发送给对端

而这里的get,我则模拟了ftp的主动模式,当客户用get,我会让其输入文件名,判断文件是否存在,如果 存在则用“file send ready”作为标志传给客户端,客户端收到这个信息,则就跟上文所说的卖家卖商品,临时监听一个socket,然后发送服务端“file recv ready”作为标志告诉服务端已经准备好接收数据,然后服务端读文件,发送给客户端,客户端写文件,结束关闭socket,进入下一次循环

而put命令则模拟ftp的被动模式,与put命令的实现是一个相反的过程,服务端以"file recv ready"作为标志告诉客户端可以接受信息,客户端发送完毕进入下一次循环

cd命令比较简单,留给有兴趣的看官自己实现,不会的可以给我留言

本程序依然有大量的改进空间,比如上传文件没有判断是否已存在文件等等,以后有空回来完善

ftp服务端程序如下:

from socket import *
import os,threading

backlog = 10
buffsize = 1024
ftpserver = socket(AF_INET, SOCK_STREAM)
localip="192.168.1.1"
ftpserver.bind((localip, 21))
ftpserver.listen(backlog)
welcome_list = '''welcome!what do you want ?
    put get ls cd
'''


def cd():
    pass


def put():
    clientsocket.sendall('file recv ready'.encode('utf-8'))
    data_r = socket(AF_INET, SOCK_STREAM)
    data_r.bind((localip, 20))
    data_r.listen(1)
    clientsocket.sendall('please input your filename: '.encode('utf-8'))
    filename=clientsocket.recv(buffsize).decode('utf-8')
    filesize=clientsocket.recv(buffsize).decode('utf-8')
    recv_s,recv_addr=data_r.accept()
    datalen=0
    with open(filename,'w',encoding='utf-8') as write_f:
        data=recv_s.recv(buffsize).decode('utf-8')
        while datalen<int(filesize):
            write_f.write(data)
            datalen=datalen+len(data)
    data_r.close()
def get():
    result = os.popen('ls').read()
    clientsocket.sendall(result.encode('utf-8')+'please input filename:  '.encode('utf-8'))
    filename=clientsocket.recv(buffsize)
    if os.path.isfile(filename.decode('utf-8')):
        clientsocket.sendall('file send ready'.encode('utf-8'))
        if clientsocket.recv(buffsize).decode('utf-8')=='recv ready':
            filesize=os.stat(filename.decode('utf-8')).st_size
            clientsocket.sendall(filename)
            clientsocket.sendall(str(filesize).encode('utf-8'))
            data_s=socket(AF_INET, SOCK_STREAM)
            data_s.connect((clientadd[1],20))
            with open(filename.decode('utf-8'),'r',encoding='utf-8') as read_f:
                for i in read_f:
                    data_s.sendall(i.encode('utf-8'))
    else:
        clientsocket.sendall('file not exit'.encode('utf-8'))


def ls():
    result=os.popen('ls').read()
    clientsocket.sendall(result.encode('utf-8'))
welcome_msg = welcome_list + 'current dir is ' + os.popen('pwd').read()
order = ['put', 'get', 'ls', 'cd']
var={1}
class handler():
    def __init__(self,clientsocket,clientaddr):
        self.clientsocket=clientsocket
        self.clientaddr =clientaddr
    def run(self):
        self.clientsocket.sendall(welcome_msg.encode('utf-8'))
        while True:
            msg = self.clientsocket.recv(buffsize)
            print(msg.decode('utf-8'))
            if msg.decode('utf-8') not in order:
                self.clientsocket.sendall('only support : put get ls cd '.encode('utf-8'))
            else:
                globals()[msg.decode('utf-8')]()  
def worker(clientsocket,clientaddr):
    a=handler(clientsocket,clientaddr)
    a.run()
while True:
    for i in range(backlog):
        clientsocket, clientaddr = ftpserver.accept()
	#print clientaddr
        t=threading.Thread(target=worker,args=(clientsocket,clientaddr))
        t.start()

客户端程序如下:

from socket import *
import time,os
backlog=10
buffsize=1024
localip="192.168.1.2"
serverip="192.168.1.1"
client=socket(AF_INET,SOCK_STREAM)
client.connect((serverip,21))
def data_recv():
    data_s=socket(AF_INET,SOCK_STREAM)
    data_s.bind((localip,20))
    data_s.listen(1)
    client.sendall('recv ready'.encode('utf-8'))
    filename=client.recv(buffsize).decode('utf-8')
    filesize=client.recv(buffsize).decode('utf-8')
    recv_s,recv_addr=data_s.accept()
    datalen=0
    with open(filename,'w',encoding='utf-8') as write_f:
        data=recv_s.recv(buffsize).decode('utf-8')
        while datalen<int(filesize):
            write_f.write(data)
            datalen=datalen+len(data)
    data_s.close()

def data_send():
    client.recv(buffsize).decode('utf-8')
    filename = input('your filename: ')
    print (filename)
    if not os.path.isfile(filename):
        print(os.popen('ls').read())
        filename=input('file not exist,input again :')
    client.sendall(filename.encode('utf-8'))
    filesize = os.stat(filename).st_size
    client.sendall(str(filesize).encode('utf-8'))
    data_s = socket(AF_INET, SOCK_STREAM)
    data_s.connect((serverip, 20))
    with open(filename, 'r', encoding='utf-8') as read_f:
        for i in read_f:
            data_s.sendall(i.encode('utf-8'))
    data_s.close()
while True:
    msg=client.recv(buffsize).decode('utf-8')
    if msg== 'file send ready':
        data_recv()
    elif msg== 'file recv ready':
        data_send()
    else:
        print('server msg:',msg)
    client.sendall(input('your msg:').encode('utf-8'))

这里还引用了线程模块实现并发,一般都用serve_forever,python的线程有个特点是gil的存在使得它一个进程一次只能执行一个线程,达不到真正意义的并行,要并行只能用多进程的模式,所以python多线程的意义只存在于io密集型的操作(因为cpu在io等待的时间可以利用其计算性能),对于计算密集型的操作则反而会降低效率(io密集型,cpu一直执行运算,多线程只会导致需要额外的切换开销)

那么为什么不直接使用多进程呢?

这是因为进程的开销远远比线程大,进程好比办公室,线程好比工位,创建多进程就相当于多租了几间办公室,而多线程就相当于多搞了几个工位,线程之间可以共用办公室的大部分东西,而进程与进程之间就要跨办公室,这就涉及到几种办法:1.两个办公室之间凿同一个通道(比如pipe管道)2.在外面放一张公用的桌子,桌子的东西大家通用(比如共享变量)3.找别人帮忙(比如socket)等等,具体的等下一篇说明


标签:ftp,utf,socket,python,data,buffsize,client,recv
来源: https://blog.51cto.com/linzb/2670894

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有