File: socket.rst

package info (click to toggle)
python-mido 1.3.3-0.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 920 kB
  • sloc: python: 4,006; makefile: 127; sh: 4
file content (146 lines) | stat: -rw-r--r-- 4,318 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
.. SPDX-FileCopyrightText: 2013 Ole Martin Bjorndalen <ombdalen@gmail.com>
..
.. SPDX-License-Identifier: CC-BY-4.0

Socket Ports - MIDI over TCP/IP
-------------------------------


About
^^^^^

Socket :term:`ports` allows you to send :term:`MIDI` messages over a computer
network.

The protocol is a simple MIDI bytes stream over :term:`TCP`.

.. warning::

    It is **not** :term:`rtpmidi`!


Caveats
^^^^^^^

The data is sent over an *unencrypted channel*. Also, the default server
allows connections from any host and also accepts arbitrary :term:`sysex`
messages, which could allow anyone to for example overwrite patches on
your synths (or **worse**). Use **only** on *trusted networks*.

If you need more security, you can build a *custom server* with a whitelist
of clients allowed to connect.

If *timing* is critical, *latency* and *jitter* (especially on *wireless
networks*) may make socket ports *unusable*.


Sending Messages to a Server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

First, let's import some things::

    from mido.sockets import PortServer, connect

After that, a simple server is only two lines::

    for message in PortServer('localhost', 8080):
        print(message)

You can then connect to the server and send it messages::

    output = connect('localhost', 8080):
    output.send(message)

Each end of the connection behaves like a normal Mido I/O port, with
all the usual methods.

The host may be an host name or IP address (as a string). It may also be '',
in which case connections are accepted from any IP address on the computer.

.. todo::

    Test and clarify "Any IP address on the computer".
    Does this mean only local adresses can connect or that any connection
    from any network is allowed?


Turning Things on their Head
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you want the server to send messages the client, you can instead
do::

    server = PortServer('localhost', 8080):
    while True:
        server.send(message)
        ...

and then on the client side::

    for message in connect('localhost', 8080):
        print(message)

The client will now print any message that the server sends. Each
message that the server sends will be received by all connected
clients.


Under the Hood
^^^^^^^^^^^^^^

The examples above use the server and client ports as normal Mido I/O
ports. This makes it easy to write simple servers, but you don't have
any control on connections and the way messages are sent and received.

To get more control,you can ignore all the other methods of the
``PortServer`` object and use only ``accept()``. Here's a simple
server implemented this way::

    with PortServer('localhost', 8080) as server:
        while True:
            client = server.accept()
            for message in client:
                print(message)

``accept()`` waits for a client to connect, and returns a ``SocketPort``
object which is connected to the ``SocketPort`` object returned by
``connect()`` on the other end.

The server above has one weakness: it only allows one connection at a
time. You can get around this by using ``accept(block=False)``. This
will return a ``SocketPort`` if there's a connection waiting and ``None`` if
there is connection yet.

.. todo:: Clarify "Connection waiting" vs "There is a connection yet".

Using this you can write the server any way you like, for example::

    with PortServer('localhost', 8080) as server:
        clients = []
        while True:
            # Handle connections.
            client = server.accept(block=False)
            if client:
                print('Connection from {}'.format(client.name))
                clients.append(client)

            for i, client in reversed(enumerate(clients)):
                if client.closed:
                    print('{} disconnected'.format(client.name))
                    del clients[i]

            # Receive messages.
            for client in clients:
                for message in client.iter_pending()
                    print('Received {} from {}'.format(message, client))

            # Do other things
            ...


Possible Future Additions
^^^^^^^^^^^^^^^^^^^^^^^^^

Optional HTTP-style headers could be added. As long as these are 7-bit
:term:`ASCII`, they will be counted as data bytes and ignored by clients or
servers who don't expect them.