Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
10f383a9
Commit
10f383a9
authored
Apr 07, 2012
by
Kristján Valur Jónsson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #14310: inter-process socket duplication for windows
parent
0f9eec19
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
208 additions
and
8 deletions
+208
-8
Doc/library/socket.rst
Doc/library/socket.rst
+25
-0
Lib/socket.py
Lib/socket.py
+9
-1
Lib/test/test_socket.py
Lib/test/test_socket.py
+105
-0
Misc/NEWS
Misc/NEWS
+3
-0
Modules/socketmodule.c
Modules/socketmodule.c
+66
-7
No files found.
Doc/library/socket.rst
View file @
10f383a9
...
...
@@ -680,6 +680,16 @@ The module :mod:`socket` exports the following constants and functions:
.. versionadded:: 3.3
.. function:: fromshare(data)
Instantiate a socket from data obtained from :meth:`~socket.share`.
The socket is assumed to be in blocking mode.
Availability: Windows.
.. versionadded:: 3.3
.. data:: SocketType
This is a Python type object that represents the socket object type. It is the
...
...
@@ -1082,6 +1092,21 @@ correspond to Unix system calls applicable to sockets.
are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are
disallowed.
.. method:: socket.share(process_id)
:platform: Windows
Duplacet a socket and prepare it for sharing with a target process. The
target process must be provided with *process_id*. The resulting bytes object
can then be passed to the target process using some form of interprocess
communication and the socket can be recreated there using :func:`fromshare`.
Once this method has been called, it is safe to close the socket since
the operating system has already duplicated it for the target process.
.. versionadded:: 3.3
Note that there are no methods :meth:`read` or :meth:`write`; use
:meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead.
...
...
Lib/socket.py
View file @
10f383a9
...
...
@@ -12,6 +12,7 @@ Functions:
socket() -- create a new socket object
socketpair() -- create a pair of new socket objects [*]
fromfd() -- create a socket object from an open file descriptor [*]
fromshare() -- create a socket object from data received from socket.share() [*]
gethostname() -- return the current hostname
gethostbyname() -- map a hostname to its IP number
gethostbyaddr() -- map an IP number or hostname to DNS info
...
...
@@ -209,7 +210,6 @@ class socket(_socket.socket):
self
.
_closed
=
True
return
super
().
detach
()
def
fromfd
(
fd
,
family
,
type
,
proto
=
0
):
""" fromfd(fd, family, type[, proto]) -> socket object
...
...
@@ -219,6 +219,14 @@ def fromfd(fd, family, type, proto=0):
nfd
=
dup
(
fd
)
return
socket
(
family
,
type
,
proto
,
nfd
)
if
hasattr
(
_socket
.
socket
,
"share"
):
def
fromshare
(
info
):
""" fromshare(info) -> socket object
Create a socket object from a the bytes object returned by
socket.share(pid).
"""
return
socket
(
0
,
0
,
0
,
info
)
if
hasattr
(
_socket
,
"socketpair"
):
...
...
Lib/test/test_socket.py
View file @
10f383a9
...
...
@@ -26,6 +26,10 @@ try:
import
fcntl
except
ImportError
:
fcntl
=
False
try
:
import
multiprocessing
except
ImportError
:
multiprocessing
=
False
HOST
=
support
.
HOST
MSG
=
'Michael Gilfix was here
\
u1234
\
r
\
n
'
.
encode
(
'utf-8'
)
## test unicode string and carriage return
...
...
@@ -4643,6 +4647,106 @@ class NonblockConstantTest(unittest.TestCase):
socket
.
setdefaulttimeout
(
t
)
@
unittest
.
skipUnless
(
os
.
name
==
"nt"
,
"Windows specific"
)
@
unittest
.
skipUnless
(
multiprocessing
,
"need multiprocessing"
)
class
TestSocketSharing
(
SocketTCPTest
):
# This must be classmethod and not staticmethod or multiprocessing
# won't be able to bootstrap it.
@
classmethod
def
remoteProcessServer
(
cls
,
q
):
# Recreate socket from shared data
sdata
=
q
.
get
()
message
=
q
.
get
()
s
=
socket
.
fromshare
(
sdata
)
s2
,
c
=
s
.
accept
()
# Send the message
s2
.
sendall
(
message
)
s2
.
close
()
s
.
close
()
def
testShare
(
self
):
# Transfer the listening server socket to another process
# and service it from there.
# Create process:
q
=
multiprocessing
.
Queue
()
p
=
multiprocessing
.
Process
(
target
=
self
.
remoteProcessServer
,
args
=
(
q
,))
p
.
start
()
# Get the shared socket data
data
=
self
.
serv
.
share
(
p
.
pid
)
# Pass the shared socket to the other process
addr
=
self
.
serv
.
getsockname
()
self
.
serv
.
close
()
q
.
put
(
data
)
# The data that the server will send us
message
=
b"slapmahfro"
q
.
put
(
message
)
# Connect
s
=
socket
.
create_connection
(
addr
)
# listen for the data
m
=
[]
while
True
:
data
=
s
.
recv
(
100
)
if
not
data
:
break
m
.
append
(
data
)
s
.
close
()
received
=
b""
.
join
(
m
)
self
.
assertEqual
(
received
,
message
)
p
.
join
()
def
testShareLength
(
self
):
data
=
self
.
serv
.
share
(
os
.
getpid
())
self
.
assertRaises
(
ValueError
,
socket
.
fromshare
,
data
[:
-
1
])
self
.
assertRaises
(
ValueError
,
socket
.
fromshare
,
data
+
b"foo"
)
def
compareSockets
(
self
,
org
,
other
):
# socket sharing is expected to work only for blocking socket
# since the internal python timout value isn't transfered.
self
.
assertEqual
(
org
.
gettimeout
(),
None
)
self
.
assertEqual
(
org
.
gettimeout
(),
other
.
gettimeout
())
self
.
assertEqual
(
org
.
family
,
other
.
family
)
self
.
assertEqual
(
org
.
type
,
other
.
type
)
# If the user specified "0" for proto, then
# internally windows will have picked the correct value.
# Python introspection on the socket however will still return
# 0. For the shared socket, the python value is recreated
# from the actual value, so it may not compare correctly.
if
org
.
proto
!=
0
:
self
.
assertEqual
(
org
.
proto
,
other
.
proto
)
def
testShareLocal
(
self
):
data
=
self
.
serv
.
share
(
os
.
getpid
())
s
=
socket
.
fromshare
(
data
)
try
:
self
.
compareSockets
(
self
.
serv
,
s
)
finally
:
s
.
close
()
def
testTypes
(
self
):
families
=
[
socket
.
AF_INET
,
socket
.
AF_INET6
]
types
=
[
socket
.
SOCK_STREAM
,
socket
.
SOCK_DGRAM
]
for
f
in
families
:
for
t
in
types
:
source
=
socket
.
socket
(
f
,
t
)
try
:
data
=
source
.
share
(
os
.
getpid
())
shared
=
socket
.
fromshare
(
data
)
try
:
self
.
compareSockets
(
source
,
shared
)
finally
:
shared
.
close
()
finally
:
source
.
close
()
def
test_main
():
tests
=
[
GeneralModuleTests
,
BasicTCPTest
,
TCPCloserTest
,
TCPTimeoutTest
,
TestExceptions
,
BufferIOTest
,
BasicTCPTest2
,
BasicUDPTest
,
UDPTimeoutTest
]
...
...
@@ -4699,6 +4803,7 @@ def test_main():
# These are slow when setitimer() is not available
InterruptedRecvTimeoutTest
,
InterruptedSendTimeoutTest
,
TestSocketSharing
,
])
thread_info
=
support
.
threading_setup
()
...
...
Misc/NEWS
View file @
10f383a9
...
...
@@ -232,6 +232,9 @@ Library
- Issue #14210: pdb now has tab-completion not only for command names, but
also for their arguments, wherever possible.
- Issue #14310: Sockets can now be with other processes on Windows using
the api socket.socket.share() and socket.fromshare().
Build
-----
...
...
Modules/socketmodule.c
View file @
10f383a9
...
...
@@ -3771,6 +3771,34 @@ PyDoc_STRVAR(sock_ioctl_doc,
Control the socket with WSAIoctl syscall. Currently supported 'cmd' values are
\n
\
SIO_RCVALL: 'option' must be one of the socket.RCVALL_* constants.
\n
\
SIO_KEEPALIVE_VALS: 'option' is a tuple of (onoff, timeout, interval)."
);
#endif
#if defined(MS_WINDOWS)
static
PyObject
*
sock_share
(
PySocketSockObject
*
s
,
PyObject
*
arg
)
{
WSAPROTOCOL_INFO
info
;
DWORD
processId
;
int
result
;
if
(
!
PyArg_ParseTuple
(
arg
,
"I"
,
&
processId
))
return
NULL
;
Py_BEGIN_ALLOW_THREADS
result
=
WSADuplicateSocket
(
s
->
sock_fd
,
processId
,
&
info
);
Py_END_ALLOW_THREADS
if
(
result
==
SOCKET_ERROR
)
return
set_error
();
return
PyBytes_FromStringAndSize
((
const
char
*
)
&
info
,
sizeof
(
info
));
}
PyDoc_STRVAR
(
sock_share_doc
,
"share(process_id) -> bytes
\n
\
\n
\
Share the socket with another process. The target process id
\n
\
must be provided and the resulting bytes object passed to the target
\n
\
process. There the shared socket can be instantiated by calling
\n
\
socket.fromshare()."
);
#endif
...
...
@@ -3802,6 +3830,10 @@ static PyMethodDef sock_methods[] = {
#if defined(MS_WINDOWS) && defined(SIO_RCVALL)
{
"ioctl"
,
(
PyCFunction
)
sock_ioctl
,
METH_VARARGS
,
sock_ioctl_doc
},
#endif
#if defined(MS_WINDOWS)
{
"share"
,
(
PyCFunction
)
sock_share
,
METH_VARARGS
,
sock_share_doc
},
#endif
{
"listen"
,
(
PyCFunction
)
sock_listen
,
METH_O
,
listen_doc
},
...
...
@@ -3930,13 +3962,40 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
return
-
1
;
if
(
fdobj
!=
NULL
&&
fdobj
!=
Py_None
)
{
fd
=
PyLong_AsSocket_t
(
fdobj
);
if
(
fd
==
(
SOCKET_T
)(
-
1
)
&&
PyErr_Occurred
())
return
-
1
;
if
(
fd
==
INVALID_SOCKET
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't use invalid socket value"
);
return
-
1
;
#ifdef MS_WINDOWS
/* recreate a socket that was duplicated */
if
(
PyBytes_Check
(
fdobj
))
{
WSAPROTOCOL_INFO
info
;
if
(
PyBytes_GET_SIZE
(
fdobj
)
!=
sizeof
(
info
))
{
PyErr_Format
(
PyExc_ValueError
,
"socket descriptor string has wrong size, "
"should be %zu bytes."
,
sizeof
(
info
));
return
-
1
;
}
memcpy
(
&
info
,
PyBytes_AS_STRING
(
fdobj
),
sizeof
(
info
));
Py_BEGIN_ALLOW_THREADS
fd
=
WSASocket
(
FROM_PROTOCOL_INFO
,
FROM_PROTOCOL_INFO
,
FROM_PROTOCOL_INFO
,
&
info
,
0
,
WSA_FLAG_OVERLAPPED
);
Py_END_ALLOW_THREADS
if
(
fd
==
INVALID_SOCKET
)
{
set_error
();
return
-
1
;
}
family
=
info
.
iAddressFamily
;
type
=
info
.
iSocketType
;
proto
=
info
.
iProtocol
;
}
else
#endif
{
fd
=
PyLong_AsSocket_t
(
fdobj
);
if
(
fd
==
(
SOCKET_T
)(
-
1
)
&&
PyErr_Occurred
())
return
-
1
;
if
(
fd
==
INVALID_SOCKET
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't use invalid socket value"
);
return
-
1
;
}
}
}
else
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment