diff options
author | Charles-François Natali <neologix@free.fr> | 2011-11-10 18:21:37 (GMT) |
---|---|---|
committer | Charles-François Natali <neologix@free.fr> | 2011-11-10 18:21:37 (GMT) |
commit | 10b8cf4455f48eba93077535e4d03eba2ee4bbfb (patch) | |
tree | 71ffb167f19f8c96177e9840d413c9d27d9a38da | |
parent | 0c929d9d397c2a2578a2b016ae9b3d258ab513aa (diff) | |
download | cpython-10b8cf4455f48eba93077535e4d03eba2ee4bbfb.zip cpython-10b8cf4455f48eba93077535e4d03eba2ee4bbfb.tar.gz cpython-10b8cf4455f48eba93077535e4d03eba2ee4bbfb.tar.bz2 |
Issue #7777: socket: Add Reliable Datagram Sockets (PF_RDS) support.
-rw-r--r-- | Doc/library/socket.rst | 24 | ||||
-rw-r--r-- | Doc/whatsnew/3.3.rst | 3 | ||||
-rw-r--r-- | Lib/test/test_socket.py | 159 | ||||
-rw-r--r-- | Misc/NEWS | 2 | ||||
-rw-r--r-- | Modules/socketmodule.c | 77 |
5 files changed, 260 insertions, 5 deletions
diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 3d1a6a4..17a99e2 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -236,6 +236,19 @@ The module :mod:`socket` exports the following constants and functions: .. versionadded:: 3.3 +.. data:: AF_RDS + PF_RDS + SOL_RDS + RDS_* + + Many constants of these forms, documented in the Linux documentation, are + also defined in the socket module. + + Availability: Linux >= 2.6.30. + + .. versionadded:: 3.3 + + .. data:: SIO_* RCVALL_* @@ -407,14 +420,15 @@ The module :mod:`socket` exports the following constants and functions: Create a new socket using the given address family, socket type and protocol number. The address family should be :const:`AF_INET` (the default), - :const:`AF_INET6`, :const:`AF_UNIX` or :const:`AF_CAN`. The socket type - should be :const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM`, - :const:`SOCK_RAW` or perhaps one of the other ``SOCK_`` constants. The - protocol number is usually zero and may be omitted in that case or - :const:`CAN_RAW` in case the address family is :const:`AF_CAN`. + :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN` or :const:`AF_RDS`. The + socket type should be :const:`SOCK_STREAM` (the default), + :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_`` + constants. The protocol number is usually zero and may be omitted in that + case or :const:`CAN_RAW` in case the address family is :const:`AF_CAN`. .. versionchanged:: 3.3 The AF_CAN family was added. + The AF_RDS family was added. .. function:: socketpair([family[, type[, proto]]]) diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index bfc5ed6..911d8d9 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -495,6 +495,9 @@ socket (Contributed by Matthias Fuchs, updated by Tiago Gonçalves in :issue:`10141`) +* The :class:`~socket.socket` class now supports the PF_RDS protocol family + (http://en.wikipedia.org/wiki/Reliable_Datagram_Sockets and + http://oss.oracle.com/projects/rds/). ssl --- diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index d7a521c..b5e0dc2 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -47,8 +47,20 @@ def _have_socket_can(): s.close() return True +def _have_socket_rds(): + """Check whether RDS sockets are supported on this host.""" + try: + s = socket.socket(socket.PF_RDS, socket.SOCK_SEQPACKET, 0) + except (AttributeError, OSError): + return False + else: + s.close() + return True + HAVE_SOCKET_CAN = _have_socket_can() +HAVE_SOCKET_RDS = _have_socket_rds() + # Size in bytes of the int type SIZEOF_INT = array.array("i").itemsize @@ -113,6 +125,23 @@ class SocketCANTest(unittest.TestCase): self.skipTest('network interface `%s` does not exist' % self.interface) + +class SocketRDSTest(unittest.TestCase): + + """To be able to run this test, the `rds` kernel module must be loaded: + # modprobe rds + """ + bufsize = 8192 + + def setUp(self): + self.serv = socket.socket(socket.PF_RDS, socket.SOCK_SEQPACKET, 0) + self.addCleanup(self.serv.close) + try: + self.port = support.bind_port(self.serv) + except OSError: + self.skipTest('unable to bind RDS socket') + + class ThreadableTest: """Threadable Test class @@ -271,6 +300,29 @@ class ThreadedCANSocketTest(SocketCANTest, ThreadableTest): self.cli = None ThreadableTest.clientTearDown(self) +class ThreadedRDSSocketTest(SocketRDSTest, ThreadableTest): + + def __init__(self, methodName='runTest'): + SocketRDSTest.__init__(self, methodName=methodName) + ThreadableTest.__init__(self) + self.evt = threading.Event() + + def clientSetUp(self): + self.cli = socket.socket(socket.PF_RDS, socket.SOCK_SEQPACKET, 0) + try: + # RDS sockets must be bound explicitly to send or receive data + self.cli.bind((HOST, 0)) + self.cli_addr = self.cli.getsockname() + except OSError: + # skipTest should not be called here, and will be called in the + # server instead + pass + + def clientTearDown(self): + self.cli.close() + self.cli = None + ThreadableTest.clientTearDown(self) + class SocketConnectedTest(ThreadedTCPSocketTest): """Socket tests for client-server connection. @@ -1239,6 +1291,112 @@ class CANTest(ThreadedCANSocketTest): self.cli.send(self.cf2) +@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.') +class BasicRDSTest(unittest.TestCase): + + def testCrucialConstants(self): + socket.AF_RDS + socket.PF_RDS + + def testCreateSocket(self): + with socket.socket(socket.PF_RDS, socket.SOCK_SEQPACKET, 0) as s: + pass + + def testSocketBufferSize(self): + bufsize = 16384 + with socket.socket(socket.PF_RDS, socket.SOCK_SEQPACKET, 0) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, bufsize) + s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, bufsize) + + +@unittest.skipUnless(HAVE_SOCKET_RDS, 'RDS sockets required for this test.') +@unittest.skipUnless(thread, 'Threading required for this test.') +class RDSTest(ThreadedRDSSocketTest): + + def __init__(self, methodName='runTest'): + ThreadedRDSSocketTest.__init__(self, methodName=methodName) + + def testSendAndRecv(self): + data, addr = self.serv.recvfrom(self.bufsize) + self.assertEqual(self.data, data) + self.assertEqual(self.cli_addr, addr) + + def _testSendAndRecv(self): + self.data = b'spam' + self.cli.sendto(self.data, 0, (HOST, self.port)) + + def testPeek(self): + data, addr = self.serv.recvfrom(self.bufsize, socket.MSG_PEEK) + self.assertEqual(self.data, data) + data, addr = self.serv.recvfrom(self.bufsize) + self.assertEqual(self.data, data) + + def _testPeek(self): + self.data = b'spam' + self.cli.sendto(self.data, 0, (HOST, self.port)) + + @requireAttrs(socket.socket, 'recvmsg') + def testSendAndRecvMsg(self): + data, ancdata, msg_flags, addr = self.serv.recvmsg(self.bufsize) + self.assertEqual(self.data, data) + + @requireAttrs(socket.socket, 'sendmsg') + def _testSendAndRecvMsg(self): + self.data = b'hello ' * 10 + self.cli.sendmsg([self.data], (), 0, (HOST, self.port)) + + def testSendAndRecvMulti(self): + data, addr = self.serv.recvfrom(self.bufsize) + self.assertEqual(self.data1, data) + + data, addr = self.serv.recvfrom(self.bufsize) + self.assertEqual(self.data2, data) + + def _testSendAndRecvMulti(self): + self.data1 = b'bacon' + self.cli.sendto(self.data1, 0, (HOST, self.port)) + + self.data2 = b'egg' + self.cli.sendto(self.data2, 0, (HOST, self.port)) + + def testSelect(self): + r, w, x = select.select([self.serv], [], [], 3.0) + self.assertIn(self.serv, r) + data, addr = self.serv.recvfrom(self.bufsize) + self.assertEqual(self.data, data) + + def _testSelect(self): + self.data = b'select' + self.cli.sendto(self.data, 0, (HOST, self.port)) + + def testCongestion(self): + # wait until the sender is done + self.evt.wait() + + def _testCongestion(self): + # test the behavior in case of congestion + self.data = b'fill' + self.cli.setblocking(False) + try: + # try to lower the receiver's socket buffer size + self.cli.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 16384) + except OSError: + pass + with self.assertRaises(OSError) as cm: + try: + # fill the receiver's socket buffer + while True: + self.cli.sendto(self.data, 0, (HOST, self.port)) + finally: + # signal the receiver we're done + self.evt.set() + # sendto() should have failed with ENOBUFS + self.assertEqual(cm.exception.errno, errno.ENOBUFS) + # and we should have received a congestion notification through poll + r, w, x = select.select([self.serv], [], [], 3.0) + self.assertIn(self.serv, r) + + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): @@ -4362,6 +4520,7 @@ def test_main(): tests.append(TIPCTest) tests.append(TIPCThreadableTest) tests.extend([BasicCANTest, CANTest]) + tests.extend([BasicRDSTest, RDSTest]) tests.extend([ CmsgMacroTests, SendmsgUDPTest, @@ -1516,6 +1516,8 @@ Tools/Demos Extension Modules ----------------- +- Issue #7777: socket: Add Reliable Datagram Sockets (PF_RDS) support. + - Issue #13159: FileIO and BZ2Compressor/BZ2Decompressor now use a linear-time buffer growth strategy instead of a quadratic-time one. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index c930ed3..240feb7 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1328,6 +1328,11 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, } #endif +#ifdef AF_RDS + case AF_RDS: + /* RDS sockets use sockaddr_in: fall-through */ +#endif + case AF_INET: { struct sockaddr_in* addr; @@ -1686,6 +1691,11 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) } #endif +#ifdef AF_RDS + case AF_RDS: + /* RDS sockets use sockaddr_in: fall-through */ +#endif + case AF_INET: { *len_ret = sizeof (struct sockaddr_in); @@ -5614,6 +5624,14 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "PF_CAN", PF_CAN); #endif +/* Reliable Datagram Sockets */ +#ifdef AF_RDS + PyModule_AddIntConstant(m, "AF_RDS", AF_RDS); +#endif +#ifdef PF_RDS + PyModule_AddIntConstant(m, "PF_RDS", PF_RDS); +#endif + #ifdef AF_PACKET PyModule_AddIntMacro(m, AF_PACKET); #endif @@ -5909,6 +5927,27 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "CAN_RAW_LOOPBACK", CAN_RAW_LOOPBACK); PyModule_AddIntConstant(m, "CAN_RAW_RECV_OWN_MSGS", CAN_RAW_RECV_OWN_MSGS); #endif +#ifdef SOL_RDS + PyModule_AddIntConstant(m, "SOL_RDS", SOL_RDS); +#endif +#ifdef RDS_CANCEL_SENT_TO + PyModule_AddIntConstant(m, "RDS_CANCEL_SENT_TO", RDS_CANCEL_SENT_TO); +#endif +#ifdef RDS_GET_MR + PyModule_AddIntConstant(m, "RDS_GET_MR", RDS_GET_MR); +#endif +#ifdef RDS_FREE_MR + PyModule_AddIntConstant(m, "RDS_FREE_MR", RDS_FREE_MR); +#endif +#ifdef RDS_RECVERR + PyModule_AddIntConstant(m, "RDS_RECVERR", RDS_RECVERR); +#endif +#ifdef RDS_CONG_MONITOR + PyModule_AddIntConstant(m, "RDS_CONG_MONITOR", RDS_CONG_MONITOR); +#endif +#ifdef RDS_GET_MR_FOR_DEST + PyModule_AddIntConstant(m, "RDS_GET_MR_FOR_DEST", RDS_GET_MR_FOR_DEST); +#endif #ifdef IPPROTO_IP PyModule_AddIntConstant(m, "IPPROTO_IP", IPPROTO_IP); #else @@ -6261,6 +6300,44 @@ PyInit__socket(void) PyModule_AddIntConstant(m, "IPX_TYPE", IPX_TYPE); #endif +/* Reliable Datagram Sockets */ +#ifdef RDS_CMSG_RDMA_ARGS + PyModule_AddIntConstant(m, "RDS_CMSG_RDMA_ARGS", RDS_CMSG_RDMA_ARGS); +#endif +#ifdef RDS_CMSG_RDMA_DEST + PyModule_AddIntConstant(m, "RDS_CMSG_RDMA_DEST", RDS_CMSG_RDMA_DEST); +#endif +#ifdef RDS_CMSG_RDMA_MAP + PyModule_AddIntConstant(m, "RDS_CMSG_RDMA_MAP", RDS_CMSG_RDMA_MAP); +#endif +#ifdef RDS_CMSG_RDMA_STATUS + PyModule_AddIntConstant(m, "RDS_CMSG_RDMA_STATUS", RDS_CMSG_RDMA_STATUS); +#endif +#ifdef RDS_CMSG_RDMA_UPDATE + PyModule_AddIntConstant(m, "RDS_CMSG_RDMA_UPDATE", RDS_CMSG_RDMA_UPDATE); +#endif +#ifdef RDS_RDMA_READWRITE + PyModule_AddIntConstant(m, "RDS_RDMA_READWRITE", RDS_RDMA_READWRITE); +#endif +#ifdef RDS_RDMA_FENCE + PyModule_AddIntConstant(m, "RDS_RDMA_FENCE", RDS_RDMA_FENCE); +#endif +#ifdef RDS_RDMA_INVALIDATE + PyModule_AddIntConstant(m, "RDS_RDMA_INVALIDATE", RDS_RDMA_INVALIDATE); +#endif +#ifdef RDS_RDMA_USE_ONCE + PyModule_AddIntConstant(m, "RDS_RDMA_USE_ONCE", RDS_RDMA_USE_ONCE); +#endif +#ifdef RDS_RDMA_DONTWAIT + PyModule_AddIntConstant(m, "RDS_RDMA_DONTWAIT", RDS_RDMA_DONTWAIT); +#endif +#ifdef RDS_RDMA_NOTIFY_ME + PyModule_AddIntConstant(m, "RDS_RDMA_NOTIFY_ME", RDS_RDMA_NOTIFY_ME); +#endif +#ifdef RDS_RDMA_SILENT + PyModule_AddIntConstant(m, "RDS_RDMA_SILENT", RDS_RDMA_SILENT); +#endif + /* get{addr,name}info parameters */ #ifdef EAI_ADDRFAMILY PyModule_AddIntConstant(m, "EAI_ADDRFAMILY", EAI_ADDRFAMILY); |