Update RPC with simpler methods for connecting
This commit is contained in:
@@ -1814,13 +1814,16 @@ cdef class TwoPartyServer:
|
|||||||
try:
|
try:
|
||||||
self.on_disconnect().wait()
|
self.on_disconnect().wait()
|
||||||
(clientsocket, address) = self._server_socket.accept()
|
(clientsocket, address) = self._server_socket.accept()
|
||||||
|
self._orig_stream = clientsocket
|
||||||
self._stream = _FdAsyncIoStream(clientsocket.fileno())
|
self._stream = _FdAsyncIoStream(clientsocket.fileno())
|
||||||
self._network = _TwoPartyVatNetwork()._init(deref(self._stream.thisptr), capnp.SERVER)
|
self._network = _TwoPartyVatNetwork()._init(deref(self._stream.thisptr), capnp.SERVER)
|
||||||
self.thisptr = new RpcSystem(makeRpcServer(deref(self._network.thisptr), deref(self._restorer.thisptr)))
|
self.thisptr = new RpcSystem(makeRpcServer(deref(self._network.thisptr), deref(self._restorer.thisptr)))
|
||||||
|
Py_INCREF(self._orig_stream)
|
||||||
|
Py_INCREF(self._stream)
|
||||||
|
Py_INCREF(self._network) # TODO:MEMORY: attach this to onDrained, also figure out what's leaking
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# TODO: add restore functionality here?
|
# TODO: add restore functionality here?
|
||||||
|
|
||||||
cdef class _FdAsyncIoStream:
|
cdef class _FdAsyncIoStream:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.. capnp documentation master file
|
.. capnp documentation master file
|
||||||
|
|
||||||
Welcome to capnp's documentation!
|
Welcome to pycapnp's documentation!
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
This is a python wrapping of the C++ implementation of the `Cap'n Proto <http://kentonv.github.io/capnproto/>`_ library. Here is a short description, quoted from its docs:
|
This is a python wrapping of the C++ implementation of the `Cap'n Proto <http://kentonv.github.io/capnproto/>`_ library. Here is a short description, quoted from its docs:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ On some systems you will have to install Python's headers before doing any of th
|
|||||||
|
|
||||||
sudo apt-get install python-dev
|
sudo apt-get install python-dev
|
||||||
|
|
||||||
You can control what compiler is used with the environment variable CC, ie. `CC=gcc-4.8 pip install pycapnp`. You only need to run the setuptools line if you have a setuptools older than v0.8.0, and the cython line if you have a version older than v0.19.1.
|
You can control what compiler is used with the environment variable CC, ie. `CC=gcc-4.8 pip install pycapnp`, and flags with CCFLAGS. You only need to run the setuptools line if you have a setuptools older than v0.8.0, and the cython line if you have a version older than v0.19.1.
|
||||||
|
|
||||||
From Source
|
From Source
|
||||||
---------------------
|
---------------------
|
||||||
@@ -43,8 +43,6 @@ or::
|
|||||||
cd pycapnp
|
cd pycapnp
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
||||||
If you don't use pip, you will need to manually install Cython, and a setuptools with a version >= .8.
|
|
||||||
|
|
||||||
Development
|
Development
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|||||||
@@ -303,29 +303,21 @@ The examples below will be using `calculator.capnp <https://github.com/jparyani/
|
|||||||
Client
|
Client
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Making a socket
|
Starting a CLient
|
||||||
################
|
################
|
||||||
|
|
||||||
Before you do anything, you'll need to create a connection to the server. You can use anything that is socket-like and has a `fileno` member function, and below we'll just use the :py:mod:`socket` module::
|
Starting a client is very easy::
|
||||||
|
|
||||||
import socket
|
|
||||||
|
|
||||||
host = 'localhost'
|
|
||||||
port = 60000
|
|
||||||
sock = socket.create_connection((host, port))
|
|
||||||
|
|
||||||
# Optionally set TCP_NODELAY to disable Nagle's algorithm and speed up RPC
|
|
||||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
||||||
|
|
||||||
Restore Methods
|
|
||||||
################
|
|
||||||
|
|
||||||
Now that you have a socket, it's very easy to connect::
|
|
||||||
|
|
||||||
import capnp
|
import capnp
|
||||||
import calculator_capnp
|
import calculator_capnp
|
||||||
|
|
||||||
client = capnp.TwoPartyClient(sock)
|
client = capnp.TwoPartyClient('localhost:60000')
|
||||||
|
|
||||||
|
Restoring
|
||||||
|
###################
|
||||||
|
|
||||||
|
Before you do anything else, you will need to restore a capability from the server. Refer to the `Cap'n Proto docs <http://kentonv.github.io/capnproto/rpc.html>`_ if you don't know what this means::
|
||||||
|
|
||||||
calculator = client.ez_restore('calculator').cast_as(calculator_capnp.Calculator)
|
calculator = client.ez_restore('calculator').cast_as(calculator_capnp.Calculator)
|
||||||
|
|
||||||
There's two things worth noting here. First, we used the simpler `ez_restore` function. For servers that use a struct type as their Restorer, you will have to do the following instead::
|
There's two things worth noting here. First, we used the simpler `ez_restore` function. For servers that use a struct type as their Restorer, you will have to do the following instead::
|
||||||
@@ -383,35 +375,18 @@ You can also chain promises with `then` and the same pipelining will occur::
|
|||||||
Server
|
Server
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Making a socket
|
|
||||||
#################
|
|
||||||
|
|
||||||
Again, any socket-like object will work, but here's how to do it with the :py:mod:`socket` module::
|
|
||||||
|
|
||||||
host = '' # this will bind to all interfaces
|
|
||||||
port = 60000
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
|
|
||||||
# Optionally set TCP_NODELAY on socket to disable Nagle's algorithm
|
|
||||||
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
||||||
|
|
||||||
s.bind((host, port))
|
|
||||||
s.listen(1) # service only 1 client at a time
|
|
||||||
|
|
||||||
Then, you will have to accept connections one at a time::
|
|
||||||
|
|
||||||
(sock, address) = s.accept()
|
|
||||||
|
|
||||||
Starting a Server
|
Starting a Server
|
||||||
##################
|
##################
|
||||||
|
|
||||||
Once you have a socket, it's quite simple to start a server::
|
Once you have a socket, it's quite simple to start a server::
|
||||||
|
|
||||||
server = capnp.TwoPartyServer(sock, restore)
|
server = capnp.TwoPartyServer('*:60000', restore)
|
||||||
|
|
||||||
server.run_forever()
|
server.run_forever()
|
||||||
|
|
||||||
See the `Restore`_ section
|
See the `Restore`_ section for an explanation of what the `restore` object needs to looks like.
|
||||||
|
|
||||||
|
.. note:: You can also pass a socket with a `fileno()` method to TwoPartyServer. In that case, `run_forever` will not work, and you will have to use `on_disconnect.wait()`.
|
||||||
|
|
||||||
Implementing a Server
|
Implementing a Server
|
||||||
#######################
|
#######################
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ at the given address and does some RPCs')
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def main(sock):
|
def main(host):
|
||||||
client = capnp.TwoPartyClient(sock)
|
client = capnp.TwoPartyClient(host)
|
||||||
|
|
||||||
# Pass "calculator" to ez_restore (there's also a `restore` function that
|
# Pass "calculator" to ez_restore (there's also a `restore` function that
|
||||||
# takes a struct or AnyPointer as an argument), and then cast the returned
|
# takes a struct or AnyPointer as an argument), and then cast the returned
|
||||||
@@ -282,11 +282,4 @@ def main(sock):
|
|||||||
print("PASS")
|
print("PASS")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
host, port = parse_args().host.split(':')
|
main(parse_args().host)
|
||||||
|
|
||||||
sock = socket.create_connection((host, port))
|
|
||||||
|
|
||||||
# Set TCP_NODELAY on socket to disable Nagle's algorithm. This is not
|
|
||||||
# neccessary, but it speeds things up.
|
|
||||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
||||||
main(sock)
|
|
||||||
|
|||||||
@@ -138,35 +138,8 @@ def restore(ref):
|
|||||||
def main():
|
def main():
|
||||||
address = parse_args().address
|
address = parse_args().address
|
||||||
|
|
||||||
if ':' in address:
|
server = capnp.TwoPartyServer(address, restore)
|
||||||
address, port = address.split(':')
|
|
||||||
port = int(port)
|
|
||||||
else:
|
|
||||||
port = random.randint(60000, 61000)
|
|
||||||
|
|
||||||
if address == '*':
|
|
||||||
address = ''
|
|
||||||
|
|
||||||
print("Listening on port: {}".format(port))
|
|
||||||
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
|
|
||||||
# Set TCP_NODELAY on socket to disable Nagle's algorithm. This is not
|
|
||||||
# neccessary, but it speeds things up.
|
|
||||||
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
||||||
|
|
||||||
s.bind((address, port))
|
|
||||||
s.listen(1) # service only 1 client at a time
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
(clientsocket, address) = s.accept()
|
|
||||||
server = capnp.TwoPartyServer(clientsocket, restore)
|
|
||||||
|
|
||||||
server.run_forever()
|
server.run_forever()
|
||||||
print("client disconnected")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
break
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user