📄 socket.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
// Name: socket.cpp
// Purpose: Socket handler classes
// Authors: Guilhem Lavaux, Guillermo Rodriguez Garcia
// Created: April 1997
// Copyright: (C) 1999-1997, Guilhem Lavaux
// (C) 2000-1999, Guillermo Rodriguez Garcia
// RCS_ID: $Id: socket.cpp,v 1.133.2.3 2006/03/30 15:27:59 KH Exp $
// License: see wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ==========================================================================
// Declarations
// ==========================================================================
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "socket.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_SOCKETS
#include "wx/app.h"
#include "wx/apptrait.h"
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/string.h"
#include "wx/timer.h"
#include "wx/utils.h"
#include "wx/module.h"
#include "wx/log.h"
#include "wx/intl.h"
#include "wx/event.h"
#include "wx/sckaddr.h"
#include "wx/socket.h"
#include "wx/datetime.h"
// DLL options compatibility check:
#include "wx/build.h"
WX_CHECK_BUILD_OPTIONS("wxNet")
// --------------------------------------------------------------------------
// macros and constants
// --------------------------------------------------------------------------
// discard buffer
#define MAX_DISCARD_SIZE (10 * 1024)
// what to do within waits: we have 2 cases: from the main thread itself we
// have to call wxYield() to let the events (including the GUI events and the
// low-level (not wxWidgets) events from GSocket) be processed. From another
// thread it is enough to just call wxThread::Yield() which will give away the
// rest of our time slice: the explanation is that the events will be processed
// by the main thread anyhow, without calling wxYield(), but we don't want to
// eat the CPU time uselessly while sitting in the loop waiting for the data
#if wxUSE_THREADS
#define PROCESS_EVENTS() \
{ \
if ( wxThread::IsMain() ) \
wxYield(); \
else \
wxThread::Yield(); \
}
#else // !wxUSE_THREADS
#define PROCESS_EVENTS() wxYield()
#endif // wxUSE_THREADS/!wxUSE_THREADS
#define wxTRACE_Socket _T("wxSocket")
// --------------------------------------------------------------------------
// wxWin macros
// --------------------------------------------------------------------------
IMPLEMENT_CLASS(wxSocketBase, wxObject)
IMPLEMENT_CLASS(wxSocketServer, wxSocketBase)
IMPLEMENT_CLASS(wxSocketClient, wxSocketBase)
IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase)
IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent)
// --------------------------------------------------------------------------
// private classes
// --------------------------------------------------------------------------
class wxSocketState : public wxObject
{
public:
wxSocketFlags m_flags;
wxSocketEventFlags m_eventmask;
bool m_notify;
void *m_clientData;
public:
wxSocketState() : wxObject() {}
DECLARE_NO_COPY_CLASS(wxSocketState)
};
// ==========================================================================
// wxSocketBase
// ==========================================================================
// --------------------------------------------------------------------------
// Initialization and shutdown
// --------------------------------------------------------------------------
// FIXME-MT: all this is MT-unsafe, of course, we should protect all accesses
// to m_countInit with a crit section
size_t wxSocketBase::m_countInit = 0;
bool wxSocketBase::IsInitialized()
{
return m_countInit > 0;
}
bool wxSocketBase::Initialize()
{
if ( !m_countInit++ )
{
/*
Details: Initialize() creates a hidden window as a sink for socket
events, such as 'read completed'. wxMSW has only one message loop
for the main thread. If Initialize is called in a secondary thread,
the socket window will be created for the secondary thread, but
since there is no message loop on this thread, it will never
receive events and all socket operations will time out.
BTW, the main thread must not be stopped using sleep or block
on a semaphore (a bad idea in any case) or socket operations
will time out.
On the Mac side, Initialize() stores a pointer to the CFRunLoop for
the main thread. Because secondary threads do not have run loops,
adding event notifications to the "Current" loop would have no
effect at all, events would never fire.
*/
wxASSERT_MSG( wxIsMainThread(),
wxT("Call wxSocketBase::Initialize() from the main thread first!"));
wxAppTraits *traits = wxAppConsole::GetInstance() ?
wxAppConsole::GetInstance()->GetTraits() : NULL;
GSocketGUIFunctionsTable *functions =
traits ? traits->GetSocketGUIFunctionsTable() : NULL;
GSocket_SetGUIFunctions(functions);
if ( !GSocket_Init() )
{
m_countInit--;
return false;
}
}
return true;
}
void wxSocketBase::Shutdown()
{
// we should be initialized
wxASSERT_MSG( m_countInit, _T("extra call to Shutdown()") );
if ( !--m_countInit )
{
GSocket_Cleanup();
}
}
// --------------------------------------------------------------------------
// Ctor and dtor
// --------------------------------------------------------------------------
void wxSocketBase::Init()
{
m_socket = NULL;
m_type = wxSOCKET_UNINIT;
// state
m_flags = 0;
m_connected =
m_establishing =
m_reading =
m_writing =
m_error = false;
m_lcount = 0;
m_timeout = 600;
m_beingDeleted = false;
// pushback buffer
m_unread = NULL;
m_unrd_size = 0;
m_unrd_cur = 0;
// events
m_id = wxID_ANY;
m_handler = NULL;
m_clientData = NULL;
m_notify = false;
m_eventmask = 0;
if ( !IsInitialized() )
{
// this Initialize() will be undone by wxSocketModule::OnExit(), all the
// other calls to it should be matched by a call to Shutdown()
Initialize();
}
}
wxSocketBase::wxSocketBase()
{
Init();
}
wxSocketBase::wxSocketBase(wxSocketFlags flags, wxSocketType type)
{
Init();
m_flags = flags;
m_type = type;
}
wxSocketBase::~wxSocketBase()
{
// Just in case the app called Destroy() *and* then deleted
// the socket immediately: don't leave dangling pointers.
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits )
traits->RemoveFromPendingDelete(this);
// Shutdown and close the socket
if (!m_beingDeleted)
Close();
// Destroy the GSocket object
if (m_socket)
delete m_socket;
// Free the pushback buffer
if (m_unread)
free(m_unread);
}
bool wxSocketBase::Destroy()
{
// Delayed destruction: the socket will be deleted during the next
// idle loop iteration. This ensures that all pending events have
// been processed.
m_beingDeleted = true;
// Shutdown and close the socket
Close();
// Supress events from now on
Notify(false);
// schedule this object for deletion
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( traits )
{
// let the traits object decide what to do with us
traits->ScheduleForDestroy(this);
}
else // no app or no traits
{
// in wxBase we might have no app object at all, don't leak memory
delete this;
}
return true;
}
// --------------------------------------------------------------------------
// Basic IO calls
// --------------------------------------------------------------------------
// The following IO operations update m_error and m_lcount:
// {Read, Write, ReadMsg, WriteMsg, Peek, Unread, Discard}
//
// TODO: Should Connect, Accept and AcceptWith update m_error?
bool wxSocketBase::Close()
{
// Interrupt pending waits
InterruptWait();
if (m_socket)
{
// Disable callbacks
m_socket->UnsetCallback(GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG |
GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG);
// Shutdown the connection
m_socket->Shutdown();
}
m_connected = false;
m_establishing = false;
return true;
}
wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes)
{
// Mask read events
m_reading = true;
m_lcount = _Read(buffer, nbytes);
// If in wxSOCKET_WAITALL mode, all bytes should have been read.
if (m_flags & wxSOCKET_WAITALL)
m_error = (m_lcount != nbytes);
else
m_error = (m_lcount == 0);
// Allow read events from now on
m_reading = false;
return *this;
}
wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes)
{
int total;
// Try the pushback buffer first
total = GetPushback(buffer, nbytes, false);
nbytes -= total;
buffer = (char *)buffer + total;
// Return now in one of the following cases:
// - the socket is invalid,
// - we got all the data,
// - we got *some* data and we are not using wxSOCKET_WAITALL.
if ( !m_socket ||
!nbytes ||
((total != 0) && !(m_flags & wxSOCKET_WAITALL)) )
return total;
// Possible combinations (they are checked in this order)
// wxSOCKET_NOWAIT
// wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK)
// wxSOCKET_BLOCK
// wxSOCKET_NONE
//
int ret;
if (m_flags & wxSOCKET_NOWAIT)
{
m_socket->SetNonBlocking(1);
ret = m_socket->Read((char *)buffer, nbytes);
m_socket->SetNonBlocking(0);
if (ret > 0)
total += ret;
}
else
{
bool more = true;
while (more)
{
if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForRead() )
break;
ret = m_socket->Read((char *)buffer, nbytes);
if (ret > 0)
{
total += ret;
nbytes -= ret;
buffer = (char *)buffer + ret;
}
// If we got here and wxSOCKET_WAITALL is not set, we can leave
// now. Otherwise, wait until we recv all the data or until there
// is an error.
//
more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL));
}
}
return total;
}
wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes)
{
wxUint32 len, len2, sig, total;
bool error;
int old_flags;
struct
{
unsigned char sig[4];
unsigned char len[4];
} msg;
// Mask read events
m_reading = true;
total = 0;
error = true;
old_flags = m_flags;
SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL);
if (_Read(&msg, sizeof(msg)) != sizeof(msg))
goto exit;
sig = (wxUint32)msg.sig[0];
sig |= (wxUint32)(msg.sig[1] << 8);
sig |= (wxUint32)(msg.sig[2] << 16);
sig |= (wxUint32)(msg.sig[3] << 24);
if (sig != 0xfeeddead)
{
wxLogWarning(_("wxSocket: invalid signature in ReadMsg."));
goto exit;
}
len = (wxUint32)msg.len[0];
len |= (wxUint32)(msg.len[1] << 8);
len |= (wxUint32)(msg.len[2] << 16);
len |= (wxUint32)(msg.len[3] << 24);
if (len > nbytes)
{
len2 = len - nbytes;
len = nbytes;
}
else
len2 = 0;
// Don't attemp to read if the msg was zero bytes long.
if (len)
{
total = _Read(buffer, len);
if (total != len)
goto exit;
}
if (len2)
{
char *discard_buffer = new char[MAX_DISCARD_SIZE];
long discard_len;
// NOTE: discarded bytes don't add to m_lcount.
do
{
discard_len = ((len2 > MAX_DISCARD_SIZE)? MAX_DISCARD_SIZE : len2);
discard_len = _Read(discard_buffer, (wxUint32)discard_len);
len2 -= (wxUint32)discard_len;
}
while ((discard_len > 0) && len2);
delete [] discard_buffer;
if (len2 != 0)
goto exit;
}
if (_Read(&msg, sizeof(msg)) != sizeof(msg))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -