This article mainly introduces the example of UDP port multiplexing in the Python Socket programming process. The author used the Python twisted asynchronous framework. If you need it, refer
About Port multiplexing
A socket cannot bind multiple ports at the same time. If the client wants to bind a port number, it must call the bind port before sending the information function, because when sending the information function (sendto, or write ), the system will automatically assign a random port number to the current network program. This is equivalent to a random port number bound, which will only be allocated once. Later communications will communicate with this random port, if we bind the port number, the binding will fail. If we bind it before the sending information function (sendto, or write), then the program will send the information using the port number we bind and will not randomly allocate a port number. In fact, by default, if a socket of a network application is bound with a port, other sockets cannot use this port. So how can we make the two sockets successfully bind a port? In this case, port reuse is required. Port multiplexing allows an application to bind n sockets to a port without error.
Port multiplexing can communicate on open ports of the system. It only matches the characters of input information, and does not block or copy network data, therefore, the transmission performance of network data is not affected.
However, after the connection is established, the server program occupies a very small amount of system resources. The controlled end is not aware of the system performance and is usually used by Backdoor trojans.
In the implementation of winsock, You can bind multiple servers. When determining who is used by multiple bindings, the most explicit rule is who will submit the package, there is no permission, that is, users with low-level permissions can be rebound to high-level permissions, such as the port on which the service starts. This is a very important security risk.
Python solves the problem of UDP port multiplexing
I have always thought that the UDP protocol is very simple, but today's problem makes me feel that the network is really profound and profound.
Let's take a look at the problem. Due to Protocol requirements, I have to implement a UDP client and server, and read and write data from the same port.
Initially, I did not agree. I simply used two sockets, one listening and reading data from this port (the server uses twisted), and the other writing data to this port, it takes about 10 lines of code to be implemented using python.
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()
To write data to the same port, the client must have a bind. However, after running the client, it is found that the server first has a bind port. An error is reported when the client is running.
The Code is as follows:
Error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted
Generally, this error occurs because multiple sockets cannot bind the same address at the same time.
I started a crazy search and found someone talking about port multiplexing. The so-called port multiplexing means that a socket has a wait_time after a port is released, if another socket is followed by bind, an error is reported. Although my questions are not exactly the same, I was excited to use them. Add the following sentence before client bind:
socketHandler.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
However, the following error is still reported:
The Code is as follows:
Error: [Errno 10013] An attempt was made to access a socket in a way forbidden by its access permissions
(By The Way, another parameter is SO_REUSEPORT, that is, reusing the port. Another parameter is SO_EXCLUSIVEADDRUSE, that is, the port is not allowed to be reused. There are many other socket parameters. For details, see winsockhttp: // msdn.microsoft.com/en-us/library/aa924071.aspx1_unixsocket)
The token permission is enabled by right-clicking the program, and the compatibility column is checked to run as a system administrator. Some people say it is in conflict with other program addresses or ports. But I have tested and found that none of them works.
In addition, during running, it is found that the twisted server must be in the main thread; otherwise, the signal must be accepted in the main thread, however, the twisted reactor is blocked once it is run.
In the twisted document, there is also a UDP called connected UDP, abnormal. The so-called connected UDP means that only data can be sent and received from one address. It looks like it can, however, non-conforming data can be received from multiple addresses.
Finally, I mentioned in an article that both ports need to be set up for reuse. So I tried to write a new server and work with the previous client to run well without any errors.
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)
Okay, it seems that the problem is calling twisted. I don't know if he has such a setting. I went in and flipped through this part of the code and couldn't find the parameter set like this.
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
It cannot be said that twisted does not provide such a function at all? In multicast, we finally turn to this section, that is, in the case of multicast, address multiplexing is supported and can be tested.
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
Change the server to the following code and run it!
reactor.listenMulticast(port, DhtResponseHandler(queue), listenMultiple=True) reactor.run()
With a lot of feelings, the underlying knowledge is very important, and it is really dangerous to build a high platform in the sand.