Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python中的套接字问题_Python_Sockets - Fatal编程技术网

python中的套接字问题

python中的套接字问题,python,sockets,Python,Sockets,我有一个用C编写的服务器,我想用python编写一个客户端。当python客户端想要发送文件时,它将发送一个字符串“send some_file”,后跟文件的内容,以及字符串“end some_file”。这是我的客户代码: file = sys.argv[1] host = sys.argv[2] port = int(sys.argv[3]) sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect((host

我有一个用C编写的服务器,我想用python编写一个客户端。当python客户端想要发送文件时,它将发送一个字符串“send some_file”,后跟文件的内容,以及字符串“end some_file”。这是我的客户代码:


file = sys.argv[1]
host = sys.argv[2]
port = int(sys.argv[3])
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
send_str = "send %s" % file
end_str = "end %s" % file
sock.send(send_str)
sock.send("\n")
sock.send(open(file).read())
sock.send("\n")
sock.send(end_str)
sock.send("\n")
问题是:

  • 服务器从recv接收“发送一些文件”字符串

  • 在第二次recv时,文件的内容和“结束文件”字符串一起发送

在服务器代码中,缓冲区的大小是4096。我第一次注意到这个错误是在试图发送一个小于4096k的文件时。
如何确保服务器独立地接收字符串?

使用套接字编程,即使您进行两次独立的发送,也不意味着另一方将作为两个独立的RECV接收字符串

对字符串和二进制数据都有效的一个简单解决方案是:首先发送消息中的字节数,然后发送消息

以下是您应该为每条消息(无论是文件还是字符串)执行的操作:

发送方:

  • 发送4个字节,保存以下发送中的字节数
  • 发送实际数据
接收器端:

  • 从接收器端执行一个循环,该循环在读取上阻塞4个字节
  • 然后对前面4个字节中指定的字符数执行读取块以获取数据
除了上面提到的4字节长的头文件外,还可以添加一个常量大小的命令类型头文件(同样是整数),它描述了下面recv中的内容


你也可以考虑使用一个像HTTP这样的协议,它已经为你做了大量的工作,并且有很好的包装库。

TCP/IP数据被缓冲,或多或少随机。 它只是一个字节流。如果需要,您可以读取它,就像它由“\n”字符分隔一样。然而,它并没有被分解成有意义的块;也不可能。它必须是连续的字节流

你是怎么读C语言的?您正在读取一个“\n”吗?或者你只是在读取缓冲区中的所有内容

如果您正在读取缓冲区中的所有内容,那么您应该可以看到或多或少随机缓冲的行

但是,如果您最多读取一个“\n”,您将一次看到每一行

如果你想让它真正起作用,你应该阅读。这说明了如何简单可靠地传输文件:使用两个套接字。一个用于命令,另一个用于数据。

可能使用

sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

这将有助于发送每个数据包,因为这会禁用,因为大多数TCP堆栈使用它将多个小数据包连接在一起(我相信默认情况下是打开的)

我可以想出两种更简单的方法来解决这个问题。两者都涉及到客户端和服务器行为的一些变化

第一种是使用填充。假设您正在发送一个文件。您要做的是读取该文件,将其编码为Base64等更简单的格式,然后发送足够的空格字符来填充4096字节“chunk”的其余部分。你要做的是这样的:

from cStringIO import StringIO
import base64
import socket
import sys

CHUNK_SIZE = 4096 # bytes

# Extract the socket data from the file arguments
filename = sys.argv[1]
host = sys.argv[2]
port = int(sys.argv[3])
# Make the socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
# Prepare the message to send
send_str = "send %s" % (filename,)
end_str = "end %s" % (filename,)
data = open(filename).read()
encoded_data = base64.b64encode(data)
encoded_fp = StringIO(encoded_data)
sock.send(send_str + '\n')
chunk = encoded_fp.read(CHUNK_SIZE)
while chunk:
    sock.send(chunk)
    if len(chunk) < CHUNK_SIZE:
        sock.send(' ' * (CHUNK_SIZE - len(chunk)))
    chunk = encoded_fp.read(CHUNK_SIZE)
sock.send('\n' + end_str + '\n')
import socket
import sys

# Extract the socket data from the file arguments
filename = sys.argv[1]
host = sys.argv[2]
port = int(sys.argv[3])
# Make the socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
# Prepare the message to send
data = open(filename).read()
send_str = "send %d %s" % (len(data), filename)
end_str = "end %s" % (filename,)
sock.send(send_str + '\n')
sock.send(data)
sock.send('\n' + end_str + '\n')

无论哪种方式,您都必须对服务器和客户端进行更改。最后,用C实现一个基本的HTTP服务器(或者得到一个已经实现的服务器)可能会更容易,因为这似乎就是您在这里要做的。编码/填充解决方案速度很快,但会创建大量冗余发送的数据(因为Base64通常会导致发送的数据量增加33%),长度前缀的解决方案从客户端也很容易,但在服务器上可能更困难。

如何确保它不会与另一个字符串一起发送?telnet lib模块实现telnet协议——考虑HTTP不是答案。@ GEO:它总是以一批字节的形式发送——边界是在内部确定的。TCP/IP。你读了一个尺码;然后你读了那么多字节。然后读取大小等。TCP/IP数据包和缓冲区与指定的大小无关。它将一起运行。为什么?1) FTP不应该是所有套接字编程的模型,它是一个非常古老的协议,当1个套接字可用时,没有现代理由使用2个套接字。2) 拥有2个套接字将导致NAT遍历出现问题。3) 关于\n的内容是完全错误的,与TCP/IP无关。您知道,关于两个套接字的部分可能是一个好的建议。事实并非如此,请仔细阅读此问题:@S.Lott:您可能使用了一个一次可以读取1\n的库,但这与TCP/IP无关。这个库的实现方式是一次读取整个缓冲区,并且只返回到\n之前的第一个字符。并为下一次读取缓冲其余部分。@Brian R.Bondy:套接字读取需要缓冲区大小,1是合法的缓冲区大小。协议栈完全能够从传入流中传递单个字节。如果没有,则读取块。如果数据包中有可用的数据,您就可以得到该数据。这不能保证#of reads==#of writes,但不能保证原始写入与结果读取匹配。这是一条单一的河流bytes@S.Lott当前位置我不会说没有什么可以保证这一点。您只能发送1个字节,然后关闭套接字,然后您将确保只有1个读取,这将给出发送的内容或套接字错误。所以在这种情况下,读的次数=写的次数。