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 // --------------------------------------------------------------------------
56 // Message codes (don't change them to avoid breaking the existing code using
64 IPC_ADVISE_REQUEST
= 5,
67 IPC_REQUEST_REPLY
= 8,
74 } // anonymous namespace
76 // headers needed for umask()
78 #include <sys/types.h>
80 #endif // __UNIX_LIKE__
82 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 // get the address object for the given server name, the caller must delete it
87 static wxSockAddress
*
88 GetAddressFromName(const wxString
& serverName
,
89 const wxString
& host
= wxEmptyString
)
91 // we always use INET sockets under non-Unix systems
92 #if defined(__UNIX__) && !defined(__WINDOWS__) && !defined(__WINE__)
93 // under Unix, if the server name looks like a path, create a AF_UNIX
94 // socket instead of AF_INET one
95 if ( serverName
.Find(wxT('/')) != wxNOT_FOUND
)
97 wxUNIXaddress
*addr
= new wxUNIXaddress
;
98 addr
->Filename(serverName
);
104 wxIPV4address
*addr
= new wxIPV4address
;
105 addr
->Service(serverName
);
108 addr
->Hostname(host
);
115 // --------------------------------------------------------------------------
116 // wxTCPEventHandler stuff (private class)
117 // --------------------------------------------------------------------------
119 class wxTCPEventHandler
: public wxEvtHandler
122 wxTCPEventHandler() : wxEvtHandler() { }
124 void Client_OnRequest(wxSocketEvent
& event
);
125 void Server_OnRequest(wxSocketEvent
& event
);
128 void HandleDisconnect(wxTCPConnection
*connection
);
130 DECLARE_EVENT_TABLE()
131 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandler
);
136 _CLIENT_ONREQUEST_ID
= 1000,
140 // --------------------------------------------------------------------------
141 // wxTCPEventHandlerModule (private class)
142 // --------------------------------------------------------------------------
144 class wxTCPEventHandlerModule
: public wxModule
147 wxTCPEventHandlerModule() : wxModule() { }
149 // get the global wxTCPEventHandler creating it if necessary
150 static wxTCPEventHandler
& GetHandler()
153 ms_handler
= new wxTCPEventHandler
;
158 // as ms_handler is initialized on demand, don't do anything in OnInit()
159 virtual bool OnInit() { return true; }
160 virtual void OnExit() { wxDELETE(ms_handler
); }
163 static wxTCPEventHandler
*ms_handler
;
165 DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
)
166 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandlerModule
);
169 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
)
171 wxTCPEventHandler
*wxTCPEventHandlerModule::ms_handler
= NULL
;
173 // --------------------------------------------------------------------------
174 // wxIPCSocketStreams
175 // --------------------------------------------------------------------------
179 // this class contains the various (related) streams used by wxTCPConnection
180 // and also provides a way to read from the socket stream directly
182 // for writing to the stream use the IPCOutput class below
183 class wxIPCSocketStreams
186 // ctor initializes all the streams on top of the given socket
188 // note that we use a bigger than default buffer size which matches the
189 // typical Ethernet MTU (minus TCP header overhead)
190 wxIPCSocketStreams(wxSocketBase
& sock
)
191 : m_socketStream(sock
),
193 m_bufferedOut(m_socketStream
, 1448),
195 m_bufferedOut(m_socketStream
),
197 m_dataIn(m_socketStream
),
198 m_dataOut(m_bufferedOut
)
202 // expose the IO methods needed by IPC code (notice that writing is only
203 // done via IPCOutput)
209 m_bufferedOut
.Sync();
213 // simple wrappers around the functions with the same name in
218 return m_dataIn
.Read8();
224 return m_dataIn
.Read32();
227 wxString
ReadString()
230 return m_dataIn
.ReadString();
233 // read arbitrary (size-prepended) data
235 // connection parameter is needed to call its GetBufferAtLeast() method
236 void *ReadData(wxConnectionBase
*conn
, size_t *size
)
240 wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" );
241 wxCHECK_MSG( size
, NULL
, "NULL size parameter" );
245 void * const data
= conn
->GetBufferAtLeast(*size
);
246 wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" );
248 m_socketStream
.Read(data
, *size
);
253 // same as above but for data preceded by the format
255 ReadFormatData(wxConnectionBase
*conn
, wxIPCFormat
*format
, size_t *size
)
257 wxCHECK_MSG( format
, NULL
, "NULL format parameter" );
259 *format
= static_cast<wxIPCFormat
>(Read8());
261 return ReadData(conn
, size
);
265 // these methods are only used by IPCOutput and not directly
266 wxDataOutputStream
& GetDataOut() { return m_dataOut
; }
267 wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; }
270 // this is the low-level underlying stream using the connection socket
271 wxSocketStream m_socketStream
;
273 // the buffered stream is used to avoid writing all pieces of an IPC
274 // request to the socket one by one but to instead do it all at once when
275 // we're done with it
277 wxBufferedOutputStream m_bufferedOut
;
279 wxOutputStream
& m_bufferedOut
;
282 // finally the data streams are used to be able to write typed data into
283 // the above streams easily
284 wxDataInputStream m_dataIn
;
285 wxDataOutputStream m_dataOut
;
287 wxDECLARE_NO_COPY_CLASS(wxIPCSocketStreams
);
293 // an object of this class should be instantiated on the stack to write to the
294 // underlying socket stream
296 // this class is intentionally separated from wxIPCSocketStreams to ensure that
297 // Flush() is always called
301 // construct an object associated with the given streams (which must have
302 // life time greater than ours as we keep a reference to it)
303 IPCOutput(wxIPCSocketStreams
*streams
)
304 : m_streams(*streams
)
306 wxASSERT_MSG( streams
, "NULL streams pointer" );
309 // dtor calls Flush() really sending the IPC data to the network
310 ~IPCOutput() { m_streams
.Flush(); }
314 void Write8(wxUint8 i
)
316 m_streams
.GetDataOut().Write8(i
);
319 // write the reply code and a string
320 void Write(IPCCode code
, const wxString
& str
)
323 m_streams
.GetDataOut().WriteString(str
);
326 // write the reply code, a string and a format in this order
327 void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
)
333 // write arbitrary data
334 void WriteData(const void *data
, size_t size
)
336 m_streams
.GetDataOut().Write32(size
);
337 m_streams
.GetUnformattedOut().Write(data
, size
);
342 wxIPCSocketStreams
& m_streams
;
344 wxDECLARE_NO_COPY_CLASS(IPCOutput
);
347 } // anonymous namespace
349 // ==========================================================================
351 // ==========================================================================
353 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
)
354 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
)
355 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
)
357 // --------------------------------------------------------------------------
359 // --------------------------------------------------------------------------
361 wxTCPClient::wxTCPClient()
366 bool wxTCPClient::ValidHost(const wxString
& host
)
370 return addr
.Hostname(host
);
373 wxConnectionBase
*wxTCPClient::MakeConnection(const wxString
& host
,
374 const wxString
& serverName
,
375 const wxString
& topic
)
377 wxSockAddress
*addr
= GetAddressFromName(serverName
, host
);
381 wxSocketClient
* const client
= new wxSocketClient(wxSOCKET_WAITALL
);
382 wxIPCSocketStreams
* const streams
= new wxIPCSocketStreams(*client
);
384 bool ok
= client
->Connect(*addr
);
389 // Send topic name, and enquire whether this has succeeded
390 IPCOutput(streams
).Write(IPC_CONNECT
, topic
);
392 unsigned char msg
= streams
->Read8();
395 if (msg
== IPC_CONNECT
)
398 connection
= (wxTCPConnection
*)OnMakeConnection ();
402 if (wxDynamicCast(connection
, wxTCPConnection
))
404 connection
->m_topic
= topic
;
405 connection
->m_sock
= client
;
406 connection
->m_streams
= streams
;
407 client
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
408 _CLIENT_ONREQUEST_ID
);
409 client
->SetClientData(connection
);
410 client
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
411 client
->Notify(true);
417 // and fall through to delete everything else
423 // Something went wrong, delete everything
430 wxConnectionBase
*wxTCPClient::OnMakeConnection()
432 return new wxTCPConnection();
435 // --------------------------------------------------------------------------
437 // --------------------------------------------------------------------------
439 wxTCPServer::wxTCPServer()
445 bool wxTCPServer::Create(const wxString
& serverName
)
447 // Destroy previous server, if any
450 m_server
->SetClientData(NULL
);
455 wxSockAddress
*addr
= GetAddressFromName(serverName
);
461 if ( addr
->Type() == wxSockAddress::UNIX
)
463 // ensure that the file doesn't exist as otherwise calling socket()
465 int rc
= remove(serverName
.fn_str());
466 if ( rc
< 0 && errno
!= ENOENT
)
473 // also set the umask to prevent the others from reading our file
474 umaskOld
= umask(077);
478 // unused anyhow but shut down the compiler warnings
481 #endif // __UNIX_LIKE__
483 // Create a socket listening on the specified port (reusing it to allow
484 // restarting the server listening on the same port as was used by the
485 // previous instance of this server)
486 m_server
= new wxSocketServer(*addr
, wxSOCKET_WAITALL
| wxSOCKET_REUSEADDR
);
489 if ( addr
->Type() == wxSockAddress::UNIX
)
494 // save the file name to remove it later
495 m_filename
= serverName
;
497 #endif // __UNIX_LIKE__
501 if (!m_server
->IsOk())
509 m_server
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
510 _SERVER_ONREQUEST_ID
);
511 m_server
->SetClientData(this);
512 m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
513 m_server
->Notify(true);
518 wxTCPServer::~wxTCPServer()
522 m_server
->SetClientData(NULL
);
527 if ( !m_filename
.empty() )
529 if ( remove(m_filename
.fn_str()) != 0 )
531 wxLogDebug(wxT("Stale AF_UNIX file '%s' left."), m_filename
.c_str());
534 #endif // __UNIX_LIKE__
538 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
))
540 return new wxTCPConnection();
543 // --------------------------------------------------------------------------
545 // --------------------------------------------------------------------------
547 void wxTCPConnection::Init()
553 wxTCPConnection::~wxTCPConnection()
559 m_sock
->SetClientData(NULL
);
566 void wxTCPConnection::Compress(bool WXUNUSED(on
))
571 // Calls that CLIENT can make.
572 bool wxTCPConnection::Disconnect()
574 if ( !GetConnected() )
577 // Send the disconnect message to the peer.
578 IPCOutput(m_streams
).Write8(IPC_DISCONNECT
);
582 m_sock
->Notify(false);
591 bool wxTCPConnection::DoExecute(const void *data
,
595 if ( !m_sock
->IsConnected() )
598 // Prepare EXECUTE message
599 IPCOutput
out(m_streams
);
600 out
.Write8(IPC_EXECUTE
);
603 out
.WriteData(data
, size
);
608 const void *wxTCPConnection::Request(const wxString
& item
,
612 if ( !m_sock
->IsConnected() )
615 IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
);
617 const int ret
= m_streams
->Read8();
618 if ( ret
!= IPC_REQUEST_REPLY
)
621 // ReadData() needs a non-NULL size pointer but the client code can call us
622 // with NULL pointer (this makes sense if it knows that it always works
623 // with NUL-terminated strings)
625 return m_streams
->ReadData(this, size
? size
: &sizeFallback
);
628 bool wxTCPConnection::DoPoke(const wxString
& item
,
633 if ( !m_sock
->IsConnected() )
636 IPCOutput
out(m_streams
);
637 out
.Write(IPC_POKE
, item
, format
);
638 out
.WriteData(data
, size
);
643 bool wxTCPConnection::StartAdvise(const wxString
& item
)
645 if ( !m_sock
->IsConnected() )
648 IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
);
650 const int ret
= m_streams
->Read8();
652 return ret
== IPC_ADVISE_START
;
655 bool wxTCPConnection::StopAdvise (const wxString
& item
)
657 if ( !m_sock
->IsConnected() )
660 IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
);
662 const int ret
= m_streams
->Read8();
664 return ret
== IPC_ADVISE_STOP
;
667 // Calls that SERVER can make
668 bool wxTCPConnection::DoAdvise(const wxString
& item
,
673 if ( !m_sock
->IsConnected() )
676 IPCOutput
out(m_streams
);
677 out
.Write(IPC_ADVISE
, item
, format
);
678 out
.WriteData(data
, size
);
683 // --------------------------------------------------------------------------
684 // wxTCPEventHandler (private class)
685 // --------------------------------------------------------------------------
687 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
)
688 EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
)
689 EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
)
692 void wxTCPEventHandler::HandleDisconnect(wxTCPConnection
*connection
)
694 // connection was closed (either gracefully or not): destroy everything
695 connection
->m_sock
->Notify(false);
696 connection
->m_sock
->Close();
698 // don't leave references to this soon-to-be-dangling connection in the
699 // socket as it won't be destroyed immediately as its destruction will be
700 // delayed in case there are more events pending for it
701 connection
->m_sock
->SetClientData(NULL
);
703 connection
->SetConnected(false);
704 connection
->OnDisconnect();
707 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent
&event
)
709 wxSocketBase
*sock
= event
.GetSocket();
713 wxSocketNotify evt
= event
.GetSocketEvent();
714 wxTCPConnection
* const
715 connection
= static_cast<wxTCPConnection
*>(sock
->GetClientData());
717 // This socket is being deleted; skip this event
721 if ( evt
== wxSOCKET_LOST
)
723 HandleDisconnect(connection
);
727 // Receive message number.
728 wxIPCSocketStreams
* const streams
= connection
->m_streams
;
730 const wxString topic
= connection
->m_topic
;
735 const int msg
= streams
->Read8();
741 size_t size
wxDUMMY_INITIALIZE(0);
743 data
= streams
->ReadFormatData(connection
, &format
, &size
);
745 connection
->OnExecute(topic
, data
, size
, format
);
753 item
= streams
->ReadString();
756 size_t size
wxDUMMY_INITIALIZE(0);
758 data
= streams
->ReadFormatData(connection
, &format
, &size
);
761 connection
->OnAdvise(topic
, item
, data
, size
, format
);
767 case IPC_ADVISE_START
:
769 item
= streams
->ReadString();
771 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
)
777 case IPC_ADVISE_STOP
:
779 item
= streams
->ReadString();
781 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
)
789 item
= streams
->ReadString();
790 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
792 size_t size
wxDUMMY_INITIALIZE(0);
793 void * const data
= streams
->ReadData(connection
, &size
);
796 connection
->OnPoke(topic
, item
, data
, size
, format
);
804 item
= streams
->ReadString();
806 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
808 size_t user_size
= wxNO_LEN
;
809 const void *user_data
= connection
->OnRequest(topic
,
816 IPCOutput(streams
).Write8(IPC_FAIL
);
820 IPCOutput
out(streams
);
821 out
.Write8(IPC_REQUEST_REPLY
);
823 if ( user_size
== wxNO_LEN
)
829 user_size
= strlen((const char *)user_data
) + 1; // includes final NUL
831 case wxIPC_UNICODETEXT
:
832 user_size
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t); // includes final NUL
839 out
.WriteData(user_data
, user_size
);
844 HandleDisconnect(connection
);
848 wxLogDebug("Unexpected IPC_FAIL received");
853 wxLogDebug("Unknown message code %d received.", msg
);
859 IPCOutput(streams
).Write8(IPC_FAIL
);
862 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent
&event
)
864 wxSocketServer
*server
= (wxSocketServer
*) event
.GetSocket();
867 wxTCPServer
*ipcserv
= (wxTCPServer
*) server
->GetClientData();
869 // This socket is being deleted; skip this event
873 if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
)
876 // Accept the connection, getting a new socket
877 wxSocketBase
*sock
= server
->Accept();
886 wxIPCSocketStreams
*streams
= new wxIPCSocketStreams(*sock
);
889 IPCOutput
out(streams
);
891 const int msg
= streams
->Read8();
892 if ( msg
== IPC_CONNECT
)
894 const wxString topic
= streams
->ReadString();
896 wxTCPConnection
*new_connection
=
897 (wxTCPConnection
*)ipcserv
->OnAcceptConnection (topic
);
901 if (wxDynamicCast(new_connection
, wxTCPConnection
))
903 // Acknowledge success
904 out
.Write8(IPC_CONNECT
);
906 new_connection
->m_sock
= sock
;
907 new_connection
->m_streams
= streams
;
908 new_connection
->m_topic
= topic
;
909 sock
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
910 _CLIENT_ONREQUEST_ID
);
911 sock
->SetClientData(new_connection
);
912 sock
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
918 delete new_connection
;
919 // and fall through to delete everything else
924 // Something went wrong, send failure message and delete everything
925 out
.Write8(IPC_FAIL
);
926 } // IPCOutput object is destroyed here, before destroying stream
932 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS