热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python的Socket编程过程中实现UDP端口复用的实例分享

这篇文章主要介绍了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() 

感触良多,底层的知识比较重要,浮沙筑高台果然危险。

推荐阅读
  • MQTT技术周报:硬件连接与协议解析
    本周开发笔记重点介绍了在新项目中使用MQTT协议进行硬件连接的技术细节,涵盖其特性、原理及实现步骤。 ... [详细]
  • 本文介绍如何在现有网络中部署基于Linux系统的透明防火墙(网桥模式),以实现灵活的时间段控制、流量限制等功能。通过详细的步骤和配置说明,确保内部网络的安全性和稳定性。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
  • 本文深入探讨了MAC地址与IP地址绑定策略在网络安全中的应用及其潜在风险,同时提供了针对该策略的破解方法和相应的防御措施。 ... [详细]
  • NFS(Network File System)即网络文件系统,是一种分布式文件系统协议,主要用于Unix和类Unix系统之间的文件共享。本文详细介绍NFS的配置文件/etc/exports和相关服务配置,帮助读者理解如何在Linux环境中配置NFS客户端。 ... [详细]
  • 本文详细介绍如何使用Samba软件配置CIFS文件共享服务,涵盖安装、配置、权限管理及多用户挂载等关键步骤。通过具体示例和命令行操作,帮助读者快速搭建并优化Samba服务器。 ... [详细]
  • 在现代网络环境中,两台计算机之间的文件传输需求日益增长。传统的FTP和SSH方式虽然有效,但其配置复杂、步骤繁琐,难以满足快速且安全的传输需求。本文将介绍一种基于Go语言开发的新一代文件传输工具——Croc,它不仅简化了操作流程,还提供了强大的加密和跨平台支持。 ... [详细]
  • 本文深入探讨了计算机网络的基础概念和关键协议,帮助初学者掌握网络编程的必备知识。从网络结构到分层模型,再到传输层协议和IP地址分类,文章全面覆盖了网络编程的核心内容。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • NTP服务器配置详解:原理与工作模式
    本文深入探讨了网络时间协议(NTP)的工作原理及其多种工作模式,旨在帮助读者全面理解NTP的配置参数和应用场景。NTP是基于RFC 1305的时间同步标准,广泛应用于分布式系统中,确保设备间时钟的一致性。 ... [详细]
  • 访问一个网页的全过程
    准备:DHCPUDPIP和以太网启动主机,用一根以太网电缆连接到学校的以太网交换机,交换机又与学校的路由器相连.学校的这台路由器与一个ISP链接,此ISP(Intern ... [详细]
  • 三菱PLC SLMP协议报文详解
    本文详细解析了三菱PLC中使用的SLMP协议报文结构,包括其工作原理、通信流程及报文格式,旨在帮助工程师和技术人员更好地理解和运用这一协议。 ... [详细]
  • 本文将详细探讨 Linux 系统中的 netstat 命令,该命令用于查看网络状态和连接情况。通过了解 IP 地址和端口的基本概念,我们将更好地理解如何利用 netstat 命令来监控和管理网络服务。 ... [详细]
  • TCP/IP基础知识详解
    本文详细介绍了TCP/IP协议的基本概念,包括网络层次模型、TCP三次握手过程、四次挥手过程以及TCP与UDP的比较。通过这些内容,读者可以更好地理解TCP/IP协议的工作原理。 ... [详细]
author-avatar
robioven
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有