Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / octopi / library / sockets / spocket.h
1 #ifndef SPOCKET_CLASS
2 #define SPOCKET_CLASS
3
4 /*****************************************************************************\
5 *                                                                             *
6 *  Name   : spocket                                                           *
7 *  Author : Chris Koeritz                                                     *
8 *                                                                             *
9 *  Purpose:                                                                   *
10 *                                                                             *
11 *                                                                             *
12 *******************************************************************************
13 * Copyright (c) 1989-$now By Author.  This program is free software; you can  *
14 * redistribute it and/or modify it under the terms of the GNU General Public  *
15 * License as published by the Free Software Foundation; either version 2 of   *
16 * the License or (at your option) any later version.  This is online at:      *
17 *     http://www.fsf.org/copyleft/gpl.html                                    *
18 * Please send any updates to: fred@gruntose.com                               *
19 \*****************************************************************************/
20
21 #include "tcpip_stack.h"
22
23 #include <basis/contracts.h>
24 #include <basis/mutex.h>
25 #include <timely/time_stamp.h>
26
27 namespace sockets {
28
29 // forward declarations.
30 class internet_address;
31 class raw_socket;
32
33 //! Abstraction for a higher-level BSD socket that is platform independent.
34 /*!
35   The class works on Unix and Win32 style operating systems.  This class is named in honor of
36   the venerable Vulcan Spock, which also avoid naming conflicts with the OS's socket() function.
37 */
38
39 class spocket : public virtual basis::root_object
40 {
41 public:
42   enum sock_types {
43     CONNECTED,         // connected socket over TCP.
44     BROADCAST,         // broadcasting socket over UDP.
45     UNICAST,           // single-address targeted socket over UDP.
46     BOGUS_SOCK         // special type that goes nowhere and provides no data.
47   };
48
49   spocket(const internet_address &where, sock_types type = CONNECTED);
50     // constructs the spocket object.  "where" provides the network location
51     // for either this side (for a server) or the other side (for a client).
52     // the decision about this socket's orientation (client or server) will not
53     // be made until either connect() or accept() are invoked.  note however
54     // that BROADCAST sockets are only appropriate for use as a client; they
55     // can receive and send broadcasts that way without needing a server role.
56
57   ~spocket();
58     // drops any connection that was made and destroys the spocket object.
59
60   DEFINE_CLASS_NAME("spocket");
61
62   bool healthy();
63     // returns true if the spocket seems to be okay.
64
65   const internet_address &where() const;
66     // returns the location where this socket exists.
67
68   const internet_address &remote() const;
69     // returns the location that we have accepted from.
70
71   enum outcomes {
72     OKAY = basis::common::OKAY,
73     TIMED_OUT = basis::common::TIMED_OUT,
74     ACCESS_DENIED = basis::common::ACCESS_DENIED,
75     BAD_INPUT = basis::common::BAD_INPUT,
76     NONE_READY = basis::common::NONE_READY,
77     PARTIAL = basis::common::PARTIAL,
78
79     NO_CONNECTION = sockets::communication_commons::NO_CONNECTION,
80     NO_ANSWER = sockets::communication_commons::NO_ANSWER,
81
82     DEFINE_OUTCOME(NOT_SERVER, -39, "Accept was tried on a spocket that is "
83         "not a root server")
84   };
85   
86   static const char *outcome_name(const basis::outcome &to_name);
87     // returns the text for "to_name" if it's a member of spocket outcomes.
88
89   // informative functions...
90
91   basis::astring text_form();
92     // returns a readable version of the contents of the spocket.
93
94   bool was_connected() const { return _was_connected; }
95     // a simple check of whether a connection has been made on this object.
96     // if this is not true, then the object is not usable yet.  this state
97     // will also get set to false when the spocket gets disconnected.
98
99   bool connected();
100     // returns true if the spocket is "currently" connected.  this causes an
101     // interrogation of the operating system and may take a short while.
102
103   // these report what type of spocket this is.
104   bool is_client() const { return _client; }
105   bool is_server() const { return !_client; }
106   bool is_root_server() const { return is_server() && !!_server_socket; }
107
108   basis::un_int OS_socket() { return _socket; }
109     // returns the low-level operating system form of our normal action socket.
110     // this is zero for a root server.  note: this will still record what the
111     // socket was after it is closed out, just for record keeping; do not
112     // control the returned socket outside of this class.
113   basis::un_int OS_root_socket() { return _server_socket; }
114     // returns the OS form of our server socket, but only if this is a root
115     // server type of socket.
116
117   void bind_client(const internet_address &source);
118     //!< when a client calls connect, this forces it to bind to "source".
119     /*!< this has no effect on servers.  it is also disabled again when the
120     client is disconnected, so it should always be done before every time
121     connect() is called. */
122
123   // major operations for connected mode sockets...
124
125   basis::outcome connect(int communication_wait = 20 * basis::SECOND_ms);
126     // acts as a client and connects to a destination.  the timeout is
127     // specified in milliseconds by "communication_wait".  this can be as low
128     // as zero if you don't want to wait.  TIMED_OUT is returned if the
129     // connection hasn't finished within the connection_wait.  OKAY is returned
130     // if the connection succeeded.  other outcomes denote failures.
131
132   basis::outcome accept(spocket * &sock, bool wait);
133     // makes this spocket act as a root server and accepts a connection from a
134     // client if possible.  a root server is a spocket object that manages a
135     // server spocket but which does not allow any normal sending or receiving.
136     // only root servers can have accept called on them.  the "sock" will be
137     // a normal server spocket which can be used to send and receive if it
138     // got connected.  for "sock" to be valid, it must return as NULL_POINTER
139     // and the returned outcome must be OKAY.  if no new connections are
140     // available, then NO_CONNECTION is returned.  if the "wait" flag is true,
141     // then the accept on the root server will block until a connection is
142     // accepted and the returned spocket will be made non-blocking.  if "wait"
143     // is false, then no blocking will occur at all.  note that multiple
144     // threads can invoke this without tripping over the protective locking;
145     // once the root socket is initialized, accept will not lock the spocket.
146
147   basis::outcome disconnect();
148     // closes the connection.  the state is returned to the post-construction
149     // state, i.e. it will appear that this object had never connected yet.
150
151   // these report which side of the connection this is functioning as.
152   bool client() const { return _client; }
153   bool server() const { return !_client; }
154
155   // send and receive functions...
156   //
157   //   if the outcome from one of these is NO_CONNECTION, then somehow the
158   // connection has dropped or never succeeded.
159
160   basis::outcome send(const basis::abyte *buffer, int size, int &len_sent);
161     // sends "size" bytes from the "buffer".  the number actually sent is
162     // stored in "len_sent".  this can only be used for CONNECTED type sockets.
163
164   basis::outcome send(const basis::byte_array &to_send, int &len_sent);
165     // this version takes a byte_array.
166
167   basis::outcome send_to(const internet_address &where_to, const basis::abyte *buffer,
168           int size, int &len_sent);
169     // this version is used for sending when the socket type is BROADCAST
170     // or UNICAST.
171
172   basis::outcome send_to(const internet_address &where_to, const basis::byte_array &to_send,
173           int &len_sent);
174     // clone of above, using byte_array.
175
176   basis::outcome receive(basis::abyte *buffer, int &size);
177     // attempts to retrieve data from the spocket and place it in the "buffer".
178     // the "size" specifies how much space is available.  if successful, the
179     // buffer will be filled with data and "size" will report how much there
180     // actually is.
181
182   basis::outcome receive(basis::byte_array &buffer, int &size);
183     // this version uses a byte_array for the "buffer".  the "size" expected
184     // must still be set and "size" will still report the bytes read.
185 //hmmm: could remove the size parameter for byte array type.
186
187   // these methods are used for BROADCAST and other non-connected types of
188   // spockets.  they report the data as the above receive methods do, but they
189   // also report the sender.
190   basis::outcome receive_from(basis::abyte *buffer, int &size,
191           internet_address &where_from);
192   basis::outcome receive_from(basis::byte_array &buffer, int &size,
193           internet_address &where_from);
194
195   basis::outcome await_readable(int timeout);
196     // pauses this caller until data arrives.  this is a blocking call that
197     // could potentially not return until the "timeout" elapses (measured in
198     // milliseconds).  if "timeout" is zero, then this really doesn't do
199     // anything.  if data is available, then OKAY is returned.
200
201   basis::outcome await_writable(int timeout);
202     // pauses this caller until the socket can be written to again.  could
203     // potentially not return until the "timeout" elapses (measured in
204     // milliseconds).  if "timeout" is zero, then this really doesn't do
205     // anything.  if the socket is able to send again, then OKAY is returned.
206     // otherwise NONE_READY is returned.
207
208   tcpip_stack &stack() const;
209     // provides access to the spocket's tcpip stack to derivative objects.
210
211   bool is_bogus() const { return _type == BOGUS_SOCK; }
212     //!< returns true when this object is bogus.
213     /*!< a spocket constructed as BOGUS_SOCK does not open a network
214     connection to anywhere, and it also never sends or receives any data.
215     it allows code based on spockets to be disabled when appropriate, so
216     that the spocket is still constructed and all methods can be invoked,
217     but it does nothing. */
218
219 private:
220   sock_types _type;  // records what kind of socket we'll create.
221   basis::un_int _socket;  // our socket that we communicate on.
222   basis::un_int _server_socket;  // our serving socket, if we're a root server.
223   bool _was_connected;  // did we ever successfully connect?
224   bool _client;  // true if we are acting as a client.
225   internet_address *_where;  // our addressing info.
226   internet_address *_remote;  // our addressing info.
227   raw_socket *_socks;  // provides low-level socket functionality.
228   tcpip_stack *_stack;  // provides access to socket facilities.
229   basis::mutex *_select_lock;  // protects concurrent access to socket.
230   timely::time_stamp *_last_resolve;
231     // tracks when we last tried a resolve on our address.  if they try to
232     // reconnect and we haven't done this in a while, we'll re-resolve the
233     // socket.
234   bool _client_bind;  //!< force the client to bind on an address.
235   internet_address *_cli_bind;  //!< where the client binds.
236
237   // not allowed.
238   spocket(const spocket &);
239   spocket &operator =(const spocket &);
240 };
241
242 } //namespace.
243
244 #endif
245