Python中Socket的Close方法假关闭Socket连接的问题

最近用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")代码可以注释掉,我只是想用它来测试是否会出异常而已。





你可能感兴趣的:(python-socket)