国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python的Socket編程過程中實現UDP端口復用的實例分享

2020-01-04 17:35:51
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了Python的Socket編程過程中實現UDP端口復用的實例分享,文中作者用到了Python的twisted異步框架,需要的朋友可以參考下
 

關于端口復用

一個套接字不能同時綁定多個端口,如果客戶端想綁定端口號,一定要調用發送信息函數之前綁定( bind )端口,因為在發送信息函數( sendto, 或 write ),系統會自動給當前網絡程序分配一個隨機端口號,這相當于隨機綁定了一個端口號,這里只會分配一次,以后通信就以這個隨機端口通信,我們再綁定端口號的話,就會綁定失敗。如果我們放在發送信息函數( sendto, 或 write )之前綁定,那樣程序將以我們綁定的端口號發送信息,不會再隨機分配一個端口號。實際上,默認的情況下,如果一個網絡應用程序的一個套接字 綁定了一個端口,這時候,別的套接字就無法使用這個端口。那如何讓兩個套接字都能成功綁定一個端口呢?這時候就需要要到端口復用了。端口復用允許在一個應用程序可以把 n 個套接字綁在一個端口上而不出錯。
端口復用能在系統已開放的端口上進行通訊,只對輸入的信息進行字符匹配,不對網絡數據進行任何攔截、復制類操作,所以對網絡數據的傳輸性能絲毫不受影響。
但要注意,建立連接后服務端程序占用極少系統資源,被控端不會在系統性能上有任何察覺,通常被后門木馬所利用。
在winsock的實現中,對于服務器的綁定是可以多重綁定的,在確定多重綁定使用誰的時候,根據一條原則是誰的指定最明確則將包遞交給誰,而且沒有權限之分,也就是說低級權限的用戶是可以重綁定在高級權限如服務啟動的端口上的,這是非常重大的一個安全隱患。

Python解決UDP端口復用問題
一直覺得UDP協議很簡單,但是今天問題讓我感覺到網絡的基礎真是博大精深。

    廢話少說,來看問題吧。由于協議的需要,我得實現一個UDP的客戶端和服務器端,并且從同一個端口讀寫數據。

    最初不以為然,無非就是用兩個socket,一個監聽并從這個端口讀取數據(服務器端采用了twisted),另一個向這個端口寫入數據,用python實現只要10行左右的代碼。

def startServer(queue, port):   reactor.listenUDP(port, DhtResponseHandler(queue))   reactor.run() def sendUdpMsg(self, addr, msg):   socketHandler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)   socketHandler.bind(("", self.port))   socketHandler.sendto(msg, addr)   socketHandler.close() 

     由于要向同一個端口寫數據,于是client必須有bind,但是運行后發現server先bind了這個端口,client運行時會報錯

 

復制代碼代碼如下:

 

error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted  

 


     一般這種錯誤時因為多個socket不能同時bind同一個地址
     由于基礎不夠扎實,我開始瘋狂的搜索,發現有人說端口復用的問題,所謂的端口復用,是指一個套接字釋放掉一個端口后有一個wait_time,另一個套接字如果接著bind就會報錯。雖然我的問題不完全一樣,但是我欣喜若狂的使用了。即在client bind前加上如下一句

 

socketHandler.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

     但是仍然報錯:

 

復制代碼代碼如下:

 

error: [Errno 10013] An attempt was made to access a socket in a way forbidden by its access permissions  

 


    (順便一提,還有另一個參數叫SO_REUSEPORT,即復用端口,另外有一個叫SO_EXCLUSIVEADDRUSE,即不準復用該端口,其他socket的參數還有很多,可以參考winsockhttp://msdn.microsoft.com/en-us/library/aa924071.aspx或者unix下的socket)
     這個10013錯誤讓我百思不得其解,搜索一下,主要有兩種解釋,有人說是需要提升應用程序的權限為管理員,我用的是eclipse+pydev,提升完eclipse權限沒用,實際上還要修改python.exe的權限,方法是在這個程序上右鍵,兼容性一欄中勾上以系統管理員身份運行;有人說是跟其他程序地址或者端口沖突。但是我測試過發現都不行。

 

     另外,運行的時候發現,twisted的服務器端一定是要在主線程中,否則會報signal一定要在主線程才能接受的錯誤,但是twisted的reactor一運行起來就阻塞了。

     在twisted文檔中翻到,原來還有一種UDP叫做connected UDP,變態吧,所謂connected UDP,就是只能向一個地址收發數據,看起來貌似可以,但是不符合可以向多個地址接收數據。

     最后在一篇文章中翻到說需要兩個端口都設置重用,于是我試著重新寫一個服務器,與之前的客戶端配合,運行良好,完全無錯

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(("", port)) data, address = sock.recvfrom(4096) 

     好吧,看來問題在調用twisted了,不知道他是否有這樣的設置,進去將這部分代碼翻了一下,找不到這樣設置的參數。

class Port(abstract.FileHandle):   def __init__(self, port, proto, interface='', maxPacketSize=8192,          reactor=None):     """     Initialize with a numeric port to listen on.     """     self.port = port     self.protocol = proto     self.readBufferSize = maxPacketSize     self.interface = interface     self.setLogStr()     self._connectedAddr = None      abstract.FileHandle.__init__(self, reactor)      skt = socket.socket(self.addressFamily, self.socketType)     addrLen = _iocp.maxAddrLen(skt.fileno())     self.addressBuffer = _iocp.AllocateReadBuffer(addrLen)     # WSARecvFrom takes an int     self.addressLengthBuffer = _iocp.AllocateReadBuffer(         struct.calcsize('i'))    def startListening(self):     """     Create and bind my socket, and begin listening on it.      This is called on unserialization, and must be called after creating a     server to begin listening on the specified port.     """     self._bindSocket()     self._connectToProtocol()     def createSocket(self):     return self.reactor.createSocket(self.addressFamily, self.socketType)     def _bindSocket(self):     try:       skt = self.createSocket()       skt.bind((self.interface, self.port))     except socket.error, le:       raise error.CannotListenError, (self.interface, self.port, le)      # Make sure that if we listened on port 0, we update that to     # reflect what the OS actually assigned us.     self._realPortNumber = skt.getsockname()[1]      log.msg("%s starting on %s" % (         self._getLogPrefix(self.protocol), self._realPortNumber))      self.connected = True     self.socket = skt     self.getFileHandle = self.socket.fileno 

    難道說twisted就完全不提供這樣的功能?最終在multicast中翻到這樣一段,也就是,多播的情況是支持地址復用的,動手測起來。

class MulticastPort(MulticastMixin, Port):   """   UDP Port that supports multicasting.   """    implements(interfaces.IMulticastTransport)     def __init__(self, port, proto, interface='', maxPacketSize=8192,          reactor=None, listenMultiple=False):     Port.__init__(self, port, proto, interface, maxPacketSize, reactor)     self.listenMultiple = listenMultiple     def createSocket(self):     skt = Port.createSocket(self)     if self.listenMultiple:       skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)       if hasattr(socket, "SO_REUSEPORT"):         skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)     return skt 

     將server端改成如下代碼,運行通過!

reactor.listenMulticast(port, DhtResponseHandler(queue), listenMultiple=True) reactor.run() 

     感觸良多,底層的知識比較重要,浮沙筑高臺果然危險。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 敦煌市| 吉木乃县| 麻江县| 策勒县| 全椒县| 泗阳县| 天水市| 环江| 阿拉尔市| 咸丰县| 肃南| 陕西省| 张家港市| 华阴市| 东台市| 盐山县| 屯门区| 龙岩市| 托里县| 隆安县| 沛县| 东兰县| 青州市| 秀山| 永吉县| 全州县| 湄潭县| 灯塔市| 鹤岗市| 内乡县| 郯城县| 沂源县| 永宁县| 鄂伦春自治旗| 凯里市| 阿坝| 仙居县| 万州区| 黑河市| 甘洛县| 鄯善县|