📄 tcp.h
字号:
Read: As for write, but gets a ECONNRESET.
The following macros check for various non-fatal/recoverable error
conditions, in the future we may want to address some of the others listed
above as well. A restartable error is a local error for which we can
retry the call, a recoverable error is a remote error for which we would
need to re-establish the connection. Note that any version of Winsock
newer than the 16-bit ones shouldn't give us an EINPROGRESS, however some
early stacks would still give this on occasions such as when another
thread was doing (blocking) name resolution, and even with the very latest
versions this is still something that can cause problems for other
threads */
#ifdef __WINDOWS__
#define isRecoverableError( status ) ( ( status ) == WSAECONNRESET )
#define isRestartableError() ( WSAGetLastError() == WSAEWOULDBLOCK || \
WSAGetLastError() == WSAEINPROGRESS )
#define isTimeoutError() ( WSAGetLastError() == WSAETIMEDOUT )
#else
#define isRecoverableError( status ) ( ( status ) == ECONNRESET )
#define isRestartableError() ( errno == EINTR || errno == EAGAIN )
#define isTimeoutError() ( errno == ETIMEDOUT )
#endif /* OS-specific status codes */
/****************************************************************************
* *
* Resolver Defines *
* *
****************************************************************************/
/* BeOS with the BONE network stack has just enough IPv6 defines present to
be awkward, so we temporarily re-enable IPv6 and then use a BONE-specific
subset of IPv6 defines further on */
#if defined( __BEOS__ ) && defined( BONE_VERSION )
#define IPv6
#endif /* BeOS with BONE */
/* IPv6 emulation functions used to provide a single consistent interface */
#ifndef IPv6
/* The addrinfo struct used by getaddrinfo() */
struct addrinfo {
int ai_flags; /* AI_PASSIVE, NI_NUMERICHOST */
int ai_family; /* PF_INET */
int ai_socktype; /* SOCK_STREAM */
int ai_protocol; /* IPPROTO_TCP */
size_t ai_addrlen; /* Length of ai_addr */
char *ai_canonname; /* CNAME for nodename */
struct sockaddr *ai_addr; /* IPv4 or IPv6 sockaddr */
struct addrinfo *ai_next; /* Next addrinfo structure list */
};
/* The generic sockaddr struct used to reserve storage for protocol-
specific sockaddr structs. This isn't quite right but since all
we're using it for is to reserve storage (we never actually look
inside it) it's OK to use here */
typedef char SOCKADDR_STORAGE[ 128 ];
/* getaddrinfo() flags and values */
#define AI_PASSIVE 0x1 /* Flag for hints are for getaddrinfo() */
/* getnameinfo() flags and values. We have to use slightly different
values for these under Windows because Windows uses different values
for these than anyone else, and even if we're not on an explicitly
IPv6-enabled system we could still end up dynamically pulling in the
required libraries, so we need to ensure that we're using the same flag
values that Windows does */
#ifdef __WINDOWS__
#define NI_NUMERICHOST 0x2 /* Return numeric form of host addr.*/
#define NI_NUMERICSERV 0x8 /* Return numeric form of host port */
#else
#define NI_NUMERICHOST 0x1 /* Return numeric form of host addr.*/
#define NI_NUMERICSERV 0x2 /* Return numeric form of host port */
#endif /* __WINDOWS__ */
/* If there's no getaddrinfo() available and we're not using dynamic
linking, use an emulation of the function */
#ifndef __WINDOWS__
#define getaddrinfo my_getaddrinfo
#define freeaddrinfo my_freeaddrinfo
#define getnameinfo my_getnameinfo
static int my_getaddrinfo( const char *nodename, const char *servname,
const struct addrinfo *hints,
struct addrinfo **res );
static void my_freeaddrinfo( struct addrinfo *ai );
static int my_getnameinfo( const struct sockaddr *sa, SIZE_TYPE salen,
char *node, SIZE_TYPE nodelen,
char *service, SIZE_TYPE servicelen,
int flags );
/* Windows uses the Pascal calling convention for these functions, we
hide this behind a define that becomes a no-op on non-Windows
systems */
#define SOCKET_API
#endif /* __WINDOWS__ */
#endif /* IPv6 */
/* A subset of the above for BeOS with the BONE network stack. See the
full IPv6 version above for descriptions of the entries */
#if defined( __BEOS__ ) && defined( BONE_VERSION )
#undef IPv6 /* We really don't do IPv6 */
typedef char SOCKADDR_STORAGE[ 128 ];
#define getaddrinfo my_getaddrinfo
#define freeaddrinfo my_freeaddrinfo
#define getnameinfo my_getnameinfo
static int my_getaddrinfo( const char *nodename, const char *servname,
const struct addrinfo *hints,
struct addrinfo **res );
static void my_freeaddrinfo( struct addrinfo *ai );
static int my_getnameinfo( const struct sockaddr *sa, SIZE_TYPE salen,
char *node, SIZE_TYPE nodelen,
char *service, SIZE_TYPE servicelen,
int flags );
#endif /* BeOS with BONE */
/* Values defined in some environments but not in others. T_SRV and
NS_SRVFIXEDSZ are used for DNS SRV lookups. Newer versions of bind use a
ns_t_srv enum for T_SRV but since we can't autodetect this via the
preprocessor we always define T_SRV ourselves */
#ifndef T_SRV
#define T_SRV 33
#endif /* !T_SRV */
#ifndef NS_SRVFIXEDSZ
#define NS_SRVFIXEDSZ ( NS_RRFIXEDSZ + 6 )
#endif /* !NS_SRVFIXEDSZ */
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#endif /* !AI_ADDRCONFIG */
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif /* !AI_NUMERICSERV */
/* gethostbyname is a problem function because the standard version is non-
thread-safe due to the use of static internal storage to contain the
returned host info. Some OSes (Windows, PHUX >= 11.0, OSF/1 >= 4.0,
Aches >= 4.3) don't have a problem with this because they use thread
local storage, but others either require the use of nonstandard _r
variants or simply don't handle it at all. To make it even more
entertaining, there are at least three different variations of the _r
form:
Linux (and glibc systems in general, but not BeOS with BONE):
int gethostbyname_r( const char *name, struct hostent *result_buf,
char *buf, size_t buflen, struct hostent **result,
int *h_errnop);
Slowaris >= 2.5.1, IRIX >= 6.5, QNX:
struct hostent *gethostbyname_r( const char *name,
struct hostent *result, char *buffer,
int buflen, int *h_errnop );
OSF/1, Aches (deprecated, see above):
int gethostbyname_r( const char *name, struct hostent *hptr,
struct hostent_data *hdptr );
To work around this mess, we define macros for thread-safe versions of
gethostbyname that can be retargeted to the appropriate function as
required */
#if defined( USE_THREADS ) && defined( __GLIBC__ ) && ( __GLIBC__ >= 2 ) && \
( !defined( __BEOS__ ) || !defined( BONE_VERSION ) )
#define gethostbyname_vars() \
char hostBuf[ 4096 ]; \
struct hostent hostEnt;
#define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \
if( gethostbyname_r( hostName, &hostEnt, hostBuf, 4096, &hostEntPtr, &hostErrno ) < 0 ) \
hostEntPtr = NULL
#elif defined( USE_THREADS ) && \
( ( defined( sun ) && OSVERSION > 4 ) || \
( defined( __sgi ) && OSVERSION >= 6 ) || defined( __QNX__ ) )
#define gethostbyname_vars() \
char hostBuf[ 4096 ]; \
struct hostent hostEnt;
#define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \
hostEntPtr = gethostbyname_r( hostName, &hostEnt, hostBuf, 4096, &hostErrno )
#else
#define gethostbyname_vars()
#define gethostbyname_threadsafe( hostName, hostEntPtr, hostErrno ) \
hostEntPtr = gethostbyname( hostName ); \
hostErrno = h_errno;
#endif /* Various gethostbyname variants */
/****************************************************************************
* *
* Non-blocking I/O Defines *
* *
****************************************************************************/
/* The traditional way to set a descriptor to nonblocking mode was an
ioctl with FIONBIO, however Posix prefers the O_NONBLOCK flag for fcntl
so we use this if it's available.
Unfortunately if we haven't got the fcntl() interface available there's
no way to determine whether a socket is non-blocking or not, which is
particularly troublesome for Windows where we need to ensure that the
socket is blocking in order to avoid Winsock bugs with nonblocking
sockets. Although WSAIoctl() would appear to provide an interface for
obtaining the nonblocking status, it doesn't provide any more
functionality than ioctlsocket(), returning an error if we try and read
the FIONBIO value.
The best that we can do in this case is to force the socket to be
blocking, which somewhat voids the guarantee that we leave the socket as
we found it, but OTOH if we've been passed an invalid socket the caller
will have to abort and fix the problem anyway, so changing the socket
state isn't such a big deal.
BeOS is even worse, not only is there no way to determine whether a
socket is blocking or not, it'll also quite happily perform socket
functions like setsockopt() on a file descriptor (for example stdout),
so we can't even use this as a check for socket validity as it is under
other OSes. Because of this the check socket function will always
indicate that something vaguely handle-like is a valid socket */
#if defined( F_GETFL ) && defined( F_SETFL ) && defined( O_NONBLOCK )
#define getSocketNonblockingStatus( socket, value ) \
{ \
value = fcntl( socket, F_GETFL, 0 ); \
if( !isSocketError( value ) ) \
value = ( value & O_NONBLOCK ) ? TRUE : FALSE; \
}
#define setSocketNonblocking( socket ) \
{ \
const int flags = fcntl( socket, F_GETFL, 0 ); \
fcntl( socket, F_SETFL, flags | O_NONBLOCK ); \
}
#define setSocketBlocking( socket ) \
{ \
const int flags = fcntl( socket, F_GETFL, 0 ); \
fcntl( socket, F_SETFL, flags & ~O_NONBLOCK ); \
}
#elif defined( FIONBIO )
#define getSocketNonblockingStatus( socket, value ) \
{ \
long nonBlock = FALSE; \
value = ioctlsocket( socket, FIONBIO, &nonBlock ); \
if( !isSocketError( value ) ) \
value = 0; \
}
#define setSocketNonblocking( socket ) \
{ \
long nonBlock = TRUE; \
ioctlsocket( socket, FIONBIO, &nonBlock ); \
}
#define setSocketBlocking( socket ) \
{ \
long nonBlock = FALSE; \
ioctlsocket( socket, FIONBIO, &nonBlock ); \
}
#elif defined( __AMX__ ) || defined( __BEOS__ )
#define getSocketNonblockingStatus( socket, value ) \
{ \
int nonBlock = FALSE; \
setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \
}
#define setSocketNonblocking( socket ) \
{ \
int nonBlock = TRUE; \
setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \
}
#define setSocketBlocking( socket ) \
{ \
int nonBlock = FALSE; \
setsockopt( socket, SOL_SOCKET, SO_NONBLOCK, &nonBlock, sizeof( int ) ); \
}
#elif defined( __SYMBIAN32__ )
/* Symbian OS doesn't support nonblocking I/O */
#define getSocketNonblockingStatus( socket, value ) value = 0
#define setSocketNonblocking( socket )
#define setSocketBlocking( socket )
#else
#error Need to create macros to handle nonblocking I/O
#endif /* Handling of blocking/nonblocking sockets */
/****************************************************************************
* *
* Misc.Functions and Defines *
* *
****************************************************************************/
/* DNS dynamic-binding init/shutdown functions */
#ifdef __WINDOWS__
int initDNS( INSTANCE_HANDLE hTCP, INSTANCE_HANDLE hAddr );
void endDNS( INSTANCE_HANDLE hTCP );
#endif /* __WINDOWS__ */
/* Prototypes for functions in net_dns.c */
int getAddressInfo( STREAM *stream, struct addrinfo **addrInfoPtrPtr,
const char *name, const int port,
const BOOLEAN isServer );
void freeAddressInfo( struct addrinfo *addrInfoPtr );
void getNameInfo( const struct sockaddr *sockAddr, char *address,
const int addressMaxLen, int *port );
/* Prototypes for functions in net_tcp.c */
int getSocketError( STREAM *stream, const int status );
int setSocketError( STREAM *stream, const char *errorMessage,
const int status, const BOOLEAN isFatal );
int getHostError( STREAM *stream, const int status );
#endif /* USE_TCP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -