在客户端和服务器之间传输文件(套接字错误和属性错误) [英] Transferring file between client and server (Socket error and Attribute error)
问题描述
我正在尝试通过TCP套接字发送和接收文件
I’m trying to send and receive file through TCP socket
有很多问题
1.当客户端连接到服务器。服务器未打印连接的客户端..,但使用命令后将进行打印。
2.当我在服务器上使用 put命令时,发生错误socket.error:[Errno 107]传输端点未连接,但文件图像已上传到服务器。
3.在客户端上使用 get命令时。我无法继续使用其他命令。
4.最后一个问题是客户端无法退出并从服务器列出文件。它显示AttributeError:'module'对象没有属性'send'
There are a lot of problems
1. When the client connects to the server. The server does not "print Client connected .." but it prints after using the command.
2. When I use the ‘put’ command at the server occur an error socket.error: [Errno 107] Transport endpoint is not connected but the file image is already uploaded to the server.
3. When I use the ‘get’ command at the client. I can’t continue to use another command.
4. The last problem is the client can’t quit and list file from the server. It shows AttributeError: 'module' object has no attribute 'send'
Server
import socket
import sys
import os
HOST = 'localhost'
PORT = 3820
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.bind((HOST, PORT))
socket.listen(1)
while (1):
conn, addr = socket.accept()
print 'Client connected ..'
reqCommand = conn.recv(2048)
print 'Client> %s' %(reqCommand)
if (reqCommand == 'quit'):
break
#list file on server
elif (reqCommand == 'lls'):
start_path = os.listdir('.') # server directory
for path,dirs,files in os.walk(start_path):
for filename in files:
print os.path.join(filename)
else:
string = reqCommand.split(' ', 1) #in case of 'put' and 'get' method
reqFile = string[1]
if (string[0] == 'put'):
with open(reqFile, 'wb') as file_to_write:
while True:
data = socket.recv(1024)
# print data
if not data:
break
# print data
file_to_write.write(data)
file_to_write.close()
break
print 'Receive Successful'
elif (string[0] == 'get'):
with open(reqFile, 'rb') as file_to_send:
for data in file_to_send:
conn.sendall(data)
print 'Send Successful'
conn.close()
socket.close()
Client
import socket
import sys
import os
HOST = 'localhost'
PORT = 3820
def put(commandName):
socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket1.connect((HOST, PORT))
socket1.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'rb') as file_to_send:
for data in file_to_send:
socket1.sendall(data)
print 'PUT Successful'
socket1.close()
return
def get(commandName):
socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket1.connect((HOST, PORT))
socket1.send(commandName)
string = commandName.split(' ', 1)
inputFile = string[1]
with open(inputFile, 'wb') as file_to_write:
while True:
data = socket1.recv(2048)
# print data
if not data:
break
# print data
file_to_write.write(data)
file_to_write.close()
print 'GET Successful'
socket1.close()
return
msg = raw_input('Enter your name: ')
while(1):
print 'Instructions'
print '"put [filename]" to upload the file to the server '
print '"get [filename]" to download the file from the server '
print '"ls" to list all files in the client'
print '"lls" to list all files in the server'
print '"quit" to exit'
sys.stdout.write('%s> ' % msg)
inputCommand = sys.stdin.readline().strip()
if (inputCommand == 'quit'):
socket.send('quit')
break
#list file on client
elif (inputCommand == 'ls'):
start_path = os.listdir('.') # client directory
print start_path
#list file on server
elif (inputCommand == 'lls'):
socket.send('lls')
else:
string = inputCommand.split(' ', 1)
if (string[0] == 'put'):
put(inputCommand)
elif (string[0] == 'get'):
get(inputCommand)
socket.close()
推荐答案
除其他外,您需要在传输协议中添加成帧。当您在流套接字上执行 send
时,数据将被添加到缓冲区中,缓冲区最终将被传递到另一端。但是,该缓冲区的大小不传输到另一侧。换句话说,假设您使用命令 put myfile.txt
发送初始段。然后,您从 myfile.txt
发送数据。现在,因为您正在使用文件对象迭代器(用于file_to_send
中的数据),所以实际上您一次要向其发送一行(可以说,对于文件传输协议,它将读取和发送固定的块更有意义,但这也可以工作)。假设 myfile.txt
的第一行是快速棕色狐狸
Among other things, you need to add "framing" to your transfer protocol. When you do a send
on a stream socket, the data gets added to a buffer that will eventually be delivered to the other side. However, the size of that buffer is not transmitted to the other side. In other words, say you send an initial segment with the command "put myfile.txt"
. Then you send the data from myfile.txt
. Now because you are using the file object iterator (for data in file_to_send
), you are actually sending it a line at a time (arguably, for a file transfer protocol, it would make more sense to read and send fixed chunks but this would work too). Let's assume the first line of myfile.txt
is "The quick brown fox\n"
服务器首次接收时,它可能会接收 put
或 put myfile.txt
或 put myfile.txt快速的棕色狐狸
或 put
命令加上整个文件的内容。那是因为流协议(TCP)不为您维护消息边界。
When the server does its first receive, it could receive "put "
or "put myfile.txt"
or "put myfile.txtThe quick brown fox\n"
or the put
command plus the entire file contents. That's because the stream protocol (TCP) does not maintain message boundaries for you.
现在,实际上,您可以 >在第一次接收中仅接收 put myfile.txt
,但是指望这样做是非常不明智的,因为这取决于两者上各种因素的时机发送和接收系统无法控制的系统。
Now in practice, you may be receiving only the "put myfile.txt"
in the first receive, but it's very unwise to count on that because it's dependent on the timing of all sorts of factors on both sending and receiving systems that are outside your control.
因此,有两种常见的处理方法:
So, there are two common ways of handling this:
-
在开头添加一个长度,该长度描述命令的大小和任何命令参数(以便您知道要传输的实际文件数据在流中的何处开始)。 (大多数二进制文件传输协议都是以这种方式工作的。)
Add a length at the beginning that delineates the size of the command and any command argument (so that you know where in the stream the actual file data to be transferred begins). (Most binary file transfer protocols work this way.)
在命令末尾添加一些已知的定界符-例如, '\n'
。例如,HTTP就是这样工作的。
Add some known delimiter at the end of your command -- for example, '\n'
. HTTP, for example, works this way.
同样,您的接收方也需要确保它正在读取每个点所需的数量,以保留要传输的文件的全部内容。
And likewise your receiving side needs to ensure that it is reading exactly the amount needed at each point in order to preserve the full content of the file being transferred.
这意味着您要么(a)小心地 recv
放入缓冲区,然后精确分配命令所需的内容,并确保其余的将在以后作为文件数据处理。选项(b)通常可以通过在套接字顶部构建文件对象来实现(请参见 socket.makefile
),然后使用文件对象的 readline
方法仅获取第一行。
That means you either (a) are careful to recv
exactly the number of bytes you need for the command data, then separately process the file content, or (b) recv
an initial chunk of data into a buffer, then carve off exactly what you need for the "command", and ensure the rest will be processed later as file data. Option (b) can often be accomplished by building a file object on top of the socket (see socket.makefile
), then using the file object's readline
method to obtain only the first line.
代码的其他问题:
-
socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
行是一个非常糟糕的主意。通过创建具有相同名称的变量,您刚刚隐藏了整个socket
模块。例如,如果您尝试在下一行再次引用socket.AF_INET
,则会出现异常AttributeError:'_socketobject'对象具有没有属性 AF_INET
。您应该使用其他名称来命名变量,就像在客户端一样使用socket1
。
The line
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
is a very bad idea. You have just hidden the entiresocket
module by creating a variable with the same name. For example, if you attempted to refer tosocket.AF_INET
again on the subsequent line, you'd get the exceptionAttributeError: '_socketobject' object has no attribute 'AF_INET'
. You should name the variable something else, saysocket1
as you did on the client side.
在在客户端,您遇到了相反的问题。您正在尝试使用套接字 object 方法,但改为提供 socket
模块对象。 (这就是为什么出现 AttributeError:'module'对象没有属性'send'
的原因。)您需要重新组织代码,以便调用 send就像在
方法。 put
和 get
函数中所做的一样,在已连接的套接字对象上使用
On the client side, you have the opposite problem. You're attempting to use a socket object method, but providing the socket
module object instead. (That's why you get AttributeError: 'module' object has no attribute 'send'
.) You need to reorganize your code so that you call the send
method on a connected socket object as you are doing in the put
and get
functions.
错误 socket.error:[Errno 107]传输端点未连接
的原因是尝试在侦听套接字上尝试 recv
,而不在已连接的套接字( conn
上,它由<$ c $返回c> socket.accept )。您可以使用监听套接字执行的唯一操作是接受
新连接(或关闭
)。
The error socket.error: [Errno 107] Transport endpoint is not connected
occurs because you are attempting to recv
on the listening socket, not the connected one (conn
-- which is returned by socket.accept
). The only thing you can do with a listening socket, is accept
new connections (or close
).
您应该使用 sendall
而不是 send
来确保每个字节都被发送。通常,所有数据也会通过发送
发送,但是在某些极端情况下,这种情况不会发生。
You should be using sendall
instead of send
to ensure that every byte gets sent. Generally, all the data will get sent with send
too, but there are corner cases where that doesn't happen.
服务器中的文件接收循环以开始,而True:
开始,但随后总是中断
。因此,它将仅接收第一个数据块(最大1024字节)。如果文件大于该文件,您肯定会最终将其截断。
Your file-receiving loop in the server begins with while True:
but then always breaks
. Hence it will only receive the first chunk of data (up to 1024 bytes). If the file is larger than that, you will certainly end up truncating it.
您的服务器文件列表功能( lls
命令)不会将其输出发送回客户端,而只是将其输出到服务器端的控制台。 (并且不要忘记,您需要在发送回的文件名之间提供某种分隔符,否则它们最终将全部串联成一个大字符串。)
Your server file listing function (lls
command) is not sending its output back to the client, it's only printing it to the server side's console. (And don't forget, you will need to provide a delimiter of some kind between the file names you send back or they will end up all concatenated into a single big string.)
这篇关于在客户端和服务器之间传输文件(套接字错误和属性错误)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!