星期三, 八月 08, 2007

python socket write/read only

最初服务器端的代码是这样的:
while True:
try:
......
# When modified, inform the client first:
conn.sendall("NEXT")
data = conn.recv(1024)
if data != "OK":
raise ServerExc("Sync failed")
sending = []
while sn < serial:
action, target = self.manager.wmlog[sn]
sending.append("%s:%s\n" % (action, target))
if debug:
print "after writing"
sn += 1
self.manager.svPool[self] = sn
# Assign must be an atomic operation, but dict[k] and getattr?
# But I think it is not necessary to Lock at here.
fconn.writelines(sending)
fconn.write("EOF\n")
fconn.flush()
客户端是这样的(同样在一个循环中):
data = conn.recv(1024)
self.failUnlessEqual(data, "NEXT")
conn.sendall("OK")
result = []
for i in range(incr):
line = fconn.readline()[:-1]
if line == 'EOF': break
result.append(line)
这时候,相当于每次服务器端要发送数据之前,都会先通知客户端,并等待客户端的一个回执。这时我考虑,因为两边都在循环,所以我只需要在服务器端不断的写入,在客户端不断的读取就可以了,因为从这里直到线程退出都不会在发送别的类型的数据。

那么就写成这样:
while True:
try:
......
while sn < serial:
action, target = self.manager.wmlog[sn]
fconn.write("%s:%s\n" % (action, target)) # OR:
# conn.sendall("%s:%s\n" % (action, target))
if debug:
print "after writing"
sn += 1
self.manager.svPool[self] = sn
# Assign must be an atomic operation, but dict[k] and getattr?
# But I think it is not necessary to Lock at here.
客户端:
result = []
for i in range(incr):
line = fconn.readline()[:-1]
result.append(line)
但这时就有一个问题,就是之前采用回执办法的时候,客户端退出时服务器端的 conn.recv() 总会得到空,因此可以通过判断来决定是否要关闭这个 socket(但如果是异常终止呢?)。

而现在服务器端不再做判断,如果客户端退出或异常终止,服务器端会出现什么情况呢?使用一个原型来看看:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Prototype: server write only, client read only,
if client terminated, what about the server?
This is the server side: write only

Author: Roc Zhou
Date: 2007-08-08
Email: chowroc.z@gmail.com
"""

import sys
import time
import socket
import traceback
import threading

def server(conn):
name = threading.currentThread().getName()
print "Client server: %s" % name
# print "Got connection from: ", conn.getpeername()
conn.sendall("START")
data = conn.recv(1024)
if data != "OK": raise "FAILED"
fconn = conn.makefile('w', 0)
i = 0
while True:
# conn.sendall("message %d" % i)
fconn.write("message %d\n" % i)
i += 1
time.sleep(5)
print "Working thread of %s ending ..." % name
conn.close()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('localhost', 2123))
print "Waiting for connections ..."
sock.listen(5)

while True:
try:
conn, addr = sock.accept()

print "Got connection from:", addr
that = threading.Thread(target=server, name='server', args=(conn,))
that.setDaemon(1)
that.start()
except KeyboardInterrupt:
raise
except:
print >> sys.stderr, "CATCH ALL:"
traceback.print_exc()
continue
客户端:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Prototype: server write only, client read only,
if client terminated, what about the server?
This is the client side: read only

Author: Roc Zhou
Date: 2007-08-08
Email: chowroc.z@gmail.com
"""

import socket

host = 'localhost'
port = 2123

print "Creating socket ..."
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Connecting ..."
conn.connect((host, port))
print "Done."

data = conn.recv(1024)
if data != "START": raise "FAILED"
conn.sendall("OK")
fconn = conn.makefile('r', 0)

while True:
try:
message = fconn.readline()[:-1]
print message
except KeyboardInterrupt:
raise
运行及其输出

客户端:
sh$ python fs_mirror_10_read_only.py
Creating socket ...
Connecting ...
Done.
message 0
message 1
message 2
message 3
message 4
message 5
message 6
Traceback (most recent call last):
File "fs_mirror_10_read_only.py", line 32, in
message = fconn.readline()[:-1]
File "/usr/lib/python2.5/socket.py", line 331, in readline
data = recv(1)
KeyboardInterrupt


服务器端:
sh# python mirrord_10_write_only.py
Waiting for connections ...
Got connection from: ('127.0.0.1', 38618)
Client server: server
Exception in thread server:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 460, in __bootstrap
self.run()
File "/usr/lib/python2.5/threading.py", line 440, in run
self.__target(*self.__args, **self.__kwargs)
File "mirrord_10_write_only.py", line 31, in server
fconn.write("message %d\n" % i)
File "/usr/lib/python2.5/socket.py", line 262, in write
self.flush()
File "/usr/lib/python2.5/socket.py", line 249, in flush
self._sock.sendall(buffer)
error: (32, 'Broken pipe')
或者当不使用 time.sleep(5) 时,服务器端的出错信息如下:
Waiting for connections ...
Got connection from: ('127.0.0.1', 56912)
Client server: server
Exception in thread server:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 460, in __bootstrap
self.run()
File "/usr/lib/python2.5/threading.py", line 440, in run
self.__target(*self.__args, **self.__kwargs)
File "mirrord_10_write_only.py", line 30, in server
fconn.write("message %d\n" % i)
File "/usr/lib/python2.5/socket.py", line 262, in write
self.flush()
File "/usr/lib/python2.5/socket.py", line 249, in flush
self._sock.sendall(buffer)
error: (104, 'Connection reset by peer')
总之,会抛出一个 socket.error 的异常,可以通过捕捉这个异常来退出。

但如果使用一边只读,一边只写的情况,就必须要考虑到 socket 对于缓冲(buffers)的使用及其与阻塞(blocking)之间的关系了!

现在的一个问题就是,到底采用那种方式更好呢?理由何在?

没有评论: