1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sckipc.cpp
3 // Purpose: Interprocess communication implementation (wxSocket version)
4 // Author: Julian Smart
5 // Modified by: Guilhem Lavaux (big rewrite) May 1997, 1998
6 // Guillermo Rodriguez (updated for wxSocket v2) Jan 2000
7 // (callbacks deprecated) Mar 2000
8 // Vadim Zeitlin (added support for Unix sockets) Apr 2002
9 // (use buffering, many fixes/cleanup) Oct 2008
12 // Copyright: (c) Julian Smart 1993
13 // (c) Guilhem Lavaux 1997, 1998
14 // (c) 2000 Guillermo Rodriguez <guille@iies.es>
15 // Licence: wxWindows licence
16 /////////////////////////////////////////////////////////////////////////////
18 // ==========================================================================
20 // ==========================================================================
22 // --------------------------------------------------------------------------
24 // --------------------------------------------------------------------------
26 // For compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
33 #if wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS
35 #include "wx/sckipc.h"
40 #include "wx/module.h"
47 #include "wx/socket.h"
49 // --------------------------------------------------------------------------
50 // macros and constants
51 // --------------------------------------------------------------------------
72 } // anonymous namespace
74 // headers needed for umask()
76 #include <sys/types.h>
78 #endif // __UNIX_LIKE__
80 // ----------------------------------------------------------------------------
82 // ----------------------------------------------------------------------------
84 // get the address object for the given server name, the caller must delete it
85 static wxSockAddress
*
86 GetAddressFromName(const wxString
& serverName
,
87 const wxString
& host
= wxEmptyString
)
89 // we always use INET sockets under non-Unix systems
90 #if defined(__UNIX__) && !defined(__WINDOWS__) && !defined(__WINE__)
91 // under Unix, if the server name looks like a path, create a AF_UNIX
92 // socket instead of AF_INET one
93 if ( serverName
.Find(_T('/')) != wxNOT_FOUND
)
95 wxUNIXaddress
*addr
= new wxUNIXaddress
;
96 addr
->Filename(serverName
);
102 wxIPV4address
*addr
= new wxIPV4address
;
103 addr
->Service(serverName
);
106 addr
->Hostname(host
);
113 // --------------------------------------------------------------------------
114 // wxTCPEventHandler stuff (private class)
115 // --------------------------------------------------------------------------
117 class wxTCPEventHandler
: public wxEvtHandler
120 wxTCPEventHandler() : wxEvtHandler() {}
122 void Client_OnRequest(wxSocketEvent
& event
);
123 void Server_OnRequest(wxSocketEvent
& event
);
125 DECLARE_EVENT_TABLE()
126 DECLARE_NO_COPY_CLASS(wxTCPEventHandler
)
131 _CLIENT_ONREQUEST_ID
= 1000,
135 static wxTCPEventHandler
*gs_handler
= NULL
;
137 // --------------------------------------------------------------------------
138 // wxIPCSocketStreams
139 // --------------------------------------------------------------------------
143 // this class contains the various (related) streams used by wxTCPConnection
144 // and also provides a way to read from the socket stream directly
146 // for writing to the stream use the IPCOutput class below
147 class wxIPCSocketStreams
150 // ctor initializes all the streams on top of the given socket
152 // note that we use a bigger than default buffer size which matches the
153 // typical Ethernet MTU
154 wxIPCSocketStreams(wxSocketBase
& sock
)
155 : m_socketStream(sock
),
157 m_bufferedOut(m_socketStream
, 1500),
159 m_bufferedOut(m_socketStream
),
161 m_dataIn(m_socketStream
),
162 m_dataOut(m_bufferedOut
)
166 // expose the IO methods needed by IPC code (notice that writing is only
167 // done via IPCOutput)
173 m_bufferedOut
.Sync();
177 // simple wrappers around the functions with the same name in
182 return m_dataIn
.Read8();
188 return m_dataIn
.Read32();
191 wxString
ReadString()
194 return m_dataIn
.ReadString();
197 // read arbitrary (size-prepended) data
199 // connection parameter is needed to call its GetBufferAtLeast() method
200 void *ReadData(wxConnectionBase
*conn
, size_t *size
)
204 wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" );
205 wxCHECK_MSG( size
, NULL
, "NULL size parameter" );
209 void * const data
= conn
->GetBufferAtLeast(*size
);
210 wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" );
212 m_socketStream
.Read(data
, *size
);
217 // same as above but for data preceded by the format
219 ReadFormatData(wxConnectionBase
*conn
, wxIPCFormat
*format
, size_t *size
)
221 wxCHECK_MSG( format
, NULL
, "NULL format parameter" );
223 *format
= static_cast<wxIPCFormat
>(Read8());
225 return ReadData(conn
, size
);
229 // these methods are only used by IPCOutput and not directly
230 wxDataOutputStream
& GetDataOut() { return m_dataOut
; }
231 wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; }
234 // this is the low-level underlying stream using the connection socket
235 wxSocketStream m_socketStream
;
237 // the buffered stream is used to avoid writing all pieces of an IPC
238 // request to the socket one by one but to instead do it all at once when
239 // we're done with it
241 wxBufferedOutputStream m_bufferedOut
;
243 wxOutputStream
& m_bufferedOut
;
246 // finally the data streams are used to be able to write typed data into
247 // the above streams easily
248 wxDataInputStream m_dataIn
;
249 wxDataOutputStream m_dataOut
;
251 DECLARE_NO_COPY_CLASS(wxIPCSocketStreams
)
257 // an object of this class should be instantiated on the stack to write to the
258 // underlying socket stream
260 // this class is intentionally separated from wxIPCSocketStreams to ensure that
261 // Flush() is always called
265 // construct an object associated with the given streams (which must have
266 // life time greater than ours as we keep a reference to it)
267 IPCOutput(wxIPCSocketStreams
*streams
)
268 : m_streams(*streams
)
270 wxASSERT_MSG( streams
, "NULL streams pointer" );
273 // dtor calls Flush() really sending the IPC data to the network
274 ~IPCOutput() { m_streams
.Flush(); }
278 void Write8(wxUint8 i
)
280 m_streams
.GetDataOut().Write8(i
);
283 // write the reply code and a string
284 void Write(IPCCode code
, const wxString
& str
)
287 m_streams
.GetDataOut().WriteString(str
);
290 // write the reply code, a string and a format in this order
291 void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
)
297 // write arbitrary data
298 void WriteData(const void *data
, size_t size
)
300 m_streams
.GetDataOut().Write32(size
);
301 m_streams
.GetUnformattedOut().Write(data
, size
);
306 wxIPCSocketStreams
& m_streams
;
308 DECLARE_NO_COPY_CLASS(IPCOutput
)
311 } // anonymous namespace
313 // ==========================================================================
315 // ==========================================================================
317 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
)
318 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
)
319 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
)
321 // --------------------------------------------------------------------------
323 // --------------------------------------------------------------------------
325 wxTCPClient::wxTCPClient()
330 bool wxTCPClient::ValidHost(const wxString
& host
)
334 return addr
.Hostname(host
);
337 wxConnectionBase
*wxTCPClient::MakeConnection(const wxString
& host
,
338 const wxString
& serverName
,
339 const wxString
& topic
)
341 wxSockAddress
*addr
= GetAddressFromName(serverName
, host
);
345 wxSocketClient
* const client
= new wxSocketClient(wxSOCKET_WAITALL
);
346 wxIPCSocketStreams
* const streams
= new wxIPCSocketStreams(*client
);
348 bool ok
= client
->Connect(*addr
);
353 // Send topic name, and enquire whether this has succeeded
354 IPCOutput(streams
).Write(IPC_CONNECT
, topic
);
356 unsigned char msg
= streams
->Read8();
359 if (msg
== IPC_CONNECT
)
362 connection
= (wxTCPConnection
*)OnMakeConnection ();
366 if (connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
368 connection
->m_topic
= topic
;
369 connection
->m_sock
= client
;
370 connection
->m_streams
= streams
;
371 client
->SetEventHandler(*gs_handler
, _CLIENT_ONREQUEST_ID
);
372 client
->SetClientData(connection
);
373 client
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
374 client
->Notify(true);
380 // and fall through to delete everything else
386 // Something went wrong, delete everything
393 wxConnectionBase
*wxTCPClient::OnMakeConnection()
395 return new wxTCPConnection();
398 // --------------------------------------------------------------------------
400 // --------------------------------------------------------------------------
402 wxTCPServer::wxTCPServer()
408 bool wxTCPServer::Create(const wxString
& serverName
)
410 // Destroy previous server, if any
413 m_server
->SetClientData(NULL
);
418 wxSockAddress
*addr
= GetAddressFromName(serverName
);
424 if ( addr
->Type() == wxSockAddress::UNIX
)
426 // ensure that the file doesn't exist as otherwise calling socket()
428 int rc
= remove(serverName
.fn_str());
429 if ( rc
< 0 && errno
!= ENOENT
)
436 // also set the umask to prevent the others from reading our file
437 umaskOld
= umask(077);
441 // unused anyhow but shut down the compiler warnings
444 #endif // __UNIX_LIKE__
446 // Create a socket listening on the specified port (reusing it to allow
447 // restarting the server listening on the same port as was used by the
448 // previous instance of this server)
449 m_server
= new wxSocketServer(*addr
, wxSOCKET_WAITALL
| wxSOCKET_REUSEADDR
);
452 if ( addr
->Type() == wxSockAddress::UNIX
)
457 // save the file name to remove it later
458 m_filename
= serverName
;
460 #endif // __UNIX_LIKE__
472 m_server
->SetEventHandler(*gs_handler
, _SERVER_ONREQUEST_ID
);
473 m_server
->SetClientData(this);
474 m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
475 m_server
->Notify(true);
480 wxTCPServer::~wxTCPServer()
484 m_server
->SetClientData(NULL
);
489 if ( !m_filename
.empty() )
491 if ( remove(m_filename
.fn_str()) != 0 )
493 wxLogDebug(_T("Stale AF_UNIX file '%s' left."), m_filename
.c_str());
496 #endif // __UNIX_LIKE__
500 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
))
502 return new wxTCPConnection();
505 // --------------------------------------------------------------------------
507 // --------------------------------------------------------------------------
509 void wxTCPConnection::Init()
515 wxTCPConnection::~wxTCPConnection()
521 m_sock
->SetClientData(NULL
);
528 void wxTCPConnection::Compress(bool WXUNUSED(on
))
533 // Calls that CLIENT can make.
534 bool wxTCPConnection::Disconnect()
536 if ( !GetConnected() )
539 // Send the disconnect message to the peer.
540 IPCOutput(m_streams
).Write8(IPC_DISCONNECT
);
544 m_sock
->Notify(false);
553 bool wxTCPConnection::DoExecute(const void *data
,
557 if ( !m_sock
->IsConnected() )
560 // Prepare EXECUTE message
561 IPCOutput
out(m_streams
);
562 out
.Write8(IPC_EXECUTE
);
565 out
.WriteData(data
, size
);
570 const void *wxTCPConnection::Request(const wxString
& item
,
574 if ( !m_sock
->IsConnected() )
577 IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
);
579 int ret
= m_streams
->Read8();
580 if ( ret
== IPC_FAIL
)
583 return m_streams
->ReadData(this, size
);
586 bool wxTCPConnection::DoPoke(const wxString
& item
,
591 if ( !m_sock
->IsConnected() )
594 IPCOutput
out(m_streams
);
595 out
.Write(IPC_POKE
, item
, format
);
596 out
.WriteData(data
, size
);
601 bool wxTCPConnection::StartAdvise(const wxString
& item
)
603 if ( !m_sock
->IsConnected() )
606 IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
);
608 int ret
= m_streams
->Read8();
615 bool wxTCPConnection::StopAdvise (const wxString
& item
)
617 if ( !m_sock
->IsConnected() )
620 IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
);
622 int ret
= m_streams
->Read8();
630 // Calls that SERVER can make
631 bool wxTCPConnection::DoAdvise(const wxString
& item
,
636 if ( !m_sock
->IsConnected() )
639 IPCOutput
out(m_streams
);
640 out
.Write(IPC_ADVISE
, item
, format
);
641 out
.WriteData(data
, size
);
646 // --------------------------------------------------------------------------
647 // wxTCPEventHandler (private class)
648 // --------------------------------------------------------------------------
650 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
)
651 EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
)
652 EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
)
655 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent
&event
)
657 wxSocketBase
*sock
= event
.GetSocket();
661 wxSocketNotify evt
= event
.GetSocketEvent();
662 wxTCPConnection
*connection
= (wxTCPConnection
*)(sock
->GetClientData());
664 // This socket is being deleted; skip this event
668 // We lost the connection: destroy everything
669 if (evt
== wxSOCKET_LOST
)
673 connection
->OnDisconnect();
677 // Receive message number.
678 wxIPCSocketStreams
* const streams
= connection
->m_streams
;
680 const wxString topic
= connection
->m_topic
;
685 const int msg
= streams
->Read8();
691 size_t size
wxDUMMY_INITIALIZE(0);
693 data
= streams
->ReadFormatData(connection
, &format
, &size
);
695 connection
->OnExecute(topic
, data
, size
, format
);
703 item
= streams
->ReadString();
706 size_t size
wxDUMMY_INITIALIZE(0);
708 data
= streams
->ReadFormatData(connection
, &format
, &size
);
711 connection
->OnAdvise(topic
, item
, data
, size
, format
);
717 case IPC_ADVISE_START
:
719 item
= streams
->ReadString();
721 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
)
727 case IPC_ADVISE_STOP
:
729 item
= streams
->ReadString();
731 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
)
739 item
= streams
->ReadString();
740 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
742 size_t size
wxDUMMY_INITIALIZE(0);
743 void * const data
= streams
->ReadData(connection
, &size
);
746 connection
->OnPoke(topic
, item
, data
, size
, format
);
754 item
= streams
->ReadString();
756 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
758 size_t user_size
= wxNO_LEN
;
759 const void *user_data
= connection
->OnRequest(topic
,
766 IPCOutput(streams
).Write8(IPC_FAIL
);
770 IPCOutput
out(streams
);
771 out
.Write8(IPC_REQUEST_REPLY
);
773 if ( user_size
== wxNO_LEN
)
779 user_size
= strlen((const char *)user_data
) + 1; // includes final NUL
781 case wxIPC_UNICODETEXT
:
782 user_size
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t); // includes final NUL
789 out
.WriteData(user_data
, user_size
);
796 connection
->SetConnected(false);
797 connection
->OnDisconnect();
801 wxLogDebug("Unknown message code %d received.", msg
);
807 IPCOutput(streams
).Write8(IPC_FAIL
);
810 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent
&event
)
812 wxSocketServer
*server
= (wxSocketServer
*) event
.GetSocket();
815 wxTCPServer
*ipcserv
= (wxTCPServer
*) server
->GetClientData();
817 // This socket is being deleted; skip this event
821 if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
)
824 // Accept the connection, getting a new socket
825 wxSocketBase
*sock
= server
->Accept();
834 wxIPCSocketStreams
*streams
= new wxIPCSocketStreams(*sock
);
837 IPCOutput
out(streams
);
839 const int msg
= streams
->Read8();
840 if ( msg
== IPC_CONNECT
)
842 const wxString topic
= streams
->ReadString();
844 wxTCPConnection
*new_connection
=
845 (wxTCPConnection
*)ipcserv
->OnAcceptConnection (topic
);
849 if (new_connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
851 // Acknowledge success
852 out
.Write8(IPC_CONNECT
);
854 new_connection
->m_sock
= sock
;
855 new_connection
->m_streams
= streams
;
856 new_connection
->m_topic
= topic
;
857 sock
->SetEventHandler(*gs_handler
, _CLIENT_ONREQUEST_ID
);
858 sock
->SetClientData(new_connection
);
859 sock
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
865 delete new_connection
;
866 // and fall through to delete everything else
871 // Something went wrong, send failure message and delete everything
872 out
.Write8(IPC_FAIL
);
873 } // IPCOutput object is destroyed here, before destroying stream
879 // --------------------------------------------------------------------------
880 // wxTCPEventHandlerModule (private class)
881 // --------------------------------------------------------------------------
883 class wxTCPEventHandlerModule
: public wxModule
886 virtual bool OnInit() { gs_handler
= new wxTCPEventHandler
; return true; }
887 virtual void OnExit() { wxDELETE(gs_handler
); }
889 DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
)
892 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
)
894 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS