?一:UDP基础知识:
????????udp没有连接的概念,udp套接字可以从网络上的任何服务器接收数据报,并将数据报发送到网络上的任何主机。数据报可以以任意顺序到达 or 根本不会到达 or 在传输过程中被复制。
? ? ? ? udp中可以使用connect系统调用,udp的connect和tcp的connect有着本质的区别,tcp中调用connect会引起三次握手然后client和server建立连接。udp中调用connect 内核没不干啥事儿,只是检查是否存在立即可知的错误(例如一个显然不可达到的目的地) 然后把对端的ip和端口记录一下而且。udp中可以多次调用connect,而tcp只能调用一次。
? ? ? ? 使用connect建立的udp连接 相比普通udp连接的优势:
优势1:提高发送效率。比如发送2个报文,普通udp连接时内核的操作是:建立连接---->发送第一个报文---->断开连接--->建立连接--->发送第二个报文----->断开连接。而connect udp内核操作是:建立连接----->发送第一个报文----->发送第二个报文---->断开连接,每次发送报文内核都可能要做路由查询。从这来看 connect udp确实高效不少。
优势2:高并发服务中会增加系统稳定性。比如说A通过普通udp和 server B、C通信。B,C提供相同的服务,为了负载均衡,我们让A和B,C交替通信。A 与 B通信 ip_a:port_a <----> ip_b:port_b A 与 C通信ip_a:port_a' <---->ip_c:port_c,在高并发情况下会发生port_a和port_a'相同,那么就有可能出现A等待B的报文却收到了C的报文。对于这种问题解决办法就是采用connect的udp通信方式,在A中建立2个udp,分别去connect B和C。
?注意事项:采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom。调用sendto的时候第五个参数必须是NULL,第六个参数是0.调recvfrom,recv,read系统调用只能获取到先前connect的ip+port发送的报文。
二:Twisted中UDP介绍
由于没有连接,所以对于每个udp套接字我们只能使用一个对象,一个协议。然后使用已经定义的接口twisted.internet.interfaces.IReactorUDP
class IReactorUDP(Interface):
"""
UDP socket methods.
"""
def listenUDP(
port: int, protocol: "DatagramProtocol", interface: str, maxPacketSize: int
) -> "IListeningPort":
"""
Connects a given L{DatagramProtocol} to the given numeric UDP port.
@param port: A port number on which to listen.
@param protocol: A L{DatagramProtocol} instance which will be
connected to the given C{port}.
@param interface: The local IPv4 or IPv6 address to which to bind;
defaults to '', ie all IPv4 addresses.
@param maxPacketSize: The maximum packet size to accept.
@return: object which provides L{IListeningPort}.
"""
twisted中已经帮忙实现了udp协议:twisted.internet.protocol.DatagramProtocol
@implementer(interfaces.ILoggingContext)
class DatagramProtocol(AbstractDatagramProtocol):
"""
Protocol for datagram-oriented transport, e.g. UDP.
@type transport: L{None} or
L{IUDPTransport<twisted.internet.interfaces.IUDPTransport>} provider
@ivar transport: The transport with which this protocol is associated,
if it is associated with one.
"""
def logPrefix(self):
"""
Return a prefix matching the class name, to identify log messages
related to this protocol instance.
"""
return self.__class__.__name__
def connectionRefused(self):
"""
Called due to error from write in connected mode.
Note this is a result of ICMP message generated by *previous*
write.
"""
class AbstractDatagramProtocol:
"""
Abstract protocol for datagram-oriented transports, e.g. IP, ICMP, ARP,
UDP.
"""
transport = None
numPorts = 0
noisy = True
def __getstate__(self):
d = self.__dict__.copy()
d["transport"] = None
return d
def doStart(self):
"""
Make sure startProtocol is called.
This will be called by makeConnection(), users should not call it.
"""
if not self.numPorts:
if self.noisy:
log.msg("Starting protocol %s" % self)
self.startProtocol()
self.numPorts = self.numPorts + 1
def doStop(self):
"""
Make sure stopProtocol is called.
This will be called by the port, users should not call it.
"""
assert self.numPorts > 0
self.numPorts = self.numPorts - 1
self.transport = None
if not self.numPorts:
if self.noisy:
log.msg("Stopping protocol %s" % self)
self.stopProtocol()
def startProtocol(self):
"""
Called when a transport is connected to this protocol.
Will only be called once, even if multiple ports are connected.
"""
def stopProtocol(self):
"""
Called when the transport is disconnected.
Will only be called once, after all ports are disconnected.
"""
def makeConnection(self, transport):
"""
Make a connection to a transport and a server.
This sets the 'transport' attribute of this DatagramProtocol, and calls the
doStart() callback.
"""
assert self.transport == None
self.transport = transport
self.doStart()
def datagramReceived(self, datagram: bytes, addr):
"""
Called when a datagram is received.
@param datagram: the bytes received from the transport.
@param addr: tuple of source of datagram.
"""
?server.py
from twisted.internet import protocol
from twisted.internet import reactor
class ServerProtocol(protocol.DatagramProtocol):
def startProtocol(self):
print("startProtocol")
def stopProtocol(self):
print("stopProtocol")
def connectionRefused(self):
print("connectionRefused")
def datagramReceived(self, data, addr):
print("received %r from %s" % (data, addr))
self.transport.write(data, addr)
reactor.listenUDP(10004,ServerProtocol())
reactor.run()
client.py
from twisted.internet import protocol
from twisted.internet import reactor
class ClientProtocol(protocol.DatagramProtocol):
def __init__(self,host,port):
self.host = host
self.port = port
def startProtocol(self):
print("startProtocol")
self.transport.connect(self.host,self.port)
self.transport.write(b"hello")
def stopProtocol(self):
print("stopProtocol")
def connectionRefused(self):
print("connectionRefused")
def datagramReceived(self, data, addr):
print("received %r from %s" % (data, addr))
self.transport.write(data, addr)
reactor.listenUDP(0,ClientProtocol("192.168.0.102",10004))
reactor.run()
运行结果:
另外一种写法用的少
import socket
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
class Echo(DatagramProtocol):
def datagramReceived(self, data, addr):
print("received %r from %s" % (data, addr))
self.transport.write(data, addr)
portSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
portSocket.setblocking(False)
portSocket.bind(('127.0.0.1', 9999))
port = reactor.adoptDatagramPort(portSocket.fileno(), socket.AF_INET, Echo())
portSocket.close()
reactor.run()
connect()只接受IP,不接受未解析的主机名。?
from twisted.internet import reactor
def gotIP(ip):
print("IP of 'localhost' is", ip)
reactor.stop()
reactor.resolve('localhost').addCallback(gotIP)
reactor.run()
|