📄 net_tcp.c
字号:
#endif /* IPv6 */
/* Many older Unixen don't define the in_*_t's */
#if defined( __APPLE__ ) || defined( __BEOS__ ) || \
defined( __bsdi__ ) || defined( __CYGWIN__ ) || \
defined( __FreeBSD__ ) || defined( __hpux ) || \
defined( __linux__ ) || defined( __OpenBSD__ ) || \
( defined( sun ) && OSVERSION <= 5 )
#ifndef in_addr_t
#define in_addr_t u_long
#define in_port_t u_short
#endif /* in_addr_t */
#endif /* Older Unixen without in_*_t's */
/* Some systems use int for size parameters to socket functions and some
use size_t (and just to be difficult some use socklen_t, which we use
if we can get it). The following is required to distinguish the
different ones to avoid compiler warnings on systems that insist on
having it one particular way */
#if defined( socklen_t ) || defined( __socklen_t_defined )
#define SIZE_TYPE socklen_t
#elif defined( __APPLE__ ) || defined( __BEOS__ )
#define SIZE_TYPE int
#else
#define SIZE_TYPE size_t
#endif /* Different size types */
/* PHUX generally doesn't define h_errno, we have to be careful here since
later versions may use macros to get around threading issues so we check
for the existence of a macro with the given name before defining our own
version */
#if defined( __hpux ) && !defined( h_errno )
/* Usually missing from netdb.h */
extern int h_errno;
#endif /* PHUX && !h_errno */
/* The Bind namespace (via nameser.h) was cleaned up between the old
(widely-used) Bind4 API and the newer (little-used) Bind8/9 one. In
order to handle both, we use the newer definitions, but map them back
to the Bind4 forms if required. The only thing this doesn't give us
is the HEADER struct, which seems to have no equivalent in Bind8/9 */
#ifndef NS_PACKETSZ
#define NS_PACKETSZ PACKETSZ
#define NS_HFIXEDSZ HFIXEDSZ
#define NS_RRFIXEDSZ RRFIXEDSZ
#define NS_QFIXEDSZ QFIXEDSZ
#endif /* Bind8 names */
/* Windows and BeOS use a distinct socket handle type and require the use
of separate closesocket() and ioctlsocket() functions because socket
handles aren't the same as standard Windows/BeOS handles */
#ifdef SOCKET
/* MP-RAS has already defined this */
#undef SOCKET
#endif /* SOCKET */
#define SOCKET int
#if !defined( __BEOS__ ) || \
( defined( __BEOS__ ) && defined( BONE_VERSION ) )
#define closesocket close
#endif /* BeOS without BONE */
#define ioctlsocket ioctl
#endif /* OS-specific portability 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 );
#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. MSG_NOSIGNAL is
used to avoid SIGPIPEs on writes if the other side closes the connection,
if it's not implemented in this environment we just clear the write flag.
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 defien T_SRV ourselves */
#ifndef SHUT_WR
#define SHUT_WR 1
#endif /* SHUT_WR */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif /* MSG_NOSIGNAL */
#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 */
/* 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 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( __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 */
/* 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 */
/* For some connections that involve long-running sessions we need to be
able to gracefully recover from local errors such as an interrupted system
call, and remote errors such as the remote process or host crashing and
restarting, which we can do by closing and re-opening the connection. The
various situations are:
Local error:
Retry the call on EAGAIN or EINTR
Process crashes and restarts:
Write: Remote host sends a RST in response to an attempt to continue
a TCP session that it doesn't remember, which is reported
locally as the dreaded (if you ssh or NNTP to remote hosts a
lot) connection reset by peer error.
Read: Remote host sends a FIN, we read 0 bytes.
Network problem:
Write: Data is resent, if a read is pending it returns ETIMEDOUT,
otherwise write returns EPIPE or SIGPIPE (although we try
and avoid the latter using MSG_NOSIGNAL). Some
implementations may also return ENETUNREACH or EHOSTUNREACH
if they receive the right ICMP information.
Read: See above, without the write sematics.
Host crashes and restarts:
Write: Looks like a network outage until the host is restarted, then
gets an EPIPE/SIGPIPE.
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -