?? winsock.cxx
字號:
/*
* winsock.cxx
*
* WINSOCK implementation of Berkley sockets.
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: winsock.cxx,v $
* Revision 1.45 2001/09/10 02:51:23 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.44 2001/09/06 02:30:31 robertj
* Fixed mismatched declarations, thanks Vjacheslav Andrejev
*
* Revision 1.43 2001/03/20 06:57:14 robertj
* os_accept() function changed due to unix changes re unblocking threads.
*
* Revision 1.42 2001/01/24 06:46:45 yurik
* Windows CE port-related changes
*
* Revision 1.41 1998/11/30 04:50:19 robertj
* New directory structure
*
* Revision 1.40 1998/11/14 06:31:15 robertj
* Changed semantics of os_sendto to return TRUE if ANY bytes are sent.
*
* Revision 1.39 1998/09/24 03:31:02 robertj
* Added open software license.
*
* Revision 1.38 1998/08/28 14:09:45 robertj
* Fixed bug in Write() that caused endlesss loops, introduced in previous version.
*
* Revision 1.37 1998/08/21 05:27:31 robertj
* Fixed bug where write streams out to non-stream socket.
*
* Revision 1.36 1998/08/06 00:55:21 robertj
* Fixed conversion of text to IPX address, was swapping nibbles.
*
* Revision 1.35 1998/05/08 11:52:03 robertj
* Added workaround for winsock bug where getpeername() doesn't work immediately after connect().
*
* Revision 1.34 1998/05/07 05:21:04 robertj
* Fixed DNS lookup so only works around bug in old Win95 and not OSR2
*
* Revision 1.33 1998/01/26 01:00:06 robertj
* Added timeout to os_connect().
* Fixed problems with NT version of IsLocalHost().
*
* Revision 1.32 1997/12/18 05:05:27 robertj
* Moved IsLocalHost() to platform dependent code.
*
* Revision 1.31 1997/12/11 10:41:55 robertj
* Added DWORD operator for IP addresses.
*
* Revision 1.30 1997/01/03 04:37:11 robertj
* Fixed '95 problem with send timeouts.
*
* Revision 1.29 1996/12/05 11:51:50 craigs
* Fixed Win95 recvfrom timeout problem
*
* Revision 1.28 1996/11/10 21:04:56 robertj
* Fixed bug in not flushing stream on close of socket.
*
* Revision 1.27 1996/10/31 12:39:30 robertj
* Fixed bug in byte order of port numbers in IPX protocol.
*
* Revision 1.26 1996/10/26 01:43:18 robertj
* Removed translation of IP address to host order DWORD. Is ALWAYS net order.
*
* Revision 1.25 1996/10/08 13:03:09 robertj
* More IPX support.
*
* Revision 1.24 1996/09/14 13:09:47 robertj
* Major upgrade:
* rearranged sockets to help support IPX.
* added indirect channel class and moved all protocols to descend from it,
* separating the protocol from the low level byte transport.
*
* Revision 1.23 1996/08/08 10:06:07 robertj
* Fixed incorrect value in write, causes incorrect output if send is split.
*
* Revision 1.22 1996/07/27 04:03:29 robertj
* Created static version of ConvertOSError().
*
* Revision 1.21 1996/06/01 04:19:34 robertj
* Added flush to PSocket destructor as needs to use Write() at that level.
*
* Revision 1.20 1996/05/15 10:23:08 robertj
* Changed millisecond access functions to get 64 bit integer.
* Added timeout to accept function.
* Added ICMP protocol socket, getting common ancestor to UDP.
*
* Revision 1.19 1996/04/29 12:22:26 robertj
* Fixed detection of infinite timeout.
*
* Revision 1.18 1996/04/17 12:09:52 robertj
* Fixed bug in detecting infinte timeout.
*
* Revision 1.17 1996/04/12 09:45:06 robertj
* Rewrite of PSocket::Read() to avoid "Connection Reset" errors caused by SO_RCVTIMEO
*
* Revision 1.17 1996/04/10 12:15:11 robertj
* Rewrite of PSocket::Read() to avoid "Connection Reset" errors caused by SO_RCVTIMEO.
*
* Revision 1.16 1996/04/05 01:42:28 robertj
* Assured PSocket::Write always writes the number of bytes specified.
*
* Revision 1.15 1996/03/31 09:11:06 robertj
* Fixed major performance problem in timeout read/write to sockets.
*
* Revision 1.14 1996/03/10 13:16:25 robertj
* Fixed ioctl of closed socket.
*
* Revision 1.13 1996/03/04 12:41:02 robertj
* Fixed bug in leaving socket in non-blocking mode.
* Changed _Close to os_close to be consistent.
*
* Revision 1.12 1996/02/25 11:23:40 robertj
* Fixed bug in Read for when a timeout occurs on select, not returning error code.
*
* Revision 1.11 1996/02/25 03:13:12 robertj
* Moved some socket functions to platform dependent code.
*
* Revision 1.10 1996/02/19 13:52:39 robertj
* Added SO_LINGER option to socket to stop data loss on close.
* Fixed error reporting for winsock classes.
*
* Revision 1.9 1996/02/15 14:53:36 robertj
* Added Select() function to PSocket.
*
* Revision 1.8 1996/01/23 13:25:48 robertj
* Moved Accept from platform independent code.
*
* Revision 1.7 1996/01/02 12:57:17 robertj
* Unix compatibility.
*
* Revision 1.6 1995/12/10 12:06:00 robertj
* Numerous fixes for sockets.
*
* Revision 1.5 1995/06/17 00:59:49 robertj
* Fixed bug with stream being flushed on read/write.
*
* Revision 1.4 1995/06/04 12:49:51 robertj
* Fixed bugs in socket read and write function return status.
* Fixed bug in socket close setting object state to "closed".
*
* Revision 1.3 1995/03/12 05:00:10 robertj
* Re-organisation of DOS/WIN16 and WIN32 platforms to maximise common code.
* Used built-in equate for WIN32 API (_WIN32).
*
* Revision 1.2 1995/01/03 09:43:27 robertj
* Moved out of band stuff to common.
*
* Revision 1.1 1994/10/30 12:06:56 robertj
* Initial revision
*/
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <nspapi.h>
#include <svcguid.h>
//////////////////////////////////////////////////////////////////////////////
// PWinSock
PWinSock::PWinSock()
{
WSADATA winsock;
PAssert(WSAStartup(0x101, &winsock) == 0, POperatingSystemError);
PAssert(LOBYTE(winsock.wVersion) == 1 &&
HIBYTE(winsock.wVersion) == 1, POperatingSystemError);
}
PWinSock::~PWinSock()
{
WSACleanup();
}
BOOL PWinSock::OpenSocket()
{
return FALSE;
}
const char * PWinSock::GetProtocolName() const
{
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
// PSocket
PSocket::~PSocket()
{
Close();
}
BOOL PSocket::Read(void * buf, PINDEX len)
{
flush();
lastReadCount = 0;
if (len == 0)
return SetErrorValues(BadParameter, EINVAL, LastReadError);
os_recvfrom((char *)buf, len, 0, NULL, NULL);
return lastReadCount > 0;
}
BOOL PSocket::Write(const void * buf, PINDEX len)
{
flush();
return os_sendto(buf, len, 0, NULL, 0) && lastWriteCount >= len;
}
BOOL PSocket::Close()
{
if (!IsOpen())
return FALSE;
flush();
return ConvertOSError(os_close());
}
int PSocket::os_close()
{
int err = closesocket(os_handle);
os_handle = -1;
return err;
}
int PSocket::os_socket(int af, int type, int proto)
{
return ::socket(af, type, proto);
}
class fd_set_class : public fd_set {
public:
fd_set_class(SOCKET fd)
{
#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif
FD_ZERO(this);
FD_SET(fd, this);
#ifdef _MSC_VER
#pragma warning(default:4127)
#endif
}
BOOL IsPresent(int h) const
{ return FD_ISSET(h, this); }
};
class timeval_class : public timeval {
public:
timeval_class(const PTimeInterval & time)
{
tv_usec = (long)(time.GetMilliSeconds()%1000)*1000;
tv_sec = time.GetSeconds();
}
};
int PSocket::os_connect(struct sockaddr * addr, PINDEX size)
{
if (readTimeout == PMaxTimeInterval)
return ::connect(os_handle, addr, size);
DWORD fionbio = 1;
if (::ioctlsocket(os_handle, FIONBIO, &fionbio) == SOCKET_ERROR)
return SOCKET_ERROR;
fionbio = 0;
if (::connect(os_handle, addr, size) != SOCKET_ERROR)
return ::ioctlsocket(os_handle, FIONBIO, &fionbio);
DWORD err = GetLastError();
if (err != WSAEWOULDBLOCK) {
::ioctlsocket(os_handle, FIONBIO, &fionbio);
SetLastError(err);
return SOCKET_ERROR;
}
fd_set_class writefds = os_handle;
timeval_class tv = readTimeout;
switch (select(0, NULL, &writefds, NULL, &tv)) {
case 1 :
err = 0;
break;
case 0 :
err = WSAETIMEDOUT;
break;
default :
err = GetLastError();
}
if (::ioctlsocket(os_handle, FIONBIO, &fionbio) == SOCKET_ERROR) {
if (err == 0)
err = GetLastError();
}
// The following is to avoid a bug in Win32 sockets. The getpeername() function doesn't
// work for some period of time after a connect, saying it is not connected yet!
for (PINDEX failsafe = 0; failsafe < 1000; failsafe++) {
sockaddr_in address;
int sz = sizeof(address);
if (::getpeername(os_handle, (struct sockaddr *)&address, &sz) == 0)
break;
::Sleep(0);
}
SetLastError(err);
return err == 0 ? 0 : SOCKET_ERROR;
}
int PSocket::os_accept(PSocket & listener, struct sockaddr * addr, int * size)
{
if (listener.GetReadTimeout() != PMaxTimeInterval) {
fd_set_class readfds = listener.GetHandle();
timeval_class tv = listener.GetReadTimeout();
switch (select(0, &readfds, NULL, NULL, &tv)) {
case 1 :
break;
case 0 :
SetLastError(WSAETIMEDOUT);
// Then return -1
default :
return -1;
}
}
return ::accept(listener.GetHandle(), addr, size);
}
BOOL PSocket::os_recvfrom(void * buf,
PINDEX len,
int flags,
struct sockaddr * from,
PINDEX * fromlen)
{
lastReadCount = 0;
if (readTimeout != PMaxTimeInterval) {
DWORD available;
if (!ConvertOSError(ioctlsocket(os_handle, FIONREAD, &available), LastReadError))
return FALSE;
if (available == 0) {
fd_set_class readfds = os_handle;
timeval_class tv = readTimeout;
int selval = ::select(0, &readfds, NULL, NULL, &tv);
if (!ConvertOSError(selval, LastReadError))
return FALSE;
if (selval == 0)
return SetErrorValues(Timeout, EAGAIN, LastReadError);
if (!ConvertOSError(ioctlsocket(os_handle, FIONREAD, &available), LastReadError))
return FALSE;
}
if (available > 0 && len > (PINDEX)available)
len = available;
}
int recvResult = ::recvfrom(os_handle, (char *)buf, len, flags, from, fromlen);
if (!ConvertOSError(recvResult, LastReadError))
return FALSE;
lastReadCount = recvResult;
return TRUE;
}
BOOL PSocket::os_sendto(const void * buf,
PINDEX len,
int flags,
struct sockaddr * to,
PINDEX tolen)
{
lastWriteCount = 0;
if (writeTimeout != PMaxTimeInterval) {
fd_set_class writefds = os_handle;
timeval_class tv = writeTimeout;
int selval = ::select(0, NULL, &writefds, NULL, &tv);
if (selval < 0)
return FALSE;
if (selval == 0) {
#ifndef _WIN32_WCE
errno = EAGAIN;
#else
SetLastError(EAGAIN);
#endif
return FALSE;
}
}
int sendResult = ::sendto(os_handle, (const char *)buf, len, flags, to, tolen);
if (!ConvertOSError(sendResult, LastWriteError))
return FALSE;
if (sendResult == 0)
return FALSE;
lastWriteCount = sendResult;
return TRUE;
}
int PSocket::os_select(int maxfds,
fd_set & readfds,
fd_set & writefds,
fd_set & exceptfds,
const PIntArray &,
const PTimeInterval & timeout)
{
struct timeval tv_buf;
struct timeval * tv = NULL;
if (timeout != PMaxTimeInterval) {
tv = &tv_buf;
tv->tv_usec = (long)(timeout.GetMilliSeconds()%1000)*1000;
tv->tv_sec = timeout.GetSeconds();
}
return select(maxfds, &readfds, &writefds, &exceptfds, tv);
}
BOOL PSocket::ConvertOSError(int status, ErrorGroup group)
{
Errors lastError;
int osError;
BOOL ok = ConvertOSError(status, lastError, osError);
SetErrorValues(lastError, osError, group);
return ok;
}
BOOL PSocket::ConvertOSError(int status, Errors & lastError, int & osError)
{
if (status >= 0) {
lastError = NoError;
osError = 0;
return TRUE;
}
#ifdef _WIN32
SetLastError(WSAGetLastError());
return PChannel::ConvertOSError(-2, lastError, osError);
#else
osError = WSAGetLastError();
switch (osError) {
case 0 :
lastError = NoError;
return TRUE;
case WSAEWOULDBLOCK :
lastError = Timeout;
break;
default :
osError |= PWIN32ErrorFlag;
lastError = Miscellaneous;
}
return FALSE;
#endif
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -