Program 1, while typical of the way in which TLS and DllMain
are combined to create thread-safe libraries, has two major weaknesses
noted in the comments in the previous section. First, the state is
associated with the thread rather than with the socket, so a given
thread can process only one socket at a time. Second, there is the
resource leak risk mentioned in the last bullet above.
An
effective alternative approach to thread-safe library functions is to
create a handle-like structure that is passed to every function call.
The state is then maintained in the structure. The application
explicitly manages the state, so you can manage multiple sockets in a
thread, and you can even use the sockets with fibers (there might be
one socket, or more, per fiber). Many UNIX and Linux applications use
this technique to create thread-safe C libraries; the main disadvantage
is that the functions require an additional parameter for the state
structure.
Program 2 modifies Program 1. Notice that DllMain
is not necessary, but there are two new functions to initialize and
free the state structure. The send and receive functions require only
minimal changes. An associated server, serverSKHA, is included in the Examples file and requires only slight changes in order to create and close the socket handle (HA denotes “handle”).
Program 2. SendReceiveSKHA: Thread-Safe DLL with a State Structure
/* SendReceiveSKHA.c -- multithreaded streaming socket. */ /* This is a modification of SendReceiveSKST.c to illustrate a */ /* different thread-safe library technique. */ /* State is preserved in a handle-like state structure rather than */ /* using TLS. This allows a thread to use several sockets at once. */ /* Messages are delimited by null characters ('\0'). */
#include "Everything.h" #include "ClientServer.h"/* Defines MESSAGEa. */
typedef struct SOCKET_HANDLE_T { /* Current socket state */ /* Contains "staticBuffLen" characters of residual data */ /* There may or may not be end-of-string (null) characters */ SOCKET sk; char staticBuff[MAX_RQRS_LEN]; LONG32 staticBuffLen; } SOCKET_HANDLE, * PSOCKET_HANDLE;
/* Functions to create and close "streaming socket handles" */ __declspec (dllexport) PVOID CreateCSSocketHandle (SOCKET s) { PVOID p; PSOCKET_HANDLE ps;
p = malloc (sizeof(SOCKET_HANDLE)); if (p == NULL) return NULL; ps = (PSOCKET_HANDLE)p; ps->sk = s; ps->staticBuffLen = 0; /* Initialize buffer state */ return p; }
__declspec (dllexport) BOOL CloseCSSocketHandle (PSOCKET_HANDLE psh) { if (psh == NULL) return FALSE; free (psh); return TRUE; }
__declspec(dllexport) BOOL ReceiveCSMessage (MESSAGE *pMsg, PSOCKET_HANDLE psh) /* Use PVOID so that calling program does not need to include the */ /* SOCKET_HANDLE definition. */ { /* TRUE return indicates an error or disconnect */ BOOL disconnect = FALSE; LONG32 nRemainRecv = 0, nXfer, k; /* Must be signed integers */ LPSTR pBuffer, message; CHAR tempBuff[MAX_RQRS_LEN+1]; SOCKET sd;
if (psh == NULL) return FALSE; sd = psh->sk;
/* This is all that's changed from SendReceiveSKST! */ message = pMsg->record; /* Read up to the null character, leaving residual data * in the static buffer */
for (k = 0; k < psh->staticBuffLen && psh->staticBuff[k] != '\0'; k++) { message[k] = psh->staticBuff[k]; } /* k is the number of characters transferred */ if (k < psh->staticBuffLen) { /* null found in buffer */ message[k] = '\0'; psh->staticBuffLen -= (k+1); /* Adjust buffer state */ memcpy (psh->staticBuff, &(psh->staticBuff[k+1]), psh->staticBuffLen); return TRUE; /* No socket input required */ }
/* The entire static buffer was transferred. No null found */ nRemainRecv = sizeof(tempBuff) - 1 - psh->staticBuffLen; pBuffer = message + psh->staticBuffLen; psh->staticBuffLen = 0;
while (nRemainRecv > 0 && !disconnect) { nXfer = recv (sd, tempBuff, nRemainRecv, 0); if (nXfer <= 0) { disconnect = TRUE; continue; }
nRemainRecv -=nXfer; /* Transfer to target message up to null, if any */ for (k = 0; k < nXfer && tempBuff[k] != '\0'; k++) { *pBuffer = tempBuff[k]; pBuffer++; } if (k >= nXfer) { /* null not found, read more */ nRemainRecv -= nXfer; } else { /* null has been found */ *pBuffer = '\0'; nRemainRecv = 0; memcpy (psh->staticBuff, &tempBuff[k+1], nXfer - k - 1); psh->staticBuffLen = nXfer -k - 1; } } return !disconnect;
}
__declspec(dllexport) BOOL SendCSMessage (MESSAGE *pMsg, PSOCKET_HANDLE psh) { /* Send the the request to the server on socket sd */ BOOL disconnect = FALSE; LONG32 nRemainSend, nXfer; LPSTR pBuffer; SOCKET sd;
if (psh == NULL || pMsg == NULL) return FALSE; sd = psh->sk;
pBuffer = pMsg->record; /* Ignore Win64 conversion warning. strlen is size_t */ nRemainSend = min(strlen (pBuffer) + 1, MAX_MESSAGE_LEN);
while (nRemainSend > 0 && !disconnect) { /* send does not guarantee that the entire message is sent */ nXfer = send (sd, pBuffer, nRemainSend, 0); if (nXfer <= 0) { disconnect = TRUE; } nRemainSend -=nXfer; pBuffer += nXfer; }
return !disconnect; }
|