#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>
Go to the source code of this file.
Functions | |
uint32 | SocketID (const Socket *sock) |
Get a socket ID from its pointer. | |
Socket * | SocketLut (uint32 id) |
Get a socket pointer from its ID. | |
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. | |
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. | |
Socket * | Socket_Create_Client (const char *address) |
Socket * | Socket_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. | |
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. | |
Socket * | Socket_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. | |
Socket * | Socket_Create_Raw (Socket *sock) |
Initialize a RAW connection. | |
Socket * | Socket_Create (void) |
Allocate, initialize and register a new socket. | |
Socket * | Socket_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'. |
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.
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.
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 |
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 }
void Socket_Close | ( | Socket * | sock | ) |
Mark a socket for cleanup.
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 }
Socket* Socket_Create | ( | void | ) |
Allocate, initialize and register a new socket.
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 }
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 }
Socket* Socket_Create_Listener | ( | const char * | label, | |
const char * | address | |||
) |
Make a listener. You'll need to make at least one to accept connections.
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 }
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.
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 }
Initialize a RAW connection.
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 }
Initialize a user connection from Login.
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 }
Allow default buffering to be modified for special cases.
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 |
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 }
void Socket_LogError | ( | Socket * | sock, | |
unsigned | mask, | |||
const char * | fmt, | |||
... | ||||
) |
Log information about a naughty socket after an error is detected.
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 }
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.
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 }
bool Socket_Write | ( | Socket * | sock, | |
const void * | buff, | |||
size_t | count | |||
) |
Write some raw data into the output queue.
sock | Instance to work with | |
buff | Pointer to data to write | |
count | Length of data to write |
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 }
bool Socket_Write_Serial | ( | Socket * | sock, | |
const ctl_serial * | serial | |||
) |
Write a fully formed and filled-in serial packet into output buffer; ignores 'curr'.
sock | Instance to work with | |
serial | Returns a serial packet to work with if function retuens true |
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 }
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.
label | Name of socket |
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.
sock | Instance to look up |
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.
id | Socket ID to lookup |
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.
ms | Milliseconds to time-out waiting for activity |
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 }
bool SocketsInit | ( | size_t | input, | |
size_t | output, | |||
size_t | heap | |||
) |
Start up sockets connections.
input | Size of socket input buffers | |
output | Size of socket output buffers (larger than input) | |
heap | Size of scratch heap attached to each socket |
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 }
const char* SocketsLastErrorString | ( | void | ) |
Retrieve the system error message for the last-error code.
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.
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 }
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
szAddress | String containing ip:port (:port is REQUIRED) |
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 }
MLCallback SocketsSetFlashPolicy | ( | MLCallback | callback | ) |
Setup what to tell Flash clients that they can connect.
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.
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.
callback | Callback function, or NULL for no 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.
callback | Callback function, or NULL for no 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 }