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
11 // Copyright: (c) Julian Smart 1993
12 // (c) Guilhem Lavaux 1997, 1998
13 // (c) 2000 Guillermo Rodriguez <guille@iies.es>
14 // Licence: wxWindows licence
15 /////////////////////////////////////////////////////////////////////////////
17 // ==========================================================================
19 // ==========================================================================
21 // --------------------------------------------------------------------------
23 // --------------------------------------------------------------------------
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
32 #if wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS
34 #include "wx/sckipc.h"
39 #include "wx/module.h"
46 #include "wx/socket.h"
48 // --------------------------------------------------------------------------
49 // macros and constants
50 // --------------------------------------------------------------------------
55 // Message codes (don't change them to avoid breaking the existing code using
63 IPC_ADVISE_REQUEST
= 5,
66 IPC_REQUEST_REPLY
= 8,
73 } // anonymous namespace
75 // headers needed for umask()
77 #include <sys/types.h>
79 #endif // __UNIX_LIKE__
81 // ----------------------------------------------------------------------------
83 // ----------------------------------------------------------------------------
85 // get the address object for the given server name, the caller must delete it
86 static wxSockAddress
*
87 GetAddressFromName(const wxString
& serverName
,
88 const wxString
& host
= wxEmptyString
)
90 // we always use INET sockets under non-Unix systems
91 #if defined(__UNIX__) && !defined(__WINDOWS__) && !defined(__WINE__)
92 // under Unix, if the server name looks like a path, create a AF_UNIX
93 // socket instead of AF_INET one
94 if ( serverName
.Find(wxT('/')) != wxNOT_FOUND
)
96 wxUNIXaddress
*addr
= new wxUNIXaddress
;
97 addr
->Filename(serverName
);
103 wxIPV4address
*addr
= new wxIPV4address
;
104 addr
->Service(serverName
);
107 addr
->Hostname(host
);
114 // --------------------------------------------------------------------------
115 // wxTCPEventHandler stuff (private class)
116 // --------------------------------------------------------------------------
118 class wxTCPEventHandler
: public wxEvtHandler
121 wxTCPEventHandler() : wxEvtHandler() { }
123 void Client_OnRequest(wxSocketEvent
& event
);
124 void Server_OnRequest(wxSocketEvent
& event
);
127 void HandleDisconnect(wxTCPConnection
*connection
);
129 DECLARE_EVENT_TABLE()
130 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandler
);
135 _CLIENT_ONREQUEST_ID
= 1000,
139 // --------------------------------------------------------------------------
140 // wxTCPEventHandlerModule (private class)
141 // --------------------------------------------------------------------------
143 class wxTCPEventHandlerModule
: public wxModule
146 wxTCPEventHandlerModule() : wxModule() { }
148 // get the global wxTCPEventHandler creating it if necessary
149 static wxTCPEventHandler
& GetHandler()
152 ms_handler
= new wxTCPEventHandler
;
157 // as ms_handler is initialized on demand, don't do anything in OnInit()
158 virtual bool OnInit() { return true; }
159 virtual void OnExit() { wxDELETE(ms_handler
); }
162 static wxTCPEventHandler
*ms_handler
;
164 DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
)
165 wxDECLARE_NO_COPY_CLASS(wxTCPEventHandlerModule
);
168 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
)
170 wxTCPEventHandler
*wxTCPEventHandlerModule::ms_handler
= NULL
;
172 // --------------------------------------------------------------------------
173 // wxIPCSocketStreams
174 // --------------------------------------------------------------------------
178 // this class contains the various (related) streams used by wxTCPConnection
179 // and also provides a way to read from the socket stream directly
181 // for writing to the stream use the IPCOutput class below
182 class wxIPCSocketStreams
185 // ctor initializes all the streams on top of the given socket
187 // note that we use a bigger than default buffer size which matches the
188 // typical Ethernet MTU (minus TCP header overhead)
189 wxIPCSocketStreams(wxSocketBase
& sock
)
190 : m_socketStream(sock
),
192 m_bufferedOut(m_socketStream
, 1448),
194 m_bufferedOut(m_socketStream
),
196 m_dataIn(m_socketStream
),
197 m_dataOut(m_bufferedOut
)
201 // expose the IO methods needed by IPC code (notice that writing is only
202 // done via IPCOutput)
208 m_bufferedOut
.Sync();
212 // simple wrappers around the functions with the same name in
217 return m_dataIn
.Read8();
223 return m_dataIn
.Read32();
226 wxString
ReadString()
229 return m_dataIn
.ReadString();
232 // read arbitrary (size-prepended) data
234 // connection parameter is needed to call its GetBufferAtLeast() method
235 void *ReadData(wxConnectionBase
*conn
, size_t *size
)
239 wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" );
240 wxCHECK_MSG( size
, NULL
, "NULL size parameter" );
244 void * const data
= conn
->GetBufferAtLeast(*size
);
245 wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" );
247 m_socketStream
.Read(data
, *size
);
252 // same as above but for data preceded by the format
254 ReadFormatData(wxConnectionBase
*conn
, wxIPCFormat
*format
, size_t *size
)
256 wxCHECK_MSG( format
, NULL
, "NULL format parameter" );
258 *format
= static_cast<wxIPCFormat
>(Read8());
260 return ReadData(conn
, size
);
264 // these methods are only used by IPCOutput and not directly
265 wxDataOutputStream
& GetDataOut() { return m_dataOut
; }
266 wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; }
269 // this is the low-level underlying stream using the connection socket
270 wxSocketStream m_socketStream
;
272 // the buffered stream is used to avoid writing all pieces of an IPC
273 // request to the socket one by one but to instead do it all at once when
274 // we're done with it
276 wxBufferedOutputStream m_bufferedOut
;
278 wxOutputStream
& m_bufferedOut
;
281 // finally the data streams are used to be able to write typed data into
282 // the above streams easily
283 wxDataInputStream m_dataIn
;
284 wxDataOutputStream m_dataOut
;
286 wxDECLARE_NO_COPY_CLASS(wxIPCSocketStreams
);
292 // an object of this class should be instantiated on the stack to write to the
293 // underlying socket stream
295 // this class is intentionally separated from wxIPCSocketStreams to ensure that
296 // Flush() is always called
300 // construct an object associated with the given streams (which must have
301 // life time greater than ours as we keep a reference to it)
302 IPCOutput(wxIPCSocketStreams
*streams
)
303 : m_streams(*streams
)
305 wxASSERT_MSG( streams
, "NULL streams pointer" );
308 // dtor calls Flush() really sending the IPC data to the network
309 ~IPCOutput() { m_streams
.Flush(); }
313 void Write8(wxUint8 i
)
315 m_streams
.GetDataOut().Write8(i
);
318 // write the reply code and a string
319 void Write(IPCCode code
, const wxString
& str
)
322 m_streams
.GetDataOut().WriteString(str
);
325 // write the reply code, a string and a format in this order
326 void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
)
332 // write arbitrary data
333 void WriteData(const void *data
, size_t size
)
335 m_streams
.GetDataOut().Write32(size
);
336 m_streams
.GetUnformattedOut().Write(data
, size
);
341 wxIPCSocketStreams
& m_streams
;
343 wxDECLARE_NO_COPY_CLASS(IPCOutput
);
346 } // anonymous namespace
348 // ==========================================================================
350 // ==========================================================================
352 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
)
353 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
)
354 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
)
356 // --------------------------------------------------------------------------
358 // --------------------------------------------------------------------------
360 wxTCPClient::wxTCPClient()
365 bool wxTCPClient::ValidHost(const wxString
& host
)
369 return addr
.Hostname(host
);
372 wxConnectionBase
*wxTCPClient::MakeConnection(const wxString
& host
,
373 const wxString
& serverName
,
374 const wxString
& topic
)
376 wxSockAddress
*addr
= GetAddressFromName(serverName
, host
);
380 wxSocketClient
* const client
= new wxSocketClient(wxSOCKET_WAITALL
);
381 wxIPCSocketStreams
* const streams
= new wxIPCSocketStreams(*client
);
383 bool ok
= client
->Connect(*addr
);
388 // Send topic name, and enquire whether this has succeeded
389 IPCOutput(streams
).Write(IPC_CONNECT
, topic
);
391 unsigned char msg
= streams
->Read8();
394 if (msg
== IPC_CONNECT
)
397 connection
= (wxTCPConnection
*)OnMakeConnection ();
401 if (wxDynamicCast(connection
, wxTCPConnection
))
403 connection
->m_topic
= topic
;
404 connection
->m_sock
= client
;
405 connection
->m_streams
= streams
;
406 client
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
407 _CLIENT_ONREQUEST_ID
);
408 client
->SetClientData(connection
);
409 client
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
410 client
->Notify(true);
416 // and fall through to delete everything else
422 // Something went wrong, delete everything
429 wxConnectionBase
*wxTCPClient::OnMakeConnection()
431 return new wxTCPConnection();
434 // --------------------------------------------------------------------------
436 // --------------------------------------------------------------------------
438 wxTCPServer::wxTCPServer()
444 bool wxTCPServer::Create(const wxString
& serverName
)
446 // Destroy previous server, if any
449 m_server
->SetClientData(NULL
);
454 wxSockAddress
*addr
= GetAddressFromName(serverName
);
460 if ( addr
->Type() == wxSockAddress::UNIX
)
462 // ensure that the file doesn't exist as otherwise calling socket()
464 int rc
= remove(serverName
.fn_str());
465 if ( rc
< 0 && errno
!= ENOENT
)
472 // also set the umask to prevent the others from reading our file
473 umaskOld
= umask(077);
477 // unused anyhow but shut down the compiler warnings
480 #endif // __UNIX_LIKE__
482 // Create a socket listening on the specified port (reusing it to allow
483 // restarting the server listening on the same port as was used by the
484 // previous instance of this server)
485 m_server
= new wxSocketServer(*addr
, wxSOCKET_WAITALL
| wxSOCKET_REUSEADDR
);
488 if ( addr
->Type() == wxSockAddress::UNIX
)
493 // save the file name to remove it later
494 m_filename
= serverName
;
496 #endif // __UNIX_LIKE__
500 if (!m_server
->IsOk())
508 m_server
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
509 _SERVER_ONREQUEST_ID
);
510 m_server
->SetClientData(this);
511 m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
512 m_server
->Notify(true);
517 wxTCPServer::~wxTCPServer()
521 m_server
->SetClientData(NULL
);
526 if ( !m_filename
.empty() )
528 if ( remove(m_filename
.fn_str()) != 0 )
530 wxLogDebug(wxT("Stale AF_UNIX file '%s' left."), m_filename
.c_str());
533 #endif // __UNIX_LIKE__
537 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
))
539 return new wxTCPConnection();
542 // --------------------------------------------------------------------------
544 // --------------------------------------------------------------------------
546 void wxTCPConnection::Init()
552 wxTCPConnection::~wxTCPConnection()
558 m_sock
->SetClientData(NULL
);
565 void wxTCPConnection::Compress(bool WXUNUSED(on
))
570 // Calls that CLIENT can make.
571 bool wxTCPConnection::Disconnect()
573 if ( !GetConnected() )
576 // Send the disconnect message to the peer.
577 IPCOutput(m_streams
).Write8(IPC_DISCONNECT
);
581 m_sock
->Notify(false);
590 bool wxTCPConnection::DoExecute(const void *data
,
594 if ( !m_sock
->IsConnected() )
597 // Prepare EXECUTE message
598 IPCOutput
out(m_streams
);
599 out
.Write8(IPC_EXECUTE
);
602 out
.WriteData(data
, size
);
607 const void *wxTCPConnection::Request(const wxString
& item
,
611 if ( !m_sock
->IsConnected() )
614 IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
);
616 const int ret
= m_streams
->Read8();
617 if ( ret
!= IPC_REQUEST_REPLY
)
620 // ReadData() needs a non-NULL size pointer but the client code can call us
621 // with NULL pointer (this makes sense if it knows that it always works
622 // with NUL-terminated strings)
624 return m_streams
->ReadData(this, size
? size
: &sizeFallback
);
627 bool wxTCPConnection::DoPoke(const wxString
& item
,
632 if ( !m_sock
->IsConnected() )
635 IPCOutput
out(m_streams
);
636 out
.Write(IPC_POKE
, item
, format
);
637 out
.WriteData(data
, size
);
642 bool wxTCPConnection::StartAdvise(const wxString
& item
)
644 if ( !m_sock
->IsConnected() )
647 IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
);
649 const int ret
= m_streams
->Read8();
651 return ret
== IPC_ADVISE_START
;
654 bool wxTCPConnection::StopAdvise (const wxString
& item
)
656 if ( !m_sock
->IsConnected() )
659 IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
);
661 const int ret
= m_streams
->Read8();
663 return ret
== IPC_ADVISE_STOP
;
666 // Calls that SERVER can make
667 bool wxTCPConnection::DoAdvise(const wxString
& item
,
672 if ( !m_sock
->IsConnected() )
675 IPCOutput
out(m_streams
);
676 out
.Write(IPC_ADVISE
, item
, format
);
677 out
.WriteData(data
, size
);
682 // --------------------------------------------------------------------------
683 // wxTCPEventHandler (private class)
684 // --------------------------------------------------------------------------
686 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
)
687 EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
)
688 EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
)
691 void wxTCPEventHandler::HandleDisconnect(wxTCPConnection
*connection
)
693 // connection was closed (either gracefully or not): destroy everything
694 connection
->m_sock
->Notify(false);
695 connection
->m_sock
->Close();
697 // don't leave references to this soon-to-be-dangling connection in the
698 // socket as it won't be destroyed immediately as its destruction will be
699 // delayed in case there are more events pending for it
700 connection
->m_sock
->SetClientData(NULL
);
702 connection
->SetConnected(false);
703 connection
->OnDisconnect();
706 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent
&event
)
708 wxSocketBase
*sock
= event
.GetSocket();
712 wxSocketNotify evt
= event
.GetSocketEvent();
713 wxTCPConnection
* const
714 connection
= static_cast<wxTCPConnection
*>(sock
->GetClientData());
716 // This socket is being deleted; skip this event
720 if ( evt
== wxSOCKET_LOST
)
722 HandleDisconnect(connection
);
726 // Receive message number.
727 wxIPCSocketStreams
* const streams
= connection
->m_streams
;
729 const wxString topic
= connection
->m_topic
;
734 const int msg
= streams
->Read8();
740 size_t size
wxDUMMY_INITIALIZE(0);
742 data
= streams
->ReadFormatData(connection
, &format
, &size
);
744 connection
->OnExecute(topic
, data
, size
, format
);
752 item
= streams
->ReadString();
755 size_t size
wxDUMMY_INITIALIZE(0);
757 data
= streams
->ReadFormatData(connection
, &format
, &size
);
760 connection
->OnAdvise(topic
, item
, data
, size
, format
);
766 case IPC_ADVISE_START
:
768 item
= streams
->ReadString();
770 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
)
776 case IPC_ADVISE_STOP
:
778 item
= streams
->ReadString();
780 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
)
788 item
= streams
->ReadString();
789 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
791 size_t size
wxDUMMY_INITIALIZE(0);
792 void * const data
= streams
->ReadData(connection
, &size
);
795 connection
->OnPoke(topic
, item
, data
, size
, format
);
803 item
= streams
->ReadString();
805 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
807 size_t user_size
= wxNO_LEN
;
808 const void *user_data
= connection
->OnRequest(topic
,
815 IPCOutput(streams
).Write8(IPC_FAIL
);
819 IPCOutput
out(streams
);
820 out
.Write8(IPC_REQUEST_REPLY
);
822 if ( user_size
== wxNO_LEN
)
828 user_size
= strlen((const char *)user_data
) + 1; // includes final NUL
830 case wxIPC_UNICODETEXT
:
831 user_size
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t); // includes final NUL
838 out
.WriteData(user_data
, user_size
);
843 HandleDisconnect(connection
);
847 wxLogDebug("Unexpected IPC_FAIL received");
852 wxLogDebug("Unknown message code %d received.", msg
);
858 IPCOutput(streams
).Write8(IPC_FAIL
);
861 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent
&event
)
863 wxSocketServer
*server
= (wxSocketServer
*) event
.GetSocket();
866 wxTCPServer
*ipcserv
= (wxTCPServer
*) server
->GetClientData();
868 // This socket is being deleted; skip this event
872 if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
)
875 // Accept the connection, getting a new socket
876 wxSocketBase
*sock
= server
->Accept();
885 wxIPCSocketStreams
*streams
= new wxIPCSocketStreams(*sock
);
888 IPCOutput
out(streams
);
890 const int msg
= streams
->Read8();
891 if ( msg
== IPC_CONNECT
)
893 const wxString topic
= streams
->ReadString();
895 wxTCPConnection
*new_connection
=
896 (wxTCPConnection
*)ipcserv
->OnAcceptConnection (topic
);
900 if (wxDynamicCast(new_connection
, wxTCPConnection
))
902 // Acknowledge success
903 out
.Write8(IPC_CONNECT
);
905 new_connection
->m_sock
= sock
;
906 new_connection
->m_streams
= streams
;
907 new_connection
->m_topic
= topic
;
908 sock
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(),
909 _CLIENT_ONREQUEST_ID
);
910 sock
->SetClientData(new_connection
);
911 sock
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
917 delete new_connection
;
918 // and fall through to delete everything else
923 // Something went wrong, send failure message and delete everything
924 out
.Write8(IPC_FAIL
);
925 } // IPCOutput object is destroyed here, before destroying stream
931 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS