我的代码中的一个错误很简单 ftp

An error in my code to be a simple ftp


运行 代码在底部时遇到错误。这就像一个简单的 ftp.
我使用 python2.6.6 和 CentOS 版本 6.8
在大多数 linux 服务器中,它会得到这样的正确结果:(非常抱歉,我刚刚注册而不能)
客户:

[root@Test ftp]# python client.py 
path:put|/home/aaa.txt

服务器:

[root@Test ftp]# python server.py 
connected...
pre_data:put|aaa.txt|4
cmd: put
file_name: aaa.txt
file_size: 4
upload successed.

但是我在某些服务器(例如我自己的 PC 中的 VM)中遇到错误。我做了很多测试(python2.6/python2.7,Centos6.5/Centos6.7),发现这个错误不是因为它们。这是错误信息:

[root@Lewis-VM ftp]# python server.py 
connected...
pre_data:put|aaa.txt|7sdfsdf           ###Here gets the wrong result, "sdfsdf" is the content of /home/aaa.txt  and it shouldn't be sent here to  'file_size' and so it  cause the "ValueError" below

cmd: put
file_name: aaa.txt
file_size: 7sdfsdf

----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 10699)
Traceback (most recent call last):
  File "/usr/lib64/python2.6/SocketServer.py", line 570, in process_request_thread
    self.finish_request(request, client_address)
  File "/usr/lib64/python2.6/SocketServer.py", line 332, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib64/python2.6/SocketServer.py", line 627, in __init__
    self.handle()
  File "server.py", line 30, in handle
    if int(file_size)>recv_size:
ValueError: invalid literal for int() with base 10: '7sdfsdf\n'

另外,我发现如果我在client.py中的sk.send(cmd+"|"+file_name+'|'+str(file_size))sk.send(data)之间插入一个time.sleep(1),错误就会消失。我已经说过我在不同的系统和 python 版本中进行了测试,错误不是因为它们。所以我猜这是因为某些系统配置?我检查了 python.org 中的 socket.send() 和 socket.recv() 但没有找到任何帮助。那么有人可以帮我解释为什么会这样吗?


代码在这里:

#!/usr/bin/env python
#coding:utf-8

################
#This is server#
################

import SocketServer
import os

class MyServer(SocketServer.BaseRequestHandler):
    def handle(self):
        base_path = '/home/ftp/file'
        conn = self.request
        print 'connected...'
        while True:
            #####receive pre_data: we should get data like 'put|/home/aaa|7'
            pre_data = conn.recv(1024)
            print 'pre_data:' + pre_data
            cmd,file_name,file_size = pre_data.split('|')
            print 'cmd: ' + cmd
            print 'file_name: '+ file_name
            print 'file_size: '+ file_size
            recv_size = 0
            file_dir = os.path.join(base_path,file_name)
            f = file(file_dir,'wb')
            Flag = True
            ####receive 1024bytes each time
            while Flag:
                if int(file_size)>recv_size:
                    data = conn.recv(1024)
                    recv_size+=len(data)
                else:
                    recv_size = 0
                    Flag = False
                    continue

                f.write(data)
            print 'upload successed.'
            f.close()

instance = SocketServer.ThreadingTCPServer(('127.0.0.1',9999),MyServer)
instance.serve_forever()


#!/usr/bin/env python
#coding:utf-8


################
#This is client#
################

import socket
import sys
import os

ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

while True:
    input = raw_input('path:')
    #####we should input like  'put|/home/aaa.txt'
    cmd,path = input.split('|')
    file_name = os.path.basename(path)
    file_size=os.stat(path).st_size
    sk.send(cmd+"|"+file_name+'|'+str(file_size))
    send_size = 0
    f= file(path,'rb')
    Flag = True
    #####read 1024 bytes and send it to server each time
    while Flag:
        if send_size + 1024 >file_size:
            data = f.read(file_size-send_size)
            Flag = False
        else:
            data = f.read(1024)
            send_size+=1024
        sk.send(data)
    f.close()

sk.close()

TCP 是数据流。那就是问题所在。 TCP 不需要保持消息边界。所以当客户调用类似

的东西时
connection.send("0123456789")
connection.send("ABCDEFGHIJ")

然后像

这样的天真的服务器
while True;
    data = conn.recv(1024)
    print data + "_"

可以打印以下任何一项:

 0123456789_ABCDEFGHIJ_
 0123456789ABCDEFGHIJ_
 0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_G_H_I_J_

服务器没有机会识别客户端调用了多少次发送,因为客户端的 TCP 堆栈刚刚将数据插入到流中,服务器必须能够处理在与客户端使用的缓冲区数量不同的缓冲区中接收到的数据.

您的服务器必须包含分离 header 和数据的逻辑。所有基于 TCP 的应用协议都使用一种机制来识别应用层边界。例如 HTTP 用空行分隔 headers 和 body,并在单独的 header.

中通知 body 长度

当服务器收到 header 时,您的程序正常工作,其中命令、名称和大小位于单独的缓冲区中,当客户端足够快并快速将数据推送到流中并且服务器读取 header 和一个块中的数据。