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(_T('/')) != 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 DECLARE_EVENT_TABLE()
129 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandler
);
134 _CLIENT_ONREQUEST_ID
= 1000,
138 // --------------------------------------------------------------------------
139 // wxTCPEventHandlerModule (private class)
140 // --------------------------------------------------------------------------
142 class wxTCPEventHandlerModule
: public wxModule
145 wxTCPEventHandlerModule() : wxModule() { }
147 // get the global wxTCPEventHandler creating it if necessary
148 static wxTCPEventHandler
& GetHandler()
151 ms_handler
= new wxTCPEventHandler
;
156 // as ms_handler is initialized on demand, don't do anything in OnInit()
157 virtual bool OnInit() { return true; }
158 virtual void OnExit() { wxDELETE(ms_handler
); }
161 static wxTCPEventHandler
*ms_handler
;
163 DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
)
164 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandlerModule
);
167 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
)
169 wxTCPEventHandler
*wxTCPEventHandlerModule::ms_handler
= NULL
;
171 // --------------------------------------------------------------------------
172 // wxIPCSocketStreams
173 // --------------------------------------------------------------------------
177 // this class contains the various (related) streams used by wxTCPConnection
178 // and also provides a way to read from the socket stream directly
180 // for writing to the stream use the IPCOutput class below
181 class wxIPCSocketStreams
184 // ctor initializes all the streams on top of the given socket
186 // note that we use a bigger than default buffer size which matches the
187 // typical Ethernet MTU (minus TCP header overhead)
188 wxIPCSocketStreams(wxSocketBase
& sock
)
189 : m_socketStream(sock
),
191 m_bufferedOut(m_socketStream
, 1448),
193 m_bufferedOut(m_socketStream
),
195 m_dataIn(m_socketStream
),
196 m_dataOut(m_bufferedOut
)
200 // expose the IO methods needed by IPC code (notice that writing is only
201 // done via IPCOutput)
207 m_bufferedOut
.Sync();
211 // simple wrappers around the functions with the same name in
216 return m_dataIn
.Read8();
222 return m_dataIn
.Read32();
225 wxString
ReadString()
228 return m_dataIn
.ReadString();
231 // read arbitrary (size-prepended) data
233 // connection parameter is needed to call its GetBufferAtLeast() method
234 void *ReadData(wxConnectionBase
*conn
, size_t *size
)
238 wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" );
239 wxCHECK_MSG( size
, NULL
, "NULL size parameter" );
243 void * const data
= conn
->GetBufferAtLeast(*size
);
244 wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" );
246 m_socketStream
.Read(data
, *size
);
251 // same as above but for data preceded by the format
253 ReadFormatData(wxConnectionBase
*conn
, wxIPCFormat
*format
, size_t *size
)
255 wxCHECK_MSG( format
, NULL
, "NULL format parameter" );
257 *format
= static_cast<wxIPCFormat
>(Read8());
259 return ReadData(conn
, size
);
263 // these methods are only used by IPCOutput and not directly
264 wxDataOutputStream
& GetDataOut() { return m_dataOut
; }
265 wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; }
268 // this is the low-level underlying stream using the connection socket
269 wxSocketStream m_socketStream
;
271 // the buffered stream is used to avoid writing all pieces of an IPC
272 // request to the socket one by one but to instead do it all at once when
273 // we're done with it
275 wxBufferedOutputStream m_bufferedOut
;
277 wxOutputStream
& m_bufferedOut
;
280 // finally the data streams are used to be able to write typed data into
281 // the above streams easily
282 wxDataInputStream m_dataIn
;
283 wxDataOutputStream m_dataOut
;
285 wxDECLARE_NO_COPY_CLASS(wxIPCSocketStreams
);
291 // an object of this class should be instantiated on the stack to write to the
292 // underlying socket stream
294 // this class is intentionally separated from wxIPCSocketStreams to ensure that
295 // Flush() is always called
299 // construct an object associated with the given streams (which must have
300 // life time greater than ours as we keep a reference to it)
301 IPCOutput(wxIPCSocketStreams
*streams
)
302 : m_streams(*streams
)
304 wxASSERT_MSG( streams
, "NULL streams pointer" );
307 // dtor calls Flush() really sending the IPC data to the network
308 ~IPCOutput() { m_streams
.Flush(); }
312 void Write8(wxUint8 i
)
314 m_streams
.GetDataOut().Write8(i
);
317 // write the reply code and a string
318 void Write(IPCCode code
, const wxString
& str
)
321 m_streams
.GetDataOut().WriteString(str
);
324 // write the reply code, a string and a format in this order
325 void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
)
331 // write arbitrary data
332 void WriteData(const void *data
, size_t size
)
334 m_streams
.GetDataOut().Write32(size
);
335 m_streams
.GetUnformattedOut().Write(data
, size
);
340 wxIPCSocketStreams
& m_streams
;
342 wxDECLARE_NO_COPY_CLASS(IPCOutput
);
345 } // anonymous namespace
347 // ==========================================================================
349 // ==========================================================================
351 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
)
352 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
)
353 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
)
355 // --------------------------------------------------------------------------
357 // --------------------------------------------------------------------------
359 wxTCPClient::wxTCPClient()
364 bool wxTCPClient::ValidHost(const wxString
& host
)
368 return addr
.Hostname(host
);
371 wxConnectionBase
*wxTCPClient::MakeConnection(const wxString
& host
,
372 const wxString
& serverName
,
373 const wxString
& topic
)
375 wxSockAddress
*addr
= GetAddressFromName(serverName
, host
);
379 wxSocketClient
* const client
= new wxSocketClient(wxSOCKET_WAITALL
);
380 wxIPCSocketStreams
* const streams
= new wxIPCSocketStreams(*client
);
382 bool ok
= client
->Connect(*addr
);
387 // Send topic name, and enquire whether this has succeeded
388 IPCOutput(streams
).Write(IPC_CONNECT
, topic
);
390 unsigned char msg
= streams
->Read8();
393 if (msg
== IPC_CONNECT
)
396 connection
= (wxTCPConnection
*)OnMakeConnection ();
400 if (connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
402 connection
->m_topic
= topic
;
403 connection
->m_sock
= client
;
404 connection
->m_streams
= streams
;
405 client
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
406 _CLIENT_ONREQUEST_ID
);
407 client
->SetClientData(connection
);
408 client
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
409 client
->Notify(true);
415 // and fall through to delete everything else
421 // Something went wrong, delete everything
428 wxConnectionBase
*wxTCPClient::OnMakeConnection()
430 return new wxTCPConnection();
433 // --------------------------------------------------------------------------
435 // --------------------------------------------------------------------------
437 wxTCPServer::wxTCPServer()
443 bool wxTCPServer::Create(const wxString
& serverName
)
445 // Destroy previous server, if any
448 m_server
->SetClientData(NULL
);
453 wxSockAddress
*addr
= GetAddressFromName(serverName
);
459 if ( addr
->Type() == wxSockAddress::UNIX
)
461 // ensure that the file doesn't exist as otherwise calling socket()
463 int rc
= remove(serverName
.fn_str());
464 if ( rc
< 0 && errno
!= ENOENT
)
471 // also set the umask to prevent the others from reading our file
472 umaskOld
= umask(077);
476 // unused anyhow but shut down the compiler warnings
479 #endif // __UNIX_LIKE__
481 // Create a socket listening on the specified port (reusing it to allow
482 // restarting the server listening on the same port as was used by the
483 // previous instance of this server)
484 m_server
= new wxSocketServer(*addr
, wxSOCKET_WAITALL
| wxSOCKET_REUSEADDR
);
487 if ( addr
->Type() == wxSockAddress::UNIX
)
492 // save the file name to remove it later
493 m_filename
= serverName
;
495 #endif // __UNIX_LIKE__
507 m_server
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
508 _SERVER_ONREQUEST_ID
);
509 m_server
->SetClientData(this);
510 m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
511 m_server
->Notify(true);
516 wxTCPServer::~wxTCPServer()
520 m_server
->SetClientData(NULL
);
525 if ( !m_filename
.empty() )
527 if ( remove(m_filename
.fn_str()) != 0 )
529 wxLogDebug(_T("Stale AF_UNIX file '%s' left."), m_filename
.c_str());
532 #endif // __UNIX_LIKE__
536 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
))
538 return new wxTCPConnection();
541 // --------------------------------------------------------------------------
543 // --------------------------------------------------------------------------
545 void wxTCPConnection::Init()
551 wxTCPConnection::~wxTCPConnection()
557 m_sock
->SetClientData(NULL
);
564 void wxTCPConnection::Compress(bool WXUNUSED(on
))
569 // Calls that CLIENT can make.
570 bool wxTCPConnection::Disconnect()
572 if ( !GetConnected() )
575 // Send the disconnect message to the peer.
576 IPCOutput(m_streams
).Write8(IPC_DISCONNECT
);
580 m_sock
->Notify(false);
589 bool wxTCPConnection::DoExecute(const void *data
,
593 if ( !m_sock
->IsConnected() )
596 // Prepare EXECUTE message
597 IPCOutput
out(m_streams
);
598 out
.Write8(IPC_EXECUTE
);
601 out
.WriteData(data
, size
);
606 const void *wxTCPConnection::Request(const wxString
& item
,
610 if ( !m_sock
->IsConnected() )
613 IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
);
615 const int ret
= m_streams
->Read8();
616 if ( ret
!= IPC_REQUEST_REPLY
)
619 // ReadData() needs a non-NULL size pointer but the client code can call us
620 // with NULL pointer (this makes sense if it knows that it always works
621 // with NUL-terminated strings)
623 return m_streams
->ReadData(this, size
? size
: &sizeFallback
);
626 bool wxTCPConnection::DoPoke(const wxString
& item
,
631 if ( !m_sock
->IsConnected() )
634 IPCOutput
out(m_streams
);
635 out
.Write(IPC_POKE
, item
, format
);
636 out
.WriteData(data
, size
);
641 bool wxTCPConnection::StartAdvise(const wxString
& item
)
643 if ( !m_sock
->IsConnected() )
646 IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
);
648 const int ret
= m_streams
->Read8();
650 return ret
== IPC_ADVISE_START
;
653 bool wxTCPConnection::StopAdvise (const wxString
& item
)
655 if ( !m_sock
->IsConnected() )
658 IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
);
660 const int ret
= m_streams
->Read8();
662 return ret
== IPC_ADVISE_STOP
;
665 // Calls that SERVER can make
666 bool wxTCPConnection::DoAdvise(const wxString
& item
,
671 if ( !m_sock
->IsConnected() )
674 IPCOutput
out(m_streams
);
675 out
.Write(IPC_ADVISE
, item
, format
);
676 out
.WriteData(data
, size
);
681 // --------------------------------------------------------------------------
682 // wxTCPEventHandler (private class)
683 // --------------------------------------------------------------------------
685 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
)
686 EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
)
687 EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
)
690 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent
&event
)
692 wxSocketBase
*sock
= event
.GetSocket();
696 wxSocketNotify evt
= event
.GetSocketEvent();
697 wxTCPConnection
*connection
= (wxTCPConnection
*)(sock
->GetClientData());
699 // This socket is being deleted; skip this event
703 // We lost the connection: destroy everything
704 if (evt
== wxSOCKET_LOST
)
708 connection
->OnDisconnect();
712 // Receive message number.
713 wxIPCSocketStreams
* const streams
= connection
->m_streams
;
715 const wxString topic
= connection
->m_topic
;
720 const int msg
= streams
->Read8();
726 size_t size
wxDUMMY_INITIALIZE(0);
728 data
= streams
->ReadFormatData(connection
, &format
, &size
);
730 connection
->OnExecute(topic
, data
, size
, format
);
738 item
= streams
->ReadString();
741 size_t size
wxDUMMY_INITIALIZE(0);
743 data
= streams
->ReadFormatData(connection
, &format
, &size
);
746 connection
->OnAdvise(topic
, item
, data
, size
, format
);
752 case IPC_ADVISE_START
:
754 item
= streams
->ReadString();
756 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
)
762 case IPC_ADVISE_STOP
:
764 item
= streams
->ReadString();
766 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
)
774 item
= streams
->ReadString();
775 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
777 size_t size
wxDUMMY_INITIALIZE(0);
778 void * const data
= streams
->ReadData(connection
, &size
);
781 connection
->OnPoke(topic
, item
, data
, size
, format
);
789 item
= streams
->ReadString();
791 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
793 size_t user_size
= wxNO_LEN
;
794 const void *user_data
= connection
->OnRequest(topic
,
801 IPCOutput(streams
).Write8(IPC_FAIL
);
805 IPCOutput
out(streams
);
806 out
.Write8(IPC_REQUEST_REPLY
);
808 if ( user_size
== wxNO_LEN
)
814 user_size
= strlen((const char *)user_data
) + 1; // includes final NUL
816 case wxIPC_UNICODETEXT
:
817 user_size
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t); // includes final NUL
824 out
.WriteData(user_data
, user_size
);
831 connection
->SetConnected(false);
832 connection
->OnDisconnect();
836 wxLogDebug("Unexpected IPC_FAIL received");
841 wxLogDebug("Unknown message code %d received.", msg
);
847 IPCOutput(streams
).Write8(IPC_FAIL
);
850 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent
&event
)
852 wxSocketServer
*server
= (wxSocketServer
*) event
.GetSocket();
855 wxTCPServer
*ipcserv
= (wxTCPServer
*) server
->GetClientData();
857 // This socket is being deleted; skip this event
861 if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
)
864 // Accept the connection, getting a new socket
865 wxSocketBase
*sock
= server
->Accept();
874 wxIPCSocketStreams
*streams
= new wxIPCSocketStreams(*sock
);
877 IPCOutput
out(streams
);
879 const int msg
= streams
->Read8();
880 if ( msg
== IPC_CONNECT
)
882 const wxString topic
= streams
->ReadString();
884 wxTCPConnection
*new_connection
=
885 (wxTCPConnection
*)ipcserv
->OnAcceptConnection (topic
);
889 if (new_connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
891 // Acknowledge success
892 out
.Write8(IPC_CONNECT
);
894 new_connection
->m_sock
= sock
;
895 new_connection
->m_streams
= streams
;
896 new_connection
->m_topic
= topic
;
897 sock
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
898 _CLIENT_ONREQUEST_ID
);
899 sock
->SetClientData(new_connection
);
900 sock
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
906 delete new_connection
;
907 // and fall through to delete everything else
912 // Something went wrong, send failure message and delete everything
913 out
.Write8(IPC_FAIL
);
914 } // IPCOutput object is destroyed here, before destroying stream
920 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS