4 /*****************************************************************************\
7 * Author : Chris Koeritz *
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 \*****************************************************************************/
21 #include "tcpip_stack.h"
23 #include <basis/contracts.h>
24 #include <basis/mutex.h>
25 #include <timely/time_stamp.h>
29 // forward declarations.
30 class internet_address;
33 //! Abstraction for a higher-level BSD socket that is platform independent.
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.
39 class spocket : public virtual basis::root_object
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.
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.
58 // drops any connection that was made and destroys the spocket object.
60 DEFINE_CLASS_NAME("spocket");
63 // returns true if the spocket seems to be okay.
65 const internet_address &where() const;
66 // returns the location where this socket exists.
68 const internet_address &remote() const;
69 // returns the location that we have accepted from.
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,
79 NO_CONNECTION = sockets::communication_commons::NO_CONNECTION,
80 NO_ANSWER = sockets::communication_commons::NO_ANSWER,
82 DEFINE_OUTCOME(NOT_SERVER, -39, "Accept was tried on a spocket that is "
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.
89 // informative functions...
91 basis::astring text_form();
92 // returns a readable version of the contents of the spocket.
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.
100 // returns true if the spocket is "currently" connected. this causes an
101 // interrogation of the operating system and may take a short while.
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; }
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.
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. */
123 // major operations for connected mode sockets...
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.
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.
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.
151 // these report which side of the connection this is functioning as.
152 bool client() const { return _client; }
153 bool server() const { return !_client; }
155 // send and receive functions...
157 // if the outcome from one of these is NO_CONNECTION, then somehow the
158 // connection has dropped or never succeeded.
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.
164 basis::outcome send(const basis::byte_array &to_send, int &len_sent);
165 // this version takes a byte_array.
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
172 basis::outcome send_to(const internet_address &where_to, const basis::byte_array &to_send,
174 // clone of above, using byte_array.
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
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.
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);
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.
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.
208 tcpip_stack &stack() const;
209 // provides access to the spocket's tcpip stack to derivative objects.
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. */
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
234 bool _client_bind; //!< force the client to bind on an address.
235 internet_address *_cli_bind; //!< where the client binds.
238 spocket(const spocket &);
239 spocket &operator =(const spocket &);