<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<title>server.cpp Source File</title>
<link href="doxygen.css" rel="stylesheet" type="text/css">
</head><body>
<!-- Generated by Doxygen 1.2.17 -->
<center>
<a class="qindex" href="main.html">Main Page</a> <a class="qindex" href="namespaces.html">Namespace List</a> <a class="qindex" href="classes.html">Alphabetical List</a> <a class="qindex" href="annotated.html">Compound List</a> <a class="qindex" href="files.html">File List</a> <a class="qindex" href="functions.html">Compound Members</a> <a class="qindex" href="globals.html">File Members</a> <a class="qindex" href="pages.html">Related Pages</a> </center>
<hr><h1>server.cpp</h1><a href="server_8cpp.html">Go to the documentation of this file.</a><div class="fragment"><pre>00001
00009 <span class="preprocessor">#include "<a class="code" href="sysconfig_8h.html">sysconfig.h</a>"</span>
00010 <span class="preprocessor">#include "<a class="code" href="server_8h.html">server.h</a>"</span>
00011 <span class="preprocessor">#include "<a class="code" href="event_8h.html">event.h</a>"</span>
00012
<a name="l00016"></a><a class="code" href="classServer.html#a0">00016</a> <a class="code" href="classServer.html#a0">Server::Server</a>(<a class="code" href="classEventQueue.html">EventQueue</a>& r_inque, <a class="code" href="classEventQueue.html">EventQueue</a>& r_outque, <a class="code" href="classLog.html">Log</a>& r_lgfile)
00017 : mShutdown(false), mrLog(r_lgfile),
00018 mrInQueue(r_inque), mrOutQueue(r_outque) {
00019 }
00020
<a name="l00028"></a><a class="code" href="classServer.html#a1">00028</a> <a class="code" href="classServer.html#a1">Server::~Server</a>() {
00029 ConnList::iterator i;
00030 <span class="keywordflow">for</span> (i = <a class="code" href="classServer.html#o5">mConns</a>.begin(); i != <a class="code" href="classServer.html#o5">mConns</a>.end(); i++) {
00031 <span class="keyword">delete</span> (*i);
00032 }
00033 }
00034
<a name="l00041"></a><a class="code" href="classServer.html#a2">00041</a> <span class="keywordtype">bool</span> <a class="code" href="classServer.html#a2">Server::Boot</a>(<span class="keywordtype">unsigned</span> <span class="keywordtype">short</span> port) {
00042 <span class="comment">// Get a TCP protocol socket for the server to listen on.</span>
00043 <a class="code" href="classServer.html#o3">mAcceptor</a> = socket (AF_INET, SOCK_STREAM, 0);
00044 <span class="keywordflow">if</span> (<a class="code" href="classServer.html#o3">mAcceptor</a> == INVALID_SOCKET) {
00045 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(socket): %d"</span>, WSAGetLastError());
00046 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00047 }
00048
00049 <span class="comment">// Set the listening socket to be non-blocking</span>
00050 <span class="keywordflow">if</span> (!<a class="code" href="classServer.html#c6">SetNonBlocking</a>(<a class="code" href="classServer.html#o3">mAcceptor</a>))
00051 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00052
00053 <span class="comment">// Set up listening socket to allow local address reuse</span>
00054 <span class="keywordtype">int</span> sockopt_val = 1; <span class="comment">// Non-zero means turn on for boolean options,</span>
00055 <span class="comment">// some options require other types and values.</span>
00056 <span class="keywordflow">if</span> (setsockopt(<a class="code" href="classServer.html#o3">mAcceptor</a>, SOL_SOCKET, SO_REUSEADDR, (<span class="keywordtype">char</span> *)&sockopt_val,
00057 <span class="keyword">sizeof</span>(sockopt_val)) == SOCKET_ERROR) {
00058 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(setsockopt): %d"</span>,WSAGetLastError());
00059 closesocket(<a class="code" href="classServer.html#o3">mAcceptor</a>);
00060 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00061 }
00062
00063 <span class="comment">// Associate our local address with this socket</span>
00064 <a class="code" href="classServer.html#o4">mTo</a>.sin_family = AF_INET; <span class="comment">// protocol - internet</span>
00065 <a class="code" href="classServer.html#o4">mTo</a>.sin_addr.s_addr = INADDR_ANY; <span class="comment">// accept on whatever IP we're set up as</span>
00066 <a class="code" href="classServer.html#o4">mTo</a>.sin_port = htons(port); <span class="comment">// port must be in network byte order</span>
00067 <span class="keywordflow">if</span> (bind(<a class="code" href="classServer.html#o3">mAcceptor</a>, (<span class="keyword">struct</span> sockaddr *)&<a class="code" href="classServer.html#o4">mTo</a>, <span class="keyword">sizeof</span>(<a class="code" href="classServer.html#o4">mTo</a>)) == SOCKET_ERROR) {
00068 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(bind): %d"</span>,WSAGetLastError());
00069 closesocket(<a class="code" href="classServer.html#o3">mAcceptor</a>);
00070 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00071 }
00072 <span class="comment">// Let's start listening for incoming connections</span>
00073 <span class="keywordflow">if</span> (listen(<a class="code" href="classServer.html#o3">mAcceptor</a>, 5) == SOCKET_ERROR) { <span class="comment">// Connection backlog of 5.</span>
00074 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(listen): %d"</span>,WSAGetLastError());
00075 closesocket(<a class="code" href="classServer.html#o3">mAcceptor</a>);
00076 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00077 }
00078 <span class="comment">/*</span>
00079 <span class="comment"> mNumFds is calculated for the select() function - Windows ignores</span>
00080 <span class="comment"> it as the underlying FD sets are constructed differently than Unixes</span>
00081 <span class="comment"> On Unixes FDs are include all files, pipes and sockets open.</span>
00082 <span class="comment"> We need to calculate the highest FD number for the Unix select.</span>
00083 <span class="comment"></span>
00084 <span class="comment"> In bootup the highest FD is the listening socket so...</span>
00085 <span class="comment"> */</span>
00086 <a class="code" href="classServer.html#o6">mNumFds</a> = <a class="code" href="classServer.html#o3">mAcceptor</a>;
00087 <span class="keywordflow">return</span> <span class="keyword">true</span>;
00088 }
00089
00102 <span class="preprocessor">#pragma argsused</span>
<a name="l00103"></a><a class="code" href="classServer.html#a3">00103</a> <span class="preprocessor"></span><span class="keywordtype">void</span> <a class="code" href="classServer.html#a3">Server::Run</a>(<span class="keywordtype">void</span> * parms) {
00104 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"INFO-StartServer(): Server thread entered."</span>);
00105 <span class="keyword">struct </span>timeval timeout = { 0, 1 }; <span class="comment">// Will poll for 1 millisecond</span>
00106
00107 <span class="keywordflow">while</span> (!<a class="code" href="classServer.html#o0">mShutdown</a>) { <span class="comment">// Loops until a SHUTDOWN_E message is received.</span>
00108 ConnList::iterator i, curr;
00109
00110 <span class="comment">// Reset our socket interest set</span>
00111 <a class="code" href="classServer.html#c3">InitFDs</a>();
00112
00113 <span class="comment">// Poll our socket interest set for about a millisec.</span>
00114 <span class="keywordflow">if</span> (select(<a class="code" href="classServer.html#o6">mNumFds</a> + 1, &<a class="code" href="classServer.html#o1">mInputFDs</a>, &<a class="code" href="classServer.html#o2">mOutputFDs</a>, NULL, &timeout) == SOCKET_ERROR) {
00115 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(select): %d"</span>,WSAGetLastError());
00116 <span class="keywordflow">break</span>;
00117 }
00118
00119 <span class="comment">// If we have incoming connections on our listening port handle them.</span>
00120 <span class="keywordflow">if</span> (FD_ISSET(<a class="code" href="classServer.html#o3">mAcceptor</a>, &<a class="code" href="classServer.html#o1">mInputFDs</a>)) {
00121 <a class="code" href="classServer.html#c2">AcceptConnection</a>();
00122 }
00123
00124 <span class="comment">// If we have incoming data on any connections handle it.</span>
00125 <span class="keywordflow">for</span>(i = <a class="code" href="classServer.html#o5">mConns</a>.begin(); i != <a class="code" href="classServer.html#o5">mConns</a>.end(); i++) {
00126 <span class="keywordflow">if</span> (FD_ISSET((*i)->GetSocket(), &<a class="code" href="classServer.html#o1">mInputFDs</a>)) {
00127 (*i)->HandleInput();
00128
00129 <span class="comment">// Loop through fully formed messages (CRLF).</span>
00130 string* msg = (*i)->ReadMsg();
00131 <span class="keywordflow">while</span>(msg) {
00132 <span class="comment">// notify the chat driver.</span>
00133 <a class="code" href="classServer.html#o9">mrOutQueue</a>.<a class="code" href="classEventQueue.html#a2">Push</a>(<span class="keyword">new</span> <a class="code" href="classEvent.html">Event</a>(<a class="code" href="event_8h.html#a5a3">MESSAGE_E</a>,
00134 (*i)->GetSocket(), msg->length(), msg->c_str()));
00135 <span class="keyword">delete</span> msg; <span class="comment">// ReadMsg creates a new string - we must delete it.</span>
00136 msg = (*i)->ReadMsg();
00137 } <span class="comment">// while</span>
00138 } <span class="comment">// if</span>
00139 } <span class="comment">// for</span>
00140
00141 <span class="comment">// Process the event queue.</span>
00142 <a class="code" href="classServer.html#c4">ProcessQueue</a>();
00143
00144 <span class="comment">// If we have outgoing data ready on any connections handle it.</span>
00145 <span class="keywordflow">for</span>(i = <a class="code" href="classServer.html#o5">mConns</a>.begin(); i != <a class="code" href="classServer.html#o5">mConns</a>.end(); i++) {
00146 <span class="keywordflow">if</span> (FD_ISSET((*i)->GetSocket(), &<a class="code" href="classServer.html#o2">mOutputFDs</a>))
00147 (*i)->HandleOutput();
00148 }
00149
00150 <span class="comment">// Cleanup sockets that have been scheduled for disconnection.</span>
00151 <span class="keywordflow">for</span>(i = <a class="code" href="classServer.html#o5">mConns</a>.begin(); i != <a class="code" href="classServer.html#o5">mConns</a>.end();) {
00152 curr = i++;
00153 <span class="keywordflow">if</span> ((*curr)->CanBeDisconnected()) {
00154 (*curr)->Disconnect();
00155 <span class="comment">// Tell the chat driver about it.</span>
00156 <a class="code" href="classServer.html#o9">mrOutQueue</a>.<a class="code" href="classEventQueue.html#a2">Push</a>(<span class="keyword">new</span> <a class="code" href="classEvent.html">Event</a>(<a class="code" href="event_8h.html#a5a2">DISCONNECT_E</a>, (*curr)->GetSocket(),
00157 0, NULL));
00158 <span class="keyword">delete</span> (*curr);
00159 <a class="code" href="classServer.html#o5">mConns</a>.erase(curr);
00160 }
00161 }
00162 } <span class="comment">// while</span>
00163 <a class="code" href="classServer.html#c5">ShutdownServer</a>();
00164 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"INFO-StartServer(): Server thread exited."</span>);
00165 }
00166
<a name="l00174"></a><a class="code" href="classServer.html#c2">00174</a> <span class="keywordtype">void</span> <a class="code" href="classServer.html#c2">Server::AcceptConnection</a>() {
00175 <span class="keyword">struct </span>sockaddr_in addr; <span class="comment">// An address structure for our incoming connection.</span>
00176 <span class="keywordtype">int</span> addrlen = <span class="keyword">sizeof</span>(addr);
00177
00178 <span class="comment">// Try to accept the connection</span>
00179 SOCKET psock = accept(<a class="code" href="classServer.html#o3">mAcceptor</a>, (<span class="keyword">struct</span> sockaddr *)&addr, &addrlen);
00180 <span class="keywordflow">if</span> (psock != INVALID_SOCKET) {
00181 <span class="comment">// Set the socket to non-blocking.</span>
00182 <span class="keywordflow">if</span> (<a class="code" href="classServer.html#c6">SetNonBlocking</a>(psock)) {
00183 <span class="comment">// The socket set up correctly. Let us add a new Connection.</span>
00184 <a class="code" href="classConnection.html">Connection</a> *conn = <span class="keyword">new</span> <a class="code" href="classConnection.html">Connection</a>(psock, <span class="keyword">this</span>);
00185 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"INFO-Server(accept): Connection %d accepted"</span>, psock);
00186 <a class="code" href="classServer.html#o5">mConns</a>.push_back(conn); <span class="comment">// Put it on the connection list.</span>
00187 <span class="comment">// Tell the chat server we have a new connection</span>
00188 <a class="code" href="classServer.html#o9">mrOutQueue</a>.<a class="code" href="classEventQueue.html#a2">Push</a>(<span class="keyword">new</span> <a class="code" href="classEvent.html">Event</a>(<a class="code" href="event_8h.html#a5a1">CONNECT_E</a>, conn-><a class="code" href="classConnection.html#a11">GetSocket</a>(), 0, NULL));
00189 }
00190 } <span class="keywordflow">else</span> {
00191 <span class="comment">// Weve encountered an error during accept.</span>
00192 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-ServerAccept(accept): %d"</span>, WSAGetLastError());
00193 }
00194 }
00195
<a name="l00211"></a><a class="code" href="classServer.html#c3">00211</a> <span class="keywordtype">void</span> <a class="code" href="classServer.html#c3">Server::InitFDs</a> () {
00212 FD_ZERO (&<a class="code" href="classServer.html#o1">mInputFDs</a>); <span class="comment">// Clears our interest sets out.</span>
00213 FD_ZERO (&<a class="code" href="classServer.html#o2">mOutputFDs</a>);
00214
00215 FD_SET (<a class="code" href="classServer.html#o3">mAcceptor</a>, &<a class="code" href="classServer.html#o1">mInputFDs</a>); <span class="comment">// Register interest in listening socket.</span>
00216 <span class="keywordflow">for</span>(ConnList::iterator i = <a class="code" href="classServer.html#o5">mConns</a>.begin(); i != <a class="code" href="classServer.html#o5">mConns</a>.end(); i++) {
00217 <span class="comment">/*</span>
00218 <span class="comment"> mNumFds is calculated for the select() function - Windows ignores</span>
00219 <span class="comment"> it as the underlying FD sets are constructed differently than Unixes</span>
00220 <span class="comment"> On Unixes FDs are include all files, pipes and sockets open.</span>
00221 <span class="comment"> We need to calculate the highest FD number for the Unix select.</span>
00222 <span class="comment"> */</span>
00223 <a class="code" href="classServer.html#o6">mNumFds</a> = max(<a class="code" href="classServer.html#o6">mNumFds</a>, (*i)->GetSocket());
00224 FD_SET((*i)->GetSocket(), &<a class="code" href="classServer.html#o1">mInputFDs</a>); <span class="comment">// Always interested in input.</span>
00225 <span class="keywordflow">if</span> ((*i)->HasOutput()) <span class="comment">// See [1] above.</span>
00226 FD_SET((*i)->GetSocket(), &<a class="code" href="classServer.html#o2">mOutputFDs</a>);
00227 }
00228 }
00229
<a name="l00244"></a><a class="code" href="classServer.html#c4">00244</a> <span class="keywordtype">void</span> <a class="code" href="classServer.html#c4">Server::ProcessQueue</a>() {
00245 <span class="comment">// manage event queue</span>
00246 <a class="code" href="classEvent.html">Event</a>* e;
00247 <span class="keywordflow">while</span> ((e = <a class="code" href="classServer.html#o8">mrInQueue</a>.<a class="code" href="classEventQueue.html#a3">Pop</a>()) != NULL) {
00248 <span class="keywordflow">switch</span> (e-><a class="code" href="classEvent.html#m0">mEventType</a>) {
00249 <span class="keywordflow">case</span> <a class="code" href="event_8h.html#a5a2">DISCONNECT_E</a>:
00250 <span class="comment">// We've been told by the chat driver to kill a connection.</span>
00251 {
00252 <span class="comment">// Not yet implemented.</span>
00253 }
00254 <span class="keywordflow">break</span>;
00255 <span class="keywordflow">case</span> <a class="code" href="event_8h.html#a5a1">CONNECT_E</a>:
00256 <span class="comment">// Should never happen - included for completeness</span>
00257 <span class="keywordflow">break</span>;
00258 <span class="keywordflow">case</span> <a class="code" href="event_8h.html#a5a3">MESSAGE_E</a>:
00259 <span class="comment">// A message from the chat driver to route to a connection here.</span>
00260 {
00261 <span class="comment">// Test for null message data and ignore it</span>
00262 <span class="keywordflow">if</span> (e-><a class="code" href="classEvent.html#m3">mpData</a> && e-><a class="code" href="classEvent.html#m2">mDataLen</a>) {
00263 string msg;
00264 msg.append(e-><a class="code" href="classEvent.html#m3">mpData</a>, e-><a class="code" href="classEvent.html#m2">mDataLen</a>);
00265 <span class="keywordflow">for</span>(ConnList::iterator c = <a class="code" href="classServer.html#o5">mConns</a>.begin(); c != <a class="code" href="classServer.html#o5">mConns</a>.end(); c++) {
00266 <span class="keywordflow">if</span> ((*c)->GetSocket() == e-><a class="code" href="classEvent.html#m1">mClientId</a>) {
00267 (*c)->SendMsg(msg);
00268 <span class="comment">// Register interest as we have data ready on the wire</span>
00269 FD_SET ((*c)->GetSocket(), &<a class="code" href="classServer.html#o2">mOutputFDs</a>);
00270 }
00271 }
00272 }
00273 }
00274 <span class="keywordflow">break</span>;
00275 <span class="keywordflow">case</span> <a class="code" href="event_8h.html#a5a4">SHUTDOWN_E</a>:
00276 <span class="comment">// We've been requested to shutdown so let's comply.</span>
00277 <a class="code" href="classServer.html#o0">mShutdown</a> = <span class="keyword">true</span>;
00278 <span class="keywordflow">break</span>;
00279 <span class="keywordflow">case</span> <a class="code" href="event_8h.html#a5a0">NONE_E</a>:
00280 <span class="keywordflow">break</span>;
00281 } <span class="comment">// switch</span>
00282 <span class="keyword">delete</span> e;
00283 } <span class="comment">// while</span>
00284 }
00285
<a name="l00291"></a><a class="code" href="classServer.html#c5">00291</a> <span class="keywordtype">void</span> <a class="code" href="classServer.html#c5">Server::ShutdownServer</a>() {
00292 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"INFO-Server(shutdown): Server shutting down"</span>);
00293 ConnList::iterator ci, curr;
00294 <span class="keywordflow">for</span>(ci = <a class="code" href="classServer.html#o5">mConns</a>.begin(); ci != <a class="code" href="classServer.html#o5">mConns</a>.end();) {
00295 curr = ci++;<span class="comment">// :WARN: The list containers iterator is invalidated on erase.</span>
00296 <span class="comment">// So we need to increment it first and delete the old.</span>
00297 (*curr)->Disconnect();
00298 <span class="comment">// Tell chat driver about each connection as it leaves</span>
00299 <a class="code" href="classServer.html#o9">mrOutQueue</a>.<a class="code" href="classEventQueue.html#a2">Push</a>(<span class="keyword">new</span> <a class="code" href="classEvent.html">Event</a>(<a class="code" href="event_8h.html#a5a2">DISCONNECT_E</a>, (*curr)->GetSocket(),
00300 0, NULL));
00301 <span class="keyword">delete</span> (*curr);
00302 <a class="code" href="classServer.html#o5">mConns</a>.erase(curr);
00303 }
00304 closesocket(<a class="code" href="classServer.html#o3">mAcceptor</a>);
00305 <a class="code" href="classServer.html#o9">mrOutQueue</a>.<a class="code" href="classEventQueue.html#a2">Push</a>(<span class="keyword">new</span> <a class="code" href="classEvent.html">Event</a>(<a class="code" href="event_8h.html#a5a4">SHUTDOWN_E</a>, 0, 0, 0));
00306 }
00307
<a name="l00314"></a><a class="code" href="classServer.html#a4">00314</a> <a class="code" href="classLog.html">Log</a>& <a class="code" href="classServer.html#a4">Server::ServerLog</a>() {
00315 <span class="keywordflow">return</span> <a class="code" href="classServer.html#o7">mrLog</a>;
00316 }
00317
<a name="l00325"></a><a class="code" href="classServer.html#c6">00325</a> <span class="keywordtype">bool</span> <a class="code" href="classServer.html#c6">Server::SetNonBlocking</a>(SOCKET s) {
00326 <span class="keywordtype">unsigned</span> <span class="keywordtype">long</span> ioctl_cmd = 1; <span class="comment">// Non-zero means turn on for ioctl commands.</span>
00327 <span class="keywordflow">if</span> (ioctlsocket(s, FIONBIO, &ioctl_cmd) == SOCKET_ERROR) {
00328 <a class="code" href="classServer.html#a4">ServerLog</a>().<a class="code" href="classLog.html#a3">Write</a>(<span class="stringliteral">"ERROR-Server(SetNonBlocking): %d on socket %d"</span>,
00329 WSAGetLastError(), s);
00330 closesocket(s);
00331 <span class="keywordflow">return</span> <span class="keyword">false</span>;
00332 }
00333 <span class="keywordflow">return</span> <span class="keyword">true</span>;
00334 }
00335
</pre></div><hr><address style="align: right;"><small>Generated on Mon Mar 29 23:12:53 2004 for Paradigm by
<a href="http://www.doxygen.org/index.html">
<img src="doxygen.png" alt="doxygen" align="middle" border=0
width=110 height=53></a>1.2.17 </small></address>
</body>
</html>