source: box/trunk/lib/server/SocketListen.h @ 3066

Revision 3066, 7.3 KB checked in by chris, 4 months ago (diff)

Improve logging of socket errors (create, bind, accept, and poll)

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    SocketListen.h
5//              Purpose: Stream based sockets for servers
6//              Created: 2003/07/31
7//
8// --------------------------------------------------------------------------
9
10#ifndef SOCKETLISTEN__H
11#define SOCKETLISTEN__H
12
13#include <errno.h>
14
15#ifdef HAVE_UNISTD_H
16        #include <unistd.h>
17#endif
18
19#ifdef HAVE_KQUEUE
20        #include <sys/event.h>
21        #include <sys/time.h>
22#endif
23
24#ifndef WIN32
25        #include <poll.h>
26#endif
27
28#include <new>
29#include <memory>
30#include <string>
31
32#include "Socket.h"
33#include "ServerException.h"
34
35#include "MemLeakFindOn.h"
36
37// --------------------------------------------------------------------------
38//
39// Class
40//              Name:    _NoSocketLocking
41//              Purpose: Default locking class for SocketListen
42//              Created: 2003/07/31
43//
44// --------------------------------------------------------------------------
45class _NoSocketLocking
46{
47public:
48        _NoSocketLocking(int sock)
49        {
50        }
51       
52        ~_NoSocketLocking()
53        {
54        }
55       
56        bool HaveLock()
57        {
58                return true;
59        }
60       
61private:
62        _NoSocketLocking(const _NoSocketLocking &rToCopy)
63        {
64        }
65};
66
67
68// --------------------------------------------------------------------------
69//
70// Class
71//              Name:    SocketListen
72//              Purpose:
73//              Created: 2003/07/31
74//
75// --------------------------------------------------------------------------
76template<typename SocketType, int ListenBacklog = 128, typename SocketLockingType = _NoSocketLocking, int MaxMultiListenSockets = 16>
77class SocketListen
78{
79public:
80        // Initialise
81        SocketListen()
82                : mSocketHandle(-1)
83        {
84        }
85        // Close socket nicely
86        ~SocketListen()
87        {
88                Close();
89        }
90
91private:
92        SocketListen(const SocketListen &rToCopy)
93        {
94        }
95
96        int mType, mPort;
97        std::string mName;
98
99public:
100        enum
101        {
102                MaxMultipleListenSockets = MaxMultiListenSockets
103        };
104
105        void Close()
106        {
107                if(mSocketHandle != -1)
108                {
109#ifdef WIN32
110                        if(::closesocket(mSocketHandle) == -1)
111#else
112                        if(::close(mSocketHandle) == -1)
113#endif
114                        {
115                                BOX_LOG_SOCKET_ERROR(mType, mName, mPort,
116                                        "Failed to close network socket");
117                                THROW_EXCEPTION(ServerException,
118                                        SocketCloseError)
119                        }
120                }
121                mSocketHandle = -1;
122        }
123
124        // ------------------------------------------------------------------
125        //
126        // Function
127        //              Name:    SocketListen::Listen(int, char*, int)
128        //              Purpose: Initialises, starts the socket listening.
129        //              Created: 2003/07/31
130        //
131        // ------------------------------------------------------------------
132        void Listen(Socket::Type Type, const char *Name, int Port = 0)
133        {
134                mType = Type;
135                mName = Name;
136                mPort = Port;
137
138                if(mSocketHandle != -1)
139                {
140                        THROW_EXCEPTION(ServerException, SocketAlreadyOpen);
141                }
142               
143                // Setup parameters based on type, looking up names if required
144                int sockDomain = 0;
145                SocketAllAddr addr;
146                int addrLen = 0;
147                Socket::NameLookupToSockAddr(addr, sockDomain, Type, Name,
148                        Port, addrLen);
149       
150                // Create the socket
151                mSocketHandle = ::socket(sockDomain, SOCK_STREAM,
152                        0 /* let OS choose protocol */);
153                if(mSocketHandle == -1)
154                {
155                        BOX_LOG_SOCKET_ERROR(Type, Name, Port,
156                                "Failed to create a network socket");
157                        THROW_EXCEPTION(ServerException, SocketOpenError)
158                }
159               
160                // Set an option to allow reuse (useful for -HUP situations!)
161#ifdef WIN32
162                if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR, "",
163                        0) == -1)
164#else
165                int option = true;
166                if(::setsockopt(mSocketHandle, SOL_SOCKET, SO_REUSEADDR,
167                        &option, sizeof(option)) == -1)
168#endif
169                {
170                        BOX_LOG_SOCKET_ERROR(Type, Name, Port,
171                                "Failed to set socket options");
172                        THROW_EXCEPTION(ServerException, SocketOpenError)
173                }
174
175                // Bind it to the right port, and start listening
176                if(::bind(mSocketHandle, &addr.sa_generic, addrLen) == -1
177                        || ::listen(mSocketHandle, ListenBacklog) == -1)
178                {
179                        int err_number = errno;
180
181                        BOX_LOG_SOCKET_ERROR(Type, Name, Port,
182                                "Failed to bind socket");
183
184                        // Dispose of the socket
185                        ::close(mSocketHandle);
186                        mSocketHandle = -1;
187
188                        THROW_SYS_FILE_ERRNO("Failed to bind or listen "
189                                "on socket", Name, err_number,
190                                ServerException, SocketBindError);
191                }       
192        }
193       
194        // ------------------------------------------------------------------
195        //
196        // Function
197        //              Name:    SocketListen::Accept(int)
198        //              Purpose: Accepts a connection, returning a pointer to
199        //                       a class of the specified type. May return a
200        //                       null pointer if a signal happens, or there's
201        //                       a timeout. Timeout specified in
202        //                       milliseconds, defaults to infinite time.
203        //              Created: 2003/07/31
204        //
205        // ------------------------------------------------------------------
206        std::auto_ptr<SocketType> Accept(int Timeout = INFTIM,
207                std::string *pLogMsg = 0)
208        {
209                if(mSocketHandle == -1)
210                {
211                        THROW_EXCEPTION(ServerException, BadSocketHandle);
212                }
213               
214                // Do the accept, using the supplied locking type
215                int sock;
216                struct sockaddr addr;
217                socklen_t addrlen = sizeof(addr);
218                // BLOCK
219                {
220                        SocketLockingType socklock(mSocketHandle);
221                       
222                        if(!socklock.HaveLock())
223                        {
224                                // Didn't get the lock for some reason.
225                                // Wait a while, then return nothing.
226                                BOX_ERROR("Failed to get a lock on incoming "
227                                        "connection");
228                                ::sleep(1);
229                                return std::auto_ptr<SocketType>();
230                        }
231                       
232                        // poll this socket
233                        struct pollfd p;
234                        p.fd = mSocketHandle;
235                        p.events = POLLIN;
236                        p.revents = 0;
237                        switch(::poll(&p, 1, Timeout))
238                        {
239                        case -1:
240                                // signal?
241                                if(errno == EINTR)
242                                {
243                                        BOX_INFO("Failed to accept "
244                                                "connection: interrupted by "
245                                                "signal");
246                                        // return nothing
247                                        return std::auto_ptr<SocketType>();
248                                }
249                                else
250                                {
251                                        BOX_LOG_SOCKET_ERROR(mType, mName, mPort,
252                                                "Failed to poll connection");
253                                        THROW_EXCEPTION(ServerException,
254                                                SocketPollError)
255                                }
256                                break;
257                        case 0: // timed out
258                                return std::auto_ptr<SocketType>();
259                                break;
260                        default:        // got some thing...
261                                // control flows on...
262                                break;
263                        }
264                       
265                        sock = ::accept(mSocketHandle, &addr, &addrlen);
266                }
267
268                // Got socket (or error), unlock (implicit in destruction)
269                if(sock == -1)
270                {
271                        BOX_LOG_SOCKET_ERROR(mType, mName, mPort,
272                                "Failed to accept connection");
273                        THROW_EXCEPTION(ServerException, SocketAcceptError)
274                }
275
276                // Log it
277                if(pLogMsg)
278                {
279                        *pLogMsg = Socket::IncomingConnectionLogMessage(&addr,
280                                addrlen);
281                }
282                else
283                {
284                        // Do logging ourselves
285                        Socket::LogIncomingConnection(&addr, addrlen);
286                }
287
288                return std::auto_ptr<SocketType>(new SocketType(sock));
289        }
290       
291        // Functions to allow adding to WaitForEvent class, for efficient waiting
292        // on multiple sockets.
293#ifdef HAVE_KQUEUE
294        // ------------------------------------------------------------------
295        //
296        // Function
297        //              Name:    SocketListen::FillInKEevent
298        //              Purpose: Fills in a kevent structure for this socket
299        //              Created: 9/3/04
300        //
301        // ------------------------------------------------------------------
302        void FillInKEvent(struct kevent &rEvent, int Flags = 0) const
303        {
304                EV_SET(&rEvent, mSocketHandle, EVFILT_READ, 0, 0, 0,
305                        (void*)this);
306        }
307#else
308        // ------------------------------------------------------------------
309        //
310        // Function
311        //              Name:    SocketListen::FillInPoll
312        //              Purpose: Fills in the data necessary for a poll
313        //                       operation
314        //              Created: 9/3/04
315        //
316        // ------------------------------------------------------------------
317        void FillInPoll(int &fd, short &events, int Flags = 0) const
318        {
319                fd = mSocketHandle;
320                events = POLLIN;
321        }
322#endif
323       
324private:
325        int mSocketHandle;
326};
327
328#include "MemLeakFindOff.h"
329
330#endif // SOCKETLISTEN__H
331
Note: See TracBrowser for help on using the repository browser.