python监听多个udp端口_Python的Socket编程过程中实现UDP端口复用的实例分享

关于端口复用

一个套接字不能同时绑定多个端口,如果客户端想绑定端口号,一定要调用发送信息函数之前绑定( 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()

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android系统的开机画面显示过程分析

提到Android系统的UI,我们最先接触到的便是系统在启动过程中所出现的画面了。Android系统在启动的过程中,最多可以出现三个画面,每一个画面都用来描述一个不同的启动阶段。本文将详细分析这三个开机画面的显示过程,以便可以开启我…

arcgis报错常用解决方法

1.输出路径不要改动,选择arcgis默认的数据库,运行完后再将数据导出一份 2.输出文件名以字母开头(个人尝试有时候数字开头会报错) 3.确保自己路径中没有中文 4.再次运行工具箱,第一次运行的时候可能程序调用会出错 …

windows和linux的内存管理

windows的内存管理很是严谨,使用内存必须首先分配,当然每个操作系统都是这样,然而windows的严谨在于分配的过程,分为保留和提交两个阶段,其中保留的含义就是在进程的虚拟地址空间保留一块空间,不能用作他用…

python垃圾邮件识别_【Python】垃圾邮件识别

下载W3Cschool手机App,0基础随时随地学编程导语利用简单的机器学习算法实现垃圾邮件识别。让我们愉快地开始吧~相关文件密码: qa49数据集源于网络,侵歉删。开发工具Python版本:3.6.4相关模块:scikit-learn模块;jieba模…

修改Linux内核的启动Logo和禁用启动光标

Linux内核下使用的图片文件类型是pnm和PPm格式的,所以在开始介绍修改linux内核启动LOGO之前, 1,先需要介绍一下怎么样设计自己开始logo的ppm图片,首先选择一张png格式的图片 使用如下命令确保你必须安装以下的工具(pngtopnm,pnmqu…

JavaScript 判断浏览器类型

var Sys {}; var ua navigator.userAgent.toLowerCase(); var s; (s ua.match(/msie ([\d.])/)) ? Sys.ie s[1] : (s ua.match(/firefox\/([\d.])/)) ? Sys.firefox s[1] : (s ua.match(/chrome\/([\d.])/)) ? Sys.chrome s[1] : (s ua.match(/opera.(…

python time perf_Python Time 的学习笔记

PyNotes-timePyNotes(2)关于time的简单介绍参考资料概述time模块 时间戳的获取、时间格式的转换和程序运行时间的计算。方法时间的获取time.time()以floa浮点数获取当前时间戳,即计算机内部时间值,epoch 是1970年1月1日00:00:00(UTC)>>> time.t…

Linux logo和屏幕光标

logo和屏幕光标 Linux默认开机LOGO一般都是80x80的小企鹅图标,有时候为了一些效果,希望在Linux 启动过程中,全屏看到用户自定义的LOGO,这就需要为Linux增加新的LOGO。 准备png图片 使用任何图片软件,制作一张自定义…

arcgis选出点规定范围的面

示例数据如下 对点数据进行缓冲区建立 打开缓冲区工具箱 主要设置输入数据和距离(缓冲范围) 缓冲结果如下 接下来进行空间连接 输入目标要素(选出的面)和连接要素(缓冲的结果) 打开空间连接的属性表 选中我…

函数实现-aoti-atol

1. 函数原型 int atoi ( const char * str ); long int atol ( const char * str ); 2. 函数工作 atoi是将字符串转化为整型,atol是将字符串转化为长整型,这两个函数的实现十分相似。工作步骤基本如下: 跳过若干空格、制表符等 如果有…

python2卸载后yum不可用_centos7误删除python2导致的python和yum不可用处理-阿里云开发者社区...

centos7查看版本cat /etc/redhat-release // 我这边是 CentOS Linux release 7.6.1810 (Core)强制删除已安装程序及其关联rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps删除所有残余文件 ##xargs,允许你对输出执行其他某些命令whereis python |xargs rm -frv验证删…

arcgis批量按掩膜提取栅格

新建工具箱 迭代要素类 插入栅格

SIP协议学习1

SIP协议是由IETF提出的在IP网络上进行多媒体通信的应用层控制协议。采用分层的方法来创建服务,是应用层上的一个控制协议,用来创建,修改和终止有多个参与者的多媒体会话进程。参与会话的成员可以通过组播,单播或者两者结合的方式进…

一些关于罗马字符的知识

I 1 V 5 X 10 L 50 C 100 D 500 M 1000 下面是关于构造罗马数字的一些通用的规则的介绍: 字符是叠加的。I 表示 1,II 表示 2,而 III 表示 3。VI 表示 6 (字面上为逐字符相加,“5 加 1”),VII 表示 7&#xff0c…

python 分词 识别_python分词如何实现新词识别

2013-12-19 回答# -*- coding: utf-8 -*-import jiebacreated on 2015-11-23def word_split(text):"""split a text in words. returns a list of tuple that contains(word, location) location is the starting byte position of the word."""…

Oracle数据库游标操作

1、含有参数的游标 declare cursor cur_my (mv number) is select * from Person where no<mv;begin for tem in cur_my(4) loop DBMS_OUTPUT.put_line(name:||tem.name); end loop;end; 2、设置引用游标declare temp_row Person%rowtype; type my_type is ref curs…

SIP协议学习2-pjsip

一、 在windows下利用vc6.0编译pjsip源码 首先阅读文档readme.txt&#xff0c;查找在win32平台源码的编译方法。自己总结方法如下&#xff1a; a.设置pjsua为当前活动工程 b.因为编译的时候提醒缺少config_site.h文件&#xff0c;所以在pjlib/include/pj/下新建一个空的c…

python库快速安装_python的pip快速安装代码

pip install xx,经常由于网速&#xff0c;或者安装版本问题导致安装速度慢超时等问题&#xff0c;现提供一个py镜像安装代码&#xff0c;安装库文件前执行下这个程序&#xff0c;可以很快下载cmd 进入命令提示符python .py文件位置pip install xx 安装库的名称import osini&quo…