socket.c File Reference

Single-Threaded Socket Server implementation. More...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ctl/ctldef.h"
#include "ctl/pool.h"
#include "ctl/ctlnew.h"
#include "ctl/socket.h"
#include <fcntl.h>
#include <stropts.h>

Include dependency graph for socket.c:

Go to the source code of this file.

Functions

uint32 SocketID (const Socket *sock)
 Get a socket ID from its pointer.
SocketSocketLut (uint32 id)
 Get a socket pointer from its ID.
SocketSocketFind (const char *label)
 Get a socket pointer from its label Will only find the first one, if several are named the same. Linear search; don't use it in inner loops and such.
NewSocketCallback SocketsSetLogin (NewSocketCallback callback)
 Set a callback handler for new login socket creation.
NewSocketCallback SocketsSetUser (NewSocketCallback callback)
 Set a callback handler for new user socket creation.
bool SocketsInit (size_t input, size_t output, size_t heap)
 Start up sockets connections.
void SocketsShutdown (void)
 Shut down sockets connections.
bool SocketsCycle (unsigned ms)
 Sockets heartbeat.
const char * SocketsLastErrorString (void)
 Retrieve the system error message for the last-error code.
void SocketsLogLastError (unsigned mask, const char *fmt,...)
 Log information about socket errors.
struct addrinfo * SocketsParseAddress (const char *szAddress)
void SocketsLogHostent (uint32 logmask, const char *name)
 Tell us about an address we're running on.
SocketSocket_Create_Client (const char *address)
SocketSocket_Create_Listener (const char *label, const char *address)
 Make a listener. You'll need to make at least one to accept connections.
MLCallback SocketsSetHTTP (MLCallback callback)
 Setup what to tell web browsers that hit this server.
MLCallback SocketsSetFlashPolicy (MLCallback callback)
 Setup what to tell Flash clients that they can connect.
SocketSocket_Create_Login (SOCKET isock)
 Make a Login handler from an accepted connection Should filter various kinds of 'bad' connections and hang up before making an appropriate user connection.
SocketSocket_Create_User (Socket *sock, const char *label)
 Initialize a user connection from Login.
const uint8 * Socket_Raw_Consume (Socket *sock, size_t size)
 After receiving 'appReceived' callback from Raw socket, call this to consume some of the input data CAUTION: If this is not a RAW socket, you'll most likely bugger it.
SocketSocket_Create_Raw (Socket *sock)
 Initialize a RAW connection.
SocketSocket_Create (void)
 Allocate, initialize and register a new socket.
SocketSocket_Customize (Socket *sock, size_t input, size_t output, size_t heap)
 Allow default buffering to be modified for special cases.
void Socket_LogError (Socket *sock, unsigned mask, const char *fmt,...)
 Log information about a naughty socket after an error is detected.
void Socket_Close (Socket *sock)
 Mark a socket for cleanup.
bool Socket_Write (Socket *sock, const void *buff, size_t count)
 Write some raw data into the output queue.
bool Socket_Alloc_Serial (Socket *sock, ctl_serial *serial, size_t size)
 Allocate space for serial packet in output buffer to write into, put size and id on it.
bool Socket_Write_Serial (Socket *sock, const ctl_serial *serial)
 Write a fully formed and filled-in serial packet into output buffer; ignores 'curr'.


Detailed Description

Single-Threaded Socket Server implementation.

Author:
David Mace Implement lightweight socket handling with minimal dynamic memory use at runtime. That which is not allocated, can not leak. Data is read into buffers in such a way that it is seldom moved around.
NOT MULTITHREADED. This implements a single-threaded server solution because it's much easier and more efficient to build multi-player game projects on a single thread. There is generally too much inter-related data to work with in a multithreaded game environment. You spend all your time locking and unlocking things, and virtually all the threads end up blocked to the point where it IS single-threaded. You're far better off with one select loop, breaking down time-consuming operations, and moving file operations to external processes (i.e. another server).

Ultimately this will have a very slim web server (pseudo-web server) with just enough brains to serve certain cached files back at web browsers and Flash.

This will allow a single lightweight server to 'do everything' through one HTTP socket, with all of the things you'd typically expect to 'script' being hard-coded into the the server in C. You can use dynamically linked code (.so/.dll) to 'customize', but either way, it's a make, and too many people make their code complicated enough to fail by trying to add too many dynamically configurable things in an attempt not to compile and test. As if you didn't need to test your scripted code. KISS.

This works differently for Winsock versus BSD sockets. Visit the select (and not-select) loop in SocketsCycle() to see why.

Definition in file socket.c.


Function Documentation

bool Socket_Alloc_Serial ( Socket sock,
ctl_serial serial,
size_t  size 
)

Allocate space for serial packet in output buffer to write into, put size and id on it.

Parameters:
sock Instance to work with
serial Returns a serial packet to work with if function retuens true
size Amount of space we want in the serial packet
Returns:
true on success

Definition at line 1605 of file socket.c.

References assertobjptr, Socket::label, Socket::output, RingBuff_AddSerial(), Socket_Close(), Socket_ShouldIgnore, and SocketsLogLastError().

01606 {
01607     assertobjptr(sock);
01608     assertobjptr(serial);
01609     if( Socket_ShouldIgnore(sock) )
01610     {
01611         return false;
01612     }
01613     else
01614     {
01615         bool bRet = RingBuff_AddSerial( &sock->output, serial, size + 2 );
01616         if( bRet )
01617         {
01618             return true;
01619         }
01620         else
01621         {   /* Not enough room left! */
01622             SocketsLogLastError( LOG_ERROR, "Socket_Alloc_Serial(%s,%u) failed\n", sock->label, size );
01623             Socket_Close( sock );
01624             return false;
01625         }
01626     }
01627 }

Here is the call graph for this function:

void Socket_Close ( Socket sock  ) 

Mark a socket for cleanup.

Parameters:
sock Instance to work with

Definition at line 1539 of file socket.c.

References Socket::appDisconnect, Socket::appReceived, assertcodeptr, assertobjptr, SockVTAB::Close, Socket::id, Socket::label, Socket::socket, SocketsLogLastError(), trace, Socket::type, and Socket::vtab.

Referenced by Socket_Alloc_Serial(), Socket_Create_Listener(), Socket_Write(), Socket_Write_Serial(), SocketsCycle(), and SocketsShutdown().

01540 {
01541     assertobjptr(sock);
01542     trace(( "Socket_Close(%s)\n", sock->label ));
01543     /* Debug: See if socket got shredded */
01544     Socket_Validate(sock);
01545     sock->type = stUninitialized;
01546     sock->id = 0;
01547 
01548     /* Close socket handle */
01549     if( INVALID_SOCKET != sock->socket )
01550     {
01551         if( SOCKET_ERROR == closesocket(sock->socket) )
01552             SocketsLogLastError( LOG_ERROR, "Socket_Close(%d,%s) closesocket failed.\n", sock->socket, sock->label );
01553         sock->socket = INVALID_SOCKET;
01554     }
01555     
01556     /* Tell app to clean up after its self */
01557     assertcodeptr(sock->appDisconnect);
01558     sock->appDisconnect(sock);
01559 
01560     /* Tell Socket to clean up after its self */
01561     assertcodeptr(sock->vtab->Close);
01562     sock->vtab->Close(sock);
01563 
01564     /* Restore default application callbacks. */
01565     /* App will receive nothing more about this instance. */
01566     sock->appReceived = Socket_DefaultReceived;
01567     sock->appDisconnect = Socket_DefaultCallback;
01568 }

Here is the call graph for this function:

Socket* Socket_Create ( void   ) 

Allocate, initialize and register a new socket.

Returns:
New socket ready to be initialized

Definition at line 1330 of file socket.c.

References Socket::appDisconnect, Socket::appReceived, countof, Socket::created, ctl_fp_calloc, ctl_fp_index, ctl_mstime(), dll_push_back, Socket::id, Socket::label, Socket::lastmsg, Socket::socket, Socket_Customize(), Socket::timeout, and Socket::type.

Referenced by Socket_Create_Client(), Socket_Create_Listener(), and Socket_Create_Login().

01331 {
01332     Socket* sock;
01333     size_t index;
01334     /* Allocate & clear it; all values should be zero */
01335     ctl_fp_calloc(SocketPool, sockPool, sock );
01336     if( NULL == sock )
01337         return NULL;
01338     sock->type = stUninitialized;
01339     sock->timeout = 5000;
01340     ++cSockActive;
01341     --cSockFree;
01342     sock->created = sock->lastmsg = ctl_mstime();
01343 
01344     {   
01345         /*
01346          * Confusion reduction (or confusion enhancement)
01347          * Socket->id is 16 bits AND a 16 bit sequence counter.  
01348          *
01349          * This will reduce the likelihood that a socket ID freed and reused 
01350          * will end up being the same ID, and help spot slightly stale identifier 
01351          * references.
01352          *
01353          * Very, very stale identifiers may not be identified because there's still 
01354          * a very small chance that the counter will wrap around and make an old 
01355          * identifier look valid again.
01356          */
01357         static uint32 seq = 0;
01358         index = ctl_fp_index(SocketPool, sockPool, sock );
01359         sock->id = index | (seq++&0xffff)<<16;
01360     }
01361 
01362 
01363     /* Add to active list */
01364     dll_push_back(Socket,active, socksActive, sock );
01365         
01366     /* Set socket to invalid value */
01367     sock->socket = INVALID_SOCKET;
01368 
01369     /* Give it a default label */
01370     strncpy(sock->label, "Socket", countof(sock->label) );
01371 
01372     /* Alocate socket storage separately */
01373     /* Keep the socket structure data small because it's iterated */
01374     /* Preallocate everything ONCE to keep server from dying at some odd time due to insufficient resources.  Die immediately, please. */
01375     /* Allocated from pool we initialized at startup */
01376     Socket_Customize( sock, 0,0,0 );
01377 
01378     /* Setup default application callbacks */
01379     sock->appReceived = Socket_DefaultReceived;
01380     sock->appDisconnect = Socket_DefaultCallback;
01381     return sock;
01382 }

Here is the call graph for this function:

Socket* Socket_Create_Client ( const char *  address  ) 

Initialize a client connection Address is in form of "address:port" or "address:port" In this case, the ':port' on the end is required

Definition at line 849 of file socket.c.

References assertconst, countof, Socket::label, Socket_Create(), Socket::timeout, Socket::type, and Socket::vtab.

00850 {
00851     Socket* sock;
00852     assertconst(address,1);
00853     sock = Socket_Create();
00854     if( NULL == sock )
00855         return NULL;
00856     sock->type = stClient;
00857     sock->timeout = int64_max;
00858     LOG( LOG_INFO, "Socket_Create_Client(%s)\n", address );
00859     sock->vtab = &ClientVTAB;
00860     strncpy(sock->label, "Client", countof(sock->label) );
00861     return sock;
00862 }

Here is the call graph for this function:

Socket* Socket_Create_Listener ( const char *  label,
const char *  address 
)

Make a listener. You'll need to make at least one to accept connections.

Parameters:
label Something to call this listener
address "127.0.0.1:port", port ID to listen on, i.e. "1000" or "myport" 127.0.0.1 != computername Address must point at localhost, because we can't bind to other computers

Definition at line 944 of file socket.c.

References assertconst, countof, Socket::ip, Socket::label, Socket::socka_buff, Socket::socket, Socket_Close(), Socket_Create(), SocketsLogLastError(), SocketsParseAddress(), Socket::timeout, Socket::type, and Socket::vtab.

00945 {
00946     Socket* sock;
00947     struct addrinfo * service;
00948     assertconst(label,1);
00949     assertconst(address,1);
00950     LOG(LOG_STARTUP,"Socket_Create_Listener(%s,%s)\n", label, address );
00951     service = SocketsParseAddress( address );
00952     if( NULL == service )
00953         return NULL;
00954     sock = Socket_Create();
00955     if( NULL == sock )
00956         return NULL;
00957     sock->vtab = &ListenerVTAB;
00958     sock->type = stListener;
00959     sock->timeout = int64_max;
00960     strncpy( sock->label, label, countof(sock->label) );
00961     sock->socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP);
00962     if( sock->socket == INVALID_SOCKET ) 
00963     {
00964         SocketsLogLastError( LOG_ERROR, "Socket_Create_Listener(%s,%s) socket failed\n", sock->label,address );
00965         Socket_Close( sock );
00966         return NULL;
00967     }
00968     /* Setup socket */
00969     Socket_Options( sock );
00970     {
00971         /* Resolve port and listen */
00972         memcpy( &sock->socka_buff, service->ai_addr, min(service->ai_addrlen,sizeof(sock->socka_buff)) );
00973         LOG( LOG_STARTUP, "Listen: %s\n", service->ai_canonname );
00974         freeaddrinfo(service);
00975         SocketLogIP(sock,LOG_INFO,"Address: ");
00976         if( SOCKET_ERROR == bind( sock->socket, (struct sockaddr*)&sock->ip, sizeof(sock->ip)) ) 
00977         {
00978             SocketsLogLastError( LOG_ERROR, "Socket_Create_Listener(%s,%s) bind failed.\n", sock->label,address );
00979             Socket_Close( sock );
00980             return NULL;
00981         }
00982     }
00983     /* Listen for connections, and allow a back-log of half the users trying to get on at once */
00984     if( SOCKET_ERROR == listen( sock->socket, MAX_SOCKETS/2 ) )
00985     {
00986         SocketsLogLastError( LOG_ERROR, "Socket_Create_Listener(%s,%s) listen failed.\n", sock->label,address );
00987         Socket_Close( sock );
00988         return NULL;
00989     }
00990     return sock;
00991 }

Here is the call graph for this function:

Socket* Socket_Create_Login ( SOCKET  isock  ) 

Make a Login handler from an accepted connection Should filter various kinds of 'bad' connections and hang up before making an appropriate user connection.

Parameters:
isock Socket handle from accept to create from

Definition at line 1168 of file socket.c.

References Socket::address, assertcodeptr, countof, Socket::label, Socket::socka_buff, Socket::socket, Socket_Create(), Socket::timeout, Socket::type, and Socket::vtab.

01169 {
01170     Socket* sock = Socket_Create();
01171     if( NULL == sock )
01172         return NULL;
01173     sock->vtab = &LoginVTAB;
01174     sock->type = stLogin;
01175     sock->socket = isock;
01176     sock->timeout = 5000; /* We expect a messages immediately upon connection */
01177     {
01178         /* Grab address from connecting client */
01179         size_t addrlen = sizeof(sock->socka_buff);
01180         getpeername( sock->socket, &sock->address, &addrlen );
01181     }
01182     cstrncpy(char)( sock->label, "Login", countof(sock->label) );
01183     /*traceip("Socket_Create_Login:",&sock->address); */
01184     /* Setup socket */
01185     Socket_Options( sock );
01186     /* Call application callback for dynamically created Login socket */
01187     assertcodeptr(login_callback);
01188     login_callback(sock);
01189     return sock;
01190 }

Here is the call graph for this function:

Socket* Socket_Create_Raw ( Socket sock  ) 

Initialize a RAW connection.

Parameters:
sock A Login socket to be transformed into a user. Similar to a User connection, but you get serialdata bounded only by what is received. If you don't consume the data, it's left on the input stack until it overflows. Support 'other' protocols besides 16 bit length-encoded data

Definition at line 1312 of file socket.c.

References assertcodeptr, assertobjptr, countof, Socket::label, Socket::timeout, Socket::type, and Socket::vtab.

01313 {
01314     assertobjptr(sock);
01315     sock->vtab = &RawVTAB;
01316     sock->type = stRaw;
01317     sock->timeout = 15000;
01318     cstrncpy(char)( sock->label, "Raw", countof(sock->label) );
01319     /*trace(("Socket_Create_Raw(%s)\n", sock->label )); */
01320     /* Call application callback for dynamically created Raw socket */
01321     assertcodeptr(user_callback);
01322     user_callback(sock);
01323     return sock;
01324 }

Socket* Socket_Create_User ( Socket sock,
const char *  label 
)

Initialize a user connection from Login.

Parameters:
sock A Login socket to be transformed into a user.
label What to call this socket from now on (such as user's name). All we're doing is transforming from a stateful socket that validates new connections to a socket that handles what the server expects in normal transactions; it doesn't mean we TRUST it, only that a subset of graceful checks can be ignored from now on, and the server app can begin initializing user information.

Definition at line 1214 of file socket.c.

References assertcodeptr, assertconst, assertobjptr, countof, Socket::label, Socket::timeout, Socket::type, and Socket::vtab.

01215 {
01216     assertobjptr(sock);
01217     assertconst(label,1);
01218     sock->vtab = &UserVTAB;
01219     sock->type = stUser;
01220     sock->timeout = (15*60*1000); /* Let user go idle for 15 minutes */
01221     cstrncpy(char)(sock->label, label, countof(sock->label));
01222     /*trace(("Socket_Create_User(%s)\n", sock->label )); */
01223     /* Call application callback for dynamically created User socket */
01224     assertcodeptr(user_callback);
01225     user_callback(sock);
01226     return sock;
01227 }

Socket* Socket_Customize ( Socket sock,
size_t  input,
size_t  output,
size_t  heap 
)

Allow default buffering to be modified for special cases.

Parameters:
sock Socket to modify
input New input size; 0 to restore default buffer
input New output size; 0 to restore default buffer
input New heap size; 0 to restore default buffer
Returns:
Socket passed in For special case connections, like a database, where normal input/output buffering limits may need to be extended. Invoking this when there is already data in the I/O buffers will bugger things. Best times would be in Login callback (before sending challenge) or after creating a Client, but also before sending anything.

Definition at line 1421 of file socket.c.

References Socket::appheap, assert, assertobjptr, ctl_fp_index, ctl_free, ctl_heap_init, ctl_malloc, Socket::input, Socket::output, Socket::pAppHeap, Socket::pInput, Socket::pOutput, and RingBuff_Init().

Referenced by Socket_Create().

01422 {
01423     size_t index;
01424     assertobjptr(sock);
01425     index = ctl_fp_index(SocketPool, sockPool, sock );
01426     assert( index >= 0 && index < MAX_SOCKETS );
01427     /* Free previously customized buffers (I don't wanna leak even if you're doing it wrong) */
01428     if( NULL != sock->pInput && SOCK_INPUT_DEFAULTPTR(index) != sock->pInput )
01429         ctl_free( sock->pInput );
01430     if( NULL != sock->pOutput && SOCK_OUTPUT_DEFAULTPTR(index) != sock->pOutput )
01431         ctl_free( sock->pOutput );
01432     if( NULL != sock->pAppHeap && SOCK_HEAP_DEFAULTPTR(index) != sock->pAppHeap )
01433         ctl_free( sock->pAppHeap );
01434     if( input > sockInSize )
01435     {
01436         ctl_malloc( sock->pInput, input + sizeof(uint32));
01437     }
01438     else
01439     {
01440         input = sockInSize;
01441         sock->pInput = SOCK_INPUT_DEFAULTPTR(index);
01442     }
01443     if( output > sockOutSize )
01444     {
01445         ctl_malloc( sock->pOutput, output + sizeof(uint32) );
01446     }
01447     else
01448     {
01449         output = sockOutSize;
01450         sock->pOutput= SOCK_OUTPUT_DEFAULTPTR(index);
01451     }
01452     if( heap > sockHeapSize )
01453     {
01454         ctl_malloc( sock->pAppHeap, heap + sizeof(uint32) );
01455     }
01456     else
01457     {
01458         heap = sockHeapSize;
01459         sock->pAppHeap = SOCK_HEAP_DEFAULTPTR(index);
01460     }
01461     /* Setup input/output ring buffers */
01462     memset( sock->pInput, 0, input );
01463     RingBuff_Init( &sock->input, sock->pInput, sockInSize );
01464     memset( sock->pInput, 0, output );
01465     RingBuff_Init( &sock->output, sock->pOutput, sockOutSize );
01466     /* Setup application heap */
01467     memset( sock->pAppHeap, 0, heap );
01468     ctl_heap_init( &sock->appheap, sock->pAppHeap, sockHeapSize );
01469     /* Initialize guard bytes whether we're checking them or not (memory dump friendly) */
01470     *((uint32*)(sock->pInput + input)) = GUARD_BYTES;
01471     *((uint32*)(sock->pOutput + output)) = GUARD_BYTES;
01472     *((uint32*)(sock->pAppHeap + heap)) = GUARD_BYTES;
01473     return sock;
01474 }

Here is the call graph for this function:

void Socket_LogError ( Socket sock,
unsigned  mask,
const char *  fmt,
  ... 
)

Log information about a naughty socket after an error is detected.

Parameters:
sock Instance to check
mask LOG mask
fmt Format for printf

Definition at line 1483 of file socket.c.

References assertconst, debug_vlog(), and SocketsLastErrorString().

Referenced by SocketsCycle().

01484 {
01485     va_list args;
01486     va_start( args, fmt );
01487     int save_errno = SocketError();
01488     assertconst(fmt,1);
01489     debug_vlog(mask, fmt, args );
01490     SocketLogIP(sock,mask,"");
01491     SocketSetError(save_errno);
01492     LOG( mask, "%s\n", SocketsLastErrorString() );
01493 }

Here is the call graph for this function:

const uint8* Socket_Raw_Consume ( Socket sock,
size_t  size 
)

After receiving 'appReceived' callback from Raw socket, call this to consume some of the input data CAUTION: If this is not a RAW socket, you'll most likely bugger it.

Parameters:
sock A Login socket to be transformed into a user.
size Number of bytes to consume

Definition at line 1284 of file socket.c.

References assertobjptr, Socket::input, and RingBuff_Pop().

01285 {
01286     const uint8* data;
01287     assertobjptr(sock);
01288     RingBuff_Pop( &sock->input, &data, size );
01289     return data;
01290 }

Here is the call graph for this function:

bool Socket_Write ( Socket sock,
const void *  buff,
size_t  count 
)

Write some raw data into the output queue.

Parameters:
sock Instance to work with
buff Pointer to data to write
count Length of data to write
Returns:
true on success

Definition at line 1577 of file socket.c.

References assertconst, assertobjptr, Socket::label, Socket::output, RingBuff_CopyTo(), Socket_Close(), Socket_ShouldIgnore, and SocketsLogLastError().

01578 {
01579     assertobjptr(sock);
01580     assertconst(buff,count);
01581     if( Socket_ShouldIgnore(sock) )
01582     {
01583         return false;
01584     }
01585     else
01586     {
01587         bool bRet = RingBuff_CopyTo( &sock->output, buff, count );
01588         if( !bRet )
01589         {
01590             SocketsLogLastError( LOG_ERROR, "Socket_Write(%s,%u) failed\n", sock->label, count );
01591             Socket_Close( sock );
01592             return false;
01593         }
01594         return true;
01595     }
01596 }

Here is the call graph for this function:

bool Socket_Write_Serial ( Socket sock,
const ctl_serial serial 
)

Write a fully formed and filled-in serial packet into output buffer; ignores 'curr'.

Parameters:
sock Instance to work with
serial Returns a serial packet to work with if function retuens true
Returns:
true on success

Definition at line 1635 of file socket.c.

References assertobjconst, assertobjptr, ctl_serial::begin, ctl_serial_total, Socket::label, Socket::output, RingBuff_CopyTo(), Socket_Close(), Socket_ShouldIgnore, and SocketsLogLastError().

01636 {
01637     assertobjptr(sock);
01638     assertobjconst(serial);
01639     if( Socket_ShouldIgnore(sock) )
01640     {
01641         return false;
01642     }
01643     else
01644     {
01645         size_t length = ctl_serial_total( serial );
01646         bool bRet = RingBuff_CopyTo( &sock->output, serial->begin, length );
01647         if( !bRet )
01648         {   /* Not enough room left! */
01649             SocketsLogLastError( LOG_ERROR, "Socket_Write_Serial(%s,%u) failed\n", sock->label, length );
01650             Socket_Close( sock );
01651             return false;
01652         }
01653         return true;
01654     }
01655 }

Here is the call graph for this function:

Socket* SocketFind ( const char *  label  ) 

Get a socket pointer from its label Will only find the first one, if several are named the same. Linear search; don't use it in inner loops and such.

Parameters:
label Name of socket
Returns:
Pointer to socket, NULL if socket isn't valid/found

Definition at line 138 of file socket.c.

References assertconst, assertobjptr, socket_foreach, and Socket_ShouldIgnore.

00139 {
00140     assertconst(label,1);
00141     {
00142         socket_foreach( sock )
00143         {
00144             assertobjptr(sock);
00145             if( !Socket_ShouldIgnore(sock) && !strcmp(sock->label,label) )
00146             {
00147                 return sock;
00148             }
00149         }
00150     }
00151     return NULL;
00152 }

uint32 SocketID ( const Socket sock  ) 

Get a socket ID from its pointer.

Parameters:
sock Instance to look up
Returns:
id if found, 0 if socket isn't a valid index

Definition at line 104 of file socket.c.

References assertobjconst, ctl_fp_valid, Socket::id, and Socket_ShouldIgnore.

00105 {
00106     assertobjconst(sock);
00107     if( !ctl_fp_valid(SocketPool, sockPool, sock ) )
00108         return 0;
00109     if( Socket_ShouldIgnore(sock) )
00110         return 0;
00111     return sock->id;
00112 }

Socket* SocketLut ( uint32  id  ) 

Get a socket pointer from its ID.

Parameters:
id Socket ID to lookup
Returns:
Pointer to socket, NULL if socket isn't valid

Definition at line 119 of file socket.c.

References ctl_fp_lut, Socket::id, and Socket_ShouldIgnore.

00120 {
00121     Socket* sock = ctl_fp_lut(SocketPool, sockPool, id&0xffff );
00122     if( NULL == sock )
00123         return NULL;
00124     if( sock->id != id )
00125         return NULL;
00126     if( Socket_ShouldIgnore(sock) )
00127         return NULL;
00128     return sock;
00129 }

bool SocketsCycle ( unsigned  ms  ) 

Sockets heartbeat.

Parameters:
ms Milliseconds to time-out waiting for activity
Returns:
true if everything is OK, false if there was some (probably fatal) problem - this means select() failed

Definition at line 288 of file socket.c.

References assertcodeptr, assertobjptr, ctl_mstime(), Socket_Close(), socket_foreach, socket_foreach_deletable, Socket_LogError(), Socket_ShouldIgnore, and SocketsLogLastError().

00289 {
00290     mstime tick = ctl_mstime();
00291 #ifdef WIN32
00292     /*
00293      * Under Windows, select() SUCKS.  Under WinSock, fd_set is an array of  
00294      * handles that is searched linearly.  A call to FD_SET in that case could  
00295      * scan through hundreds, even thousands of handles every time, times  
00296      * hundreds, even thousands of handles.  It makes it much more efficient to  
00297      * poll every socket and Sleep(1), even with the OS context switches.   
00298      * The replacement in the Winsock API is to set up threads for every  
00299      * socket, or have a WindowProc receive a message, or there's one that will 
00300      * check up to 64 handles at a time....  Just ugly.
00301      * 
00302      * For WIN32, we yield up time to the rest of the OS, since this will 
00303      * otherwise be a hard loop.  Makes CPU usage (on a given core) go from 
00304      * 100% to around 3%, so though not quite as good as select, it will do 
00305      * to keep the system running for testing in a Windows environment.
00306      */
00307     Sleep( 1 );
00308 
00309     /* Free dead sockets */
00310     {
00311         socket_foreach_deletable( sock )
00312         {
00313             assertobjptr(sock);
00314             if( tick - sock->lastmsg > sock->timeout )
00315             {   /* Close timed-out sockets */
00316                 SocketLogIP(sock,LOG_INFO,"Timed out:");
00317                 Socket_Close(sock);
00318             }
00319             if( stUninitialized == sock->type )
00320             {
00321                 Socket_Free(sock);
00322             }
00323         }
00324     }
00325 #else
00326     /*
00327      * Under Linux/Unix/etc. select() is THE way to go.  The server can go to 
00328      * sleep nicely when there's nothing to do, and you don't have to do  
00329      * thousands of context switches between application and OS to see if  
00330      * anything needs to happen.  The fd_set structure and macros under a BSD  
00331      * implementation are a set of bit masks.  Accessing the list is an indexed
00332      * lookup with some masking, and entries in the list are one bit, so the 
00333      * data is 1/32 the size of the list of handles you get in Windows, too.
00334      */
00335     static fd_set fdreads;
00336     static fd_set fdwrites;
00337     /* Maybe see about cacheing fd_set data...? */
00338     FD_ZERO(&fdreads);
00339     FD_ZERO(&fdwrites);
00340     {
00341         int nfds = -1;
00342         socket_foreach_deletable( sock )
00343         {
00344             assertobjptr(sock);
00345             if( stUninitialized != sock->type && INVALID_SOCKET != sock->socket )
00346             {
00347                 if( tick - sock->lastmsg > sock->timeout )
00348                 {   /* Close timed-out sockets */
00349                     SocketLogIP(sock,LOG_INFO,"Timed out:");
00350                     Socket_Close(sock);
00351                 }
00352                 else
00353                 {
00354                     SOCKET sockCurr = sock->socket;
00355                     if( stSendingFinal != sock->type )
00356                     {
00357                         /* Don't listen for more incoming messages from closing sockets */
00358                         FD_SET( sockCurr, &fdreads );
00359                     }
00360                     if( !RingBuff_IsEmpty(&sock->output) )
00361                     {
00362                         FD_SET( sockCurr, &fdwrites );
00363                     }
00364                     if( sock->socket > nfds )
00365                     {
00366                         nfds = sockCurr;
00367                     }
00368                 }
00369             }
00370             if( stUninitialized == sock->type )
00371             {
00372                 Socket_Free(sock);
00373             }
00374         }
00375         /* Do select on all sockets, time-out appropriately as passed in */
00376         {
00377             struct timeval timeout = { ms/1000, (ms%1000)*1000 };
00378             nfds = select( 1+nfds, &fdreads, &fdwrites, NULL, 0 == ms ? NULL : &timeout );
00379         }
00380         if( 0 == nfds )
00381         {
00382             return true;
00383         }
00384         else if( SOCKET_ERROR == nfds )
00385         {
00386             int save_errno = SocketError();
00387             switch( save_errno )
00388             {
00389             case EBADF:
00390                 /*
00391                  * One handle might be invalid - scan them all
00392                  * Shouldn't ever happen, but if one fd somehow becomes invalid,
00393                  * should we crash & nuke everyone, or just the 'invalid' one?
00394                  * I'll just assume you want to close the invalid one.
00395                  */
00396                 {
00397                     SocketsLogLastError( LOG_ERROR, "select() EBADF.\n" );
00398                     bool bFound = false;
00399                     socket_foreach_deletable( suspect )
00400                     {
00401                         int tmp;
00402                         int ret = ioctl( suspect->socket, I_GETSIG, &tmp );
00403                         if( (-1 == ret) && (EBADF == SocketError()) )
00404                         {
00405                             Socket_LogError( suspect, LOG_ERROR, "select() BAD HANDLE: %d.\n", suspect->socket );
00406                             Socket_Close( suspect );
00407                             bFound = true;
00408                         }
00409                     }
00410                     /* If we couldn't find the 'bad' socket, something else is 
00411                      * wrong, and that's probably fatal. */
00412                     return bFound;
00413                 }
00414                 break;
00415             case EAGAIN:
00416             case EINTR:
00417                 /* Time-out. */
00418                 break;
00419             case EINVAL:
00420                 
00421                 /* nfds arg might be <0 or >FD_SETSIZE */
00422                 if( (1+nfds < 0) || (1+nfds > FD_SETSIZE) )
00423                 {
00424                     SocketsLogLastError( LOG_ERROR, "select() nfds out of range.\n" );
00425                     return false;
00426                 }
00427                 
00428                 /* Invalid timeout interval (not likely, and a bug, anyways) */
00429 
00430                 /* STREAM or multiplexer downstream from multiplexer */
00431                 
00432                 /* Fall-through to default error handler */
00433             default:
00434                 SocketsLogLastError( LOG_ERROR, "select() failed.\n" );
00435                 // Make sure previous function(s) don't overwrite error status
00436                 SocketSetError(save_errno);
00437                 return false;
00438             }
00439         }
00440     }
00441 
00442 #endif
00443 
00444     /* Attempt to write sockets first, to make room for side-effects of reading sockets */
00445     {
00446         socket_foreach( sock )
00447         {
00448             assertobjptr(sock);
00449             if( stUninitialized != sock->type ppNotWindows(&& FD_ISSET(sock->socket,&fdwrites)) )
00450             {
00451                 assertcodeptr(sock->vtab->WriteCycle);
00452                 sock->vtab->WriteCycle(sock);
00453             }
00454         }
00455     }
00456 
00457     /* Attempt to read sockets */
00458     {
00459         socket_foreach( sock )
00460         {
00461             assertobjptr(sock);
00462             if( !Socket_ShouldIgnore(sock) ppNotWindows(&& FD_ISSET(sock->socket,&fdreads)) )
00463             {
00464                 assertcodeptr(sock->vtab->ReadCycle);
00465                 sock->vtab->ReadCycle(sock);
00466             }
00467         }
00468     }
00469 
00470     /* Normal-looking status */
00471     return true;
00472 }

Here is the call graph for this function:

bool SocketsInit ( size_t  input,
size_t  output,
size_t  heap 
)

Start up sockets connections.

Parameters:
input Size of socket input buffers
output Size of socket output buffers (larger than input)
heap Size of scratch heap attached to each socket
Returns:
true for success, false for failure You will need: ((input+output+heap)*MAX_SOCKETS) bytes of RAM for buffers Underlying the system, MAX_SOCKETS * whatever sockets needs

Definition at line 205 of file socket.c.

References ctl_alloctype, ctl_fp_init, ctl_malloc, SocketsLogLastError(), and trace.

00206 {
00207     trace(("SocketsInit()\n"));
00208     sockInSize = input;
00209     sockOutSize = output;
00210     sockHeapSize = heap;
00211     sockBufferSize = (sockInSize + sockOutSize + (2*sizeof(uint32)));
00212     sockOffsetOut = sockInSize + sizeof(uint32);
00213     sockOffsetEnd = sockOffsetOut + sizeof(uint32);
00214     bufferSize = MAX_SOCKETS * sockBufferSize;
00215     sockUserSize = sockHeapSize + sizeof(uint32);
00216     userSize = MAX_SOCKETS * sockUserSize;
00217 
00218 #ifdef WIN32
00219     {
00220         /* Setup Winsock */
00221         int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
00222         if (iResult != 0) 
00223         {
00224             SocketsLogLastError( LOG_ERROR, "WSAStartup failed: %d\n", iResult );
00225             return false;
00226         }
00227         LOG( LOG_STARTUP,"%s - %s\n", wsaData.szDescription, wsaData.szSystemStatus  );
00228     }
00229 #endif
00230 
00231     /* Initialize socket pool */
00232     ctl_alloctype(sockPool,SocketPool);
00233     ctl_fp_init( SocketPool, sockPool );
00234     /* 
00235      * Allocate all the socket storage at once
00236      * I'd rather die right away for insufficient space than at 2:00am when the 834th user logs in
00237      */
00238     ctl_malloc( bufferSpace, bufferSize );
00239     ctl_malloc( userSpace, userSize );
00240     return true;
00241 }

Here is the call graph for this function:

const char* SocketsLastErrorString ( void   ) 

Retrieve the system error message for the last-error code.

Returns:
String pointer describing the error

Definition at line 478 of file socket.c.

Referenced by Socket_LogError(), and SocketsLogLastError().

00479 {
00480 #ifdef WIN32
00481     static char szError[512]; // <- Multithread risk
00482     DWORD dw = SocketError(); 
00483     FormatMessage(
00484         FORMAT_MESSAGE_FROM_SYSTEM | 
00485         FORMAT_MESSAGE_IGNORE_INSERTS |
00486         FORMAT_MESSAGE_MAX_WIDTH_MASK,
00487         NULL, 
00488         dw, 
00489         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00490         szError, sizeof(szError)/sizeof(*szError), 
00491         NULL);
00492     return szError;
00493 #else
00494     return strerror(SocketError());
00495 #endif
00496 }

void SocketsLogLastError ( unsigned  mask,
const char *  fmt,
  ... 
)

Log information about socket errors.

Parameters:
mask LOG mask
fmt Format for printf

Definition at line 503 of file socket.c.

References assertconst, debug_vlog(), and SocketsLastErrorString().

Referenced by Socket_Alloc_Serial(), Socket_Close(), Socket_Create_Listener(), Socket_Write(), Socket_Write_Serial(), SocketsCycle(), SocketsInit(), SocketsLogHostent(), and SocketsParseAddress().

00504 {
00505     va_list args;
00506     va_start( args, fmt );
00507     assertconst(fmt,1);
00508     debug_vlog(mask, fmt, args );
00509     LOG( mask, "%s\n", SocketsLastErrorString() );
00510 }

Here is the call graph for this function:

struct addrinfo* SocketsParseAddress ( const char *  szAddress  )  [read]

Parse an ip:port address into a sockaddr_in list. Does not handle character escapes or embedded parameters like a web url. Potentially very time-consuming (does a blocking namespace lookup, possibly all the way to internet DNS) Use only at startup when opening listeners and dedicated client connections Handles 'friendly' names like, 'localhost' and 'www-http', so named ports or computers work

Parameters:
szAddress String containing ip:port (:port is REQUIRED)
Returns:
List from getaddrinfo(3), or NULL if there was a problem; USE freeaddrinfo();

Definition at line 521 of file socket.c.

References assertconst, SocketsLogHostent(), SocketsLogLastError(), and trace.

Referenced by Socket_Create_Listener().

00522 {
00523     char ipbuff[256];
00524     const char* port;
00525     const char* address;
00526     const char* scan;
00527     assertconst(szAddress,1);
00528     scan = address = szAddress;
00529     /* Look for colon */
00530     while( ':' != *scan )
00531     {
00532         if( 0 == *scan )
00533             return NULL;
00534         scan++;
00535     }
00536     /* If we found '://', skip over protocol and search for ':' */
00537     if( *scan == '/' )
00538     {
00539         ++scan;
00540         if( *scan == '/' )
00541         {
00542             address = ++scan;
00543             while( ':' != *scan )
00544             {
00545                 if( 0 == *scan )
00546                 {
00547                     LOG( LOG_ERROR, "SocketsParseAddress(%s) no ':port'.\n", szAddress );
00548                     return NULL;
00549                 }
00550                 scan++;
00551             }
00552         }
00553         else
00554         {
00555             LOG( LOG_ERROR, "SocketsParseAddress(%s) found ':/' instead of '://'.\n", szAddress );
00556             return NULL;
00557         }
00558     }
00559     if( *scan == ':' )
00560     {
00561         size_t address_len;
00562         port = scan+1;
00563         address_len = (scan-address);
00564         if( address_len > sizeof(ipbuff) )
00565         {
00566             LOG( LOG_ERROR, "SocketsParseAddress(%s) address too long.\n", szAddress );
00567             return NULL;
00568         }
00569         memcpy(ipbuff,address,address_len);
00570         ipbuff[address_len] = 0;
00571         SocketsLogHostent( LOG_INFO, ipbuff );
00572         {
00573             struct addrinfo aiHints;
00574             struct addrinfo *aiList = NULL;
00575             memset(&aiHints, 0, sizeof(aiHints));
00576             aiHints.ai_family = AF_INET;
00577             aiHints.ai_socktype = SOCK_STREAM;
00578             aiHints.ai_flags = AI_CANONNAME;
00579             aiHints.ai_protocol = IPPROTO_TCP;
00580             if( 0 != getaddrinfo(ipbuff, port, &aiHints, &aiList) )
00581             {
00582                 SocketsLogLastError( LOG_ERROR, "SocketsParseAddress(%s) failed.\n", szAddress );
00583                 return NULL;
00584             }
00585 #if 1
00586             {
00587                 struct addrinfo *ai = aiList;
00588                 while( NULL != ai )
00589                 {
00590                     trace(( "%s: ", ai->ai_canonname ));
00591                     traceip("Address:",ai->ai_addr);
00592                     ai = ai->ai_next;
00593                 }
00594             }
00595 #endif
00596             return aiList;
00597         }
00598     }
00599     return NULL;
00600 }

Here is the call graph for this function:

MLCallback SocketsSetFlashPolicy ( MLCallback  callback  ) 

Setup what to tell Flash clients that they can connect.

Returns:
String containing XML text.

Definition at line 1046 of file socket.c.

References assertcodeptr.

01047 {
01048     MLCallback prev = cbFlashPolicy;
01049     cbFlashPolicy = NULL == callback ? defcbFlashPolicy: callback;
01050     assertcodeptr(cbFlashPolicy);
01051     return prev;
01052 }

MLCallback SocketsSetHTTP ( MLCallback  callback  ) 

Setup what to tell web browsers that hit this server.

Returns:
String containing HTML page. Should auto-refresh to REAL home page.

Definition at line 1035 of file socket.c.

References assertcodeptr.

01036 {
01037     MLCallback prev = cbHTTP;
01038     cbHTTP = NULL == callback ? defcbHTTP: callback;
01039     assertcodeptr(cbHTTP);
01040     return prev;
01041 }

NewSocketCallback SocketsSetLogin ( NewSocketCallback  callback  ) 

Set a callback handler for new login socket creation.

Parameters:
callback Callback function, or NULL for no callback
Returns:
old callback

Definition at line 174 of file socket.c.

References assertcodeptr.

00175 {
00176     NewSocketCallback cb_old = login_callback;
00177     login_callback = NULL == callback ? Socket_DefaultCallback : callback;
00178     assertcodeptr(login_callback);
00179     return cb_old;
00180 }

NewSocketCallback SocketsSetUser ( NewSocketCallback  callback  ) 

Set a callback handler for new user socket creation.

Parameters:
callback Callback function, or NULL for no callback
Returns:
old callback

Definition at line 187 of file socket.c.

References assertcodeptr.

00188 {
00189     NewSocketCallback cb_old = user_callback;
00190     user_callback = NULL == callback ? Socket_DefaultCallback : callback;
00191     assertcodeptr(user_callback);
00192     return cb_old;
00193 }


Generated on Fri Jan 2 15:28:36 2009 for Squat by  doxygen 1.5.6