zhangmeng
2024-04-22 16935f4aebffdd1b6580b844391a0aa0f4f3012b
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//
// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt).  A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
 
#ifndef PROTOCOL_SP_TRANSPORT_H
#define PROTOCOL_SP_TRANSPORT_H
 
#include "core/options.h"
 
// Endpoint operations are called by the socket in a
// protocol-independent fashion.  The socket makes individual calls,
// which are expected to block if appropriate (except for destroy), or
// run asynchronously if an aio is provided. Endpoints are unable to
// call back into the socket, to prevent recusive entry and deadlock.
//
// For a given endpoint, the framework holds a lock so that each entry
// point is run exclusively of the others. (Transports must still guard
// against any asynchronous operations they manage themselves, though.)
 
struct nni_sp_dialer_ops {
    // d_init creates a vanilla dialer. The value created is
    // used for the first argument for all other dialer functions.
    int (*d_init)(void **, nni_url *, nni_dialer *);
 
    // d_fini frees the resources associated with the dialer.
    // The dialer will already have been closed.
    void (*d_fini)(void *);
 
    // d_connect establishes a connection.  It can return errors
    // NNG_EACCESS, NNG_ECONNREFUSED, NNG_EBADADDR,
    // NNG_ECONNFAILED, NNG_ETIMEDOUT, and NNG_EPROTO.
    void (*d_connect)(void *, nni_aio *);
 
    // d_close stops the dialer from operating altogether.  It
    // does not affect pipes that have already been created.  It is
    // nonblocking.
    void (*d_close)(void *);
 
    // d_getopt is used to obtain an option.
    int (*d_getopt)(void *, const char *, void *, size_t *, nni_type);
 
    // d_setopt is used to set or change an option.
    int (*d_setopt)(void *, const char *, const void *, size_t, nni_type);
 
    // d_options is an array of dialer options.  The final
    // element must have a NULL name. If this member is NULL, then
    // no dialer specific options are available.
    nni_option *d_options;
};
 
struct nni_sp_listener_ops {
    // l_init creates a vanilla listener. The value created is
    // used for the first argument for all other listener functions.
    int (*l_init)(void **, nni_url *, nni_listener *);
 
    // l_fini frees the resources associated with the listener.
    // The listener will already have been closed.
    void (*l_fini)(void *);
 
    // l_bind just does the bind() and listen() work,
    // reserving the address but not creating any connections.
    // It should return NNG_EADDRINUSE if the address is already
    // taken.  It can also return NNG_EBADADDR for an unsuitable
    // address, or NNG_EACCESS for permission problems.
    int (*l_bind)(void *);
 
    // l_accept accepts an inbound connection.
    void (*l_accept)(void *, nni_aio *);
 
    // l_close stops the listener from operating altogether.  It
    // does not affect pipes that have already been created.  It is
    // nonblocking.
    void (*l_close)(void *);
 
    // l_getopt is used to obtain an option.
    int (*l_getopt)(void *, const char *, void *, size_t *, nni_type);
 
    // l_setopt is used to set or change an option.
    int (*l_setopt)(void *, const char *, const void *, size_t, nni_type);
 
    // l_options is an array of listener options.  The final
    // element must have a NULL name. If this member is NULL, then
    // no dialer specific options are available.
    nni_option *l_options;
};
 
// Pipe operations are entry points called by the socket. These may be
// called with socket locks held, so it is forbidden for the transport
// to call back into the socket at this point.  (Which is one reason
// pointers back to socket or even enclosing pipe state, are not
// provided.)
struct nni_sp_pipe_ops {
    // p_init initializes the pipe data structures.  The main
    // purpose of this is so that the pipe will see the upper
    // layer nni_pipe and get a chance to register stats and such.
    int (*p_init)(void *, nni_pipe *);
 
    // p_fini destroys the pipe.  This should clean up all local
    // resources, including closing files and freeing memory, used
    // by the pipe.  After this call returns, the system will not
    // make further calls on the same pipe.
    void (*p_fini)(void *);
 
    // p_stop stops the pipe, waiting for any callbacks that are
    // outstanding to complete.  This is done before tearing down
    // resources with p_fini.
    void (*p_stop)(void *);
 
    // p_aio_send queues the message for transmit.  If this fails,
    // then the caller may try again with the same message (or free
    // it).  If the call succeeds, then the transport has taken
    // ownership of the message, and the caller may not use it
    // again.  The transport will have the responsibility to free
    // the message (nng_msg_free()) when it is finished with it.
    void (*p_send)(void *, nni_aio *);
 
    // p_recv schedules a message receive. This will be performed
    // even for cases where no data is expected, to allow detection
    // of a remote disconnect.
    void (*p_recv)(void *, nni_aio *);
 
    // p_close closes the pipe.  Further recv or send operations
    // should return back NNG_ECLOSED.
    void (*p_close)(void *);
 
    // p_peer returns the peer protocol. This may arrive in
    // whatever transport specific manner is appropriate.
    uint16_t (*p_peer)(void *);
 
    // p_getopt is used to obtain an option.  Pipes don't implement
    // option setting.
    int (*p_getopt)(void *, const char *, void *, size_t *, nni_type);
};
 
// Transport implementation details.  Transports must implement the
// interfaces in this file.
struct nni_sp_tran {
    // tran_link is for framework use only - it must initialized
    // to zero before registration.
    nni_list_node tran_link;
 
    // tran_scheme is the transport scheme, such as "tcp" or "inproc".
    const char *tran_scheme;
 
    // tran_dialer links our dialer-specific operations.
    const nni_sp_dialer_ops *tran_dialer;
 
    // tran_listener links our listener-specific operations.
    const nni_sp_listener_ops *tran_listener;
 
    // tran_pipe links our pipe-specific operations.
    const nni_sp_pipe_ops *tran_pipe;
 
    // tran_init is called once during library initialization.
    void (*tran_init)(void);
 
    // tran_fini is called during library shutdown.
    // It should release any global resources.
    void (*tran_fini)(void);
};
 
// These APIs are used by the framework internally, and not for use by
// transport implementations.
extern nni_sp_tran *nni_sp_tran_find(nni_url *);
extern int          nni_sp_tran_sys_init(void);
extern void         nni_sp_tran_sys_fini(void);
extern void         nni_sp_tran_register(nni_sp_tran *);
 
#endif // PROTOCOL_SP_TRANSPORT_H