最近用python的Socket写了一个传输通讯测试工具,但是发现在Server端调用close方法后,如果循环没有break的话,此连接还可以继续用来发送和接收数据。所以,我就觉得很是奇怪,难道close方法关闭的连接没有起作用吗?经过试验后,确实如此,以下是我的事例代码,
Server端代码:
from socket import *
import threading,os,time
class Server():
def __init__(self,host='127.0.0.1',port=9990):
try:
addr=(host,port)
self.tcpSerSock=socket(AF_INET,SOCK_STREAM)
self.tcpSerSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
self.tcpSerSock.bind(addr)
self.tcpSerSock.listen(5)
except Exception,e :
print 'ip or port error :',str(e)
self.tcpSerSock.close()
def main(self):
while 1 :
try :
print 'wait for connecting ...'
tcpCliSock,addr = self.tcpSerSock.accept()
addrStr = addr[0]+':'+str(addr[1])
print 'connect from',addrStr
except KeyboardInterrupt:
self.close=True
tcpCliSock.close()
self.tcpSerSock.close()
print 'KeyboardInterrupt'
break
ct = ClientThread(tcpCliSock,addrStr)
ct.start()
class ClientThread(threading.Thread):
def __init__(self,tcpClient,addr):
super(ClientThread,self).__init__()
self.tcpClient = tcpClient
self.addr = addr
self.timeout = 60
tcpClient.settimeout(self.timeout)
self.cf = tcpClient.makefile('rw',0)
def run(self):
while 1:
try:
data = self.cf.readline().strip()
if data:
if data.find("set time")>=0:
self.timeout = int(data.replace("set time ",""))
self.tcpClient.settimeout(self.timeout)
print self.addr,"client say:",data
self.cf.write(str(self.addr)+" recevied ok!"+"\n")
else:
break
except Exception,e:
self.tcpClient.close()
self.cf.write("time out !"+"\n")
print self.addr,"send message error,",str(e)
#此处将break注释掉
# break
if __name__ == "__main__" :
ser = Server()
ser.main()
Client端代码:
from socket import *
class Client():
def __init__(self):
pass
def main(self):
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(('127.0.0.1',9990))
print 'connect server 9999 successfully !'
cf = tcpCliSock.makefile('rw', 0)
while 1:
data=raw_input('>')
try:
if data:
cf.write(data+"\n")
data = cf.readline().strip()
if data:
print "server say:",data
else:
break
else:
break
except Exception,e:
print "send error,",str(e)
if __name__ == "__main__":
cl = Client()
cl.main()
在代码中可以看出,如果timeout后,except肯定能够捕获到timeout异常,这样就会进入到except代码中,在上面我们故意将break注释掉,也就是不让其跳出循环,经过试验,可以得知,虽然在server端已经将连接close掉了,但是client端仍然可以顺利的接收到消息,而且,如果client端发送数据的间隔小于超时时间的话,此连接可以顺利的一直使用,这样,我们close貌似就一点儿效果都没有了,经过在百度搜索,一直米有找到解决办法,最后还是硬着头皮去看鸟语文档,下面是官方解释:
close()releases the resource associated with a connection but does not necessarily close the connection immediately. If you want to close the connection in a timely fashion, callshutdown() beforeclose().
大体意思是:close方法可以释放一个连接的资源,但是不是立即释放,如果想立即释放,那么请在close之前使用shutdown方法,可是shutdown方法是干什么的呢?
还得继续看鸟语,官方解释如下:
Shut down one or both halves of the connection. If how is SHUT_RD, further receives are disallowed. If how isSHUT_WR, further sends are disallowed. Ifhow is SHUT_RDWR, further sends and receives are disallowed. Depending on the platform, shutting down one half of the connection can also close the opposite half (e.g. on Mac OS X, shutdown(SHUT_WR) does not allow further reads on the other end of the connection).
大体意思是:shutdown方法是用来实现通信模式的,模式分三种,SHUT_RD 关闭接收消息通道,SHUT_WR 关闭发送消息通道,SHUT_RDWR 两个通道都关闭
也就是说,想要关闭一个连接,首先把通道全部关闭,然后在release连接,以上三个静态变量分别对应数字常量:0,1,2
所以,要实现我们的功能,只需要改变server端的代码,也就是
except Exception,e:
self.tcpClient.close()
self.cf.write("time out !"+"\n")
print self.addr,"send message error,",str(e)
#此处将break注释掉
# break
改为:
except Exception,e:
self.tcpClient.shutdown(2)
self.tcpClient.close()
self.cf.write("time out !"+"\n")
print self.addr,"send message error,",str(e)
break
这样的话,在close后,再调用 self.cf.write("time out !"+"\n"),应该就会报异常,此时client再使用此连接向server发数据,就会出错,然后就会顺理成章的走except的代码了。
当然,上面的代码,self.cf.write("time out !"+"\n")代码可以注释掉,我只是想用它来测试是否会出异常而已。