wxWidgets/samples/sockets/client.cpp
Vadim Zeitlin 18f42b94df Remove calls to wxApp::SetTopWindow() from the samples and documentation.
It is definitely not necessary to call SetTopWindow() when there is only a
single top level window and it is arguable whether it's useful to do it even
when there are many of them so don't encourage its use in the documentation
and also remove all its occurrences from the samples.

Closes #12816.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66528 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2011-01-02 22:05:14 +00:00

691 lines
19 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: client.cpp
// Purpose: Client for wxSocket demo
// Author: Guillermo Rodriguez Garcia <guille@iies.es>
// Modified by:
// Created: 1999/09/19
// RCS-ID: $Id$
// Copyright: (c) 1999 Guillermo Rodriguez Garcia
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ==========================================================================
// declarations
// ==========================================================================
// --------------------------------------------------------------------------
// headers
// --------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
# pragma hdrstop
#endif
// for all others, include the necessary headers
#ifndef WX_PRECOMP
# include "wx/wx.h"
#endif
#include "wx/socket.h"
#include "wx/url.h"
#include "wx/sstream.h"
#include <memory>
// --------------------------------------------------------------------------
// resources
// --------------------------------------------------------------------------
// the application icon
#if !defined(__WXMSW__) && !defined(__WXPM__)
#include "../sample.xpm"
#endif
// --------------------------------------------------------------------------
// classes
// --------------------------------------------------------------------------
// Define a new application type
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
MyFrame();
~MyFrame();
// event handlers for File menu
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
// event handlers for Socket menu
void OnOpenConnection(wxCommandEvent& event);
void OnTest1(wxCommandEvent& event);
void OnTest2(wxCommandEvent& event);
void OnTest3(wxCommandEvent& event);
void OnCloseConnection(wxCommandEvent& event);
#if wxUSE_URL
// event handlers for Protocols menu
void OnTestURL(wxCommandEvent& event);
#endif
#if wxUSE_IPV6
void OnOpenConnectionIPv6(wxCommandEvent& event);
#endif
void OpenConnection(wxSockAddress::Family family);
// event handlers for DatagramSocket menu (stub)
void OnDatagram(wxCommandEvent& event);
// socket event handler
void OnSocketEvent(wxSocketEvent& event);
// convenience functions
void UpdateStatusBar();
private:
wxSocketClient *m_sock;
wxTextCtrl *m_text;
wxMenu *m_menuFile;
wxMenu *m_menuSocket;
wxMenu *m_menuDatagramSocket;
wxMenu *m_menuProtocols;
wxMenuBar *m_menuBar;
bool m_busy;
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
};
// simple helper class to log start and end of each test
class TestLogger
{
public:
TestLogger(const wxString& name) : m_name(name)
{
wxLogMessage("=== %s test begins ===", m_name);
}
~TestLogger()
{
wxLogMessage("=== %s test ends ===", m_name);
}
private:
const wxString m_name;
};
// --------------------------------------------------------------------------
// constants
// --------------------------------------------------------------------------
// IDs for the controls and the menu commands
enum
{
// menu items
CLIENT_QUIT = wxID_EXIT,
CLIENT_ABOUT = wxID_ABOUT,
CLIENT_OPEN = 100,
#if wxUSE_IPV6
CLIENT_OPENIPV6,
#endif
CLIENT_TEST1,
CLIENT_TEST2,
CLIENT_TEST3,
CLIENT_CLOSE,
#if wxUSE_URL
CLIENT_TESTURL,
#endif
CLIENT_DGRAM,
// id for socket
SOCKET_ID
};
// --------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// --------------------------------------------------------------------------
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(CLIENT_QUIT, MyFrame::OnQuit)
EVT_MENU(CLIENT_ABOUT, MyFrame::OnAbout)
EVT_MENU(CLIENT_OPEN, MyFrame::OnOpenConnection)
#if wxUSE_IPV6
EVT_MENU(CLIENT_OPENIPV6, MyFrame::OnOpenConnectionIPv6)
#endif
EVT_MENU(CLIENT_TEST1, MyFrame::OnTest1)
EVT_MENU(CLIENT_TEST2, MyFrame::OnTest2)
EVT_MENU(CLIENT_TEST3, MyFrame::OnTest3)
EVT_MENU(CLIENT_CLOSE, MyFrame::OnCloseConnection)
EVT_MENU(CLIENT_DGRAM, MyFrame::OnDatagram)
#if wxUSE_URL
EVT_MENU(CLIENT_TESTURL, MyFrame::OnTestURL)
#endif
EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
END_EVENT_TABLE()
IMPLEMENT_APP(MyApp)
// ==========================================================================
// implementation
// ==========================================================================
// --------------------------------------------------------------------------
// the application class
// --------------------------------------------------------------------------
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
// Create the main application window
MyFrame *frame = new MyFrame();
// Show it
frame->Show(true);
// success
return true;
}
// --------------------------------------------------------------------------
// main frame
// --------------------------------------------------------------------------
// frame constructor
MyFrame::MyFrame() : wxFrame((wxFrame *)NULL, wxID_ANY,
_("wxSocket demo: Client"),
wxDefaultPosition, wxSize(300, 200))
{
// Give the frame an icon
SetIcon(wxICON(sample));
// Make menus
m_menuFile = new wxMenu();
m_menuFile->Append(CLIENT_ABOUT, _("&About...\tCtrl-A"), _("Show about dialog"));
m_menuFile->AppendSeparator();
m_menuFile->Append(CLIENT_QUIT, _("E&xit\tAlt-X"), _("Quit client"));
m_menuSocket = new wxMenu();
m_menuSocket->Append(CLIENT_OPEN, _("&Open session\tCtrl-O"), _("Connect to server"));
#if wxUSE_IPV6
m_menuSocket->Append(CLIENT_OPENIPV6, _("&Open session(IPv6)\tShift-Ctrl-O"), _("Connect to server(IPv6)"));
#endif
m_menuSocket->AppendSeparator();
m_menuSocket->Append(CLIENT_TEST1, _("Test &1\tCtrl-F1"), _("Test basic functionality"));
m_menuSocket->Append(CLIENT_TEST2, _("Test &2\tCtrl-F2"), _("Test ReadMsg and WriteMsg"));
m_menuSocket->Append(CLIENT_TEST3, _("Test &3\tCtrl-F3"), _("Test large data transfer"));
m_menuSocket->AppendSeparator();
m_menuSocket->Append(CLIENT_CLOSE, _("&Close session\tCtrl-Q"), _("Close connection"));
m_menuDatagramSocket = new wxMenu();
m_menuDatagramSocket->Append(CLIENT_DGRAM, _("&Datagram test\tCtrl-D"), _("Test UDP sockets"));
#if wxUSE_URL
m_menuProtocols = new wxMenu();
m_menuProtocols->Append(CLIENT_TESTURL, _("Test URL\tCtrl-U"),
_("Get data from the specified URL"));
#endif
// Append menus to the menubar
m_menuBar = new wxMenuBar();
m_menuBar->Append(m_menuFile, _("&File"));
m_menuBar->Append(m_menuSocket, _("&TCP"));
m_menuBar->Append(m_menuDatagramSocket, _("&UDP"));
#if wxUSE_URL
m_menuBar->Append(m_menuProtocols, _("&Protocols"));
#endif
SetMenuBar(m_menuBar);
#if wxUSE_STATUSBAR
// Status bar
CreateStatusBar(2);
#endif // wxUSE_STATUSBAR
// Make a textctrl for logging
m_text = new wxTextCtrl(this, wxID_ANY,
_("Welcome to wxSocket demo: Client\nClient ready\n"),
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY);
delete wxLog::SetActiveTarget(new wxLogTextCtrl(m_text));
// Create the socket
m_sock = new wxSocketClient();
// Setup the event handler and subscribe to most events
m_sock->SetEventHandler(*this, SOCKET_ID);
m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG |
wxSOCKET_INPUT_FLAG |
wxSOCKET_LOST_FLAG);
m_sock->Notify(true);
m_busy = false;
UpdateStatusBar();
}
MyFrame::~MyFrame()
{
// No delayed deletion here, as the frame is dying anyway
delete m_sock;
}
// event handlers
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
// true is to force the frame to close
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox(_("wxSocket demo: Client\n(c) 1999 Guillermo Rodriguez Garcia\n"),
_("About Client"),
wxOK | wxICON_INFORMATION, this);
}
void MyFrame::OnOpenConnection(wxCommandEvent& WXUNUSED(event))
{
OpenConnection(wxSockAddress::IPV4);
}
#if wxUSE_IPV6
void MyFrame::OnOpenConnectionIPv6(wxCommandEvent& WXUNUSED(event))
{
OpenConnection(wxSockAddress::IPV6);
}
#endif // wxUSE_IPV6
void MyFrame::OpenConnection(wxSockAddress::Family family)
{
wxUnusedVar(family); // unused in !wxUSE_IPV6 case
wxIPaddress * addr;
wxIPV4address addr4;
#if wxUSE_IPV6
wxIPV6address addr6;
if ( family == wxSockAddress::IPV6 )
addr = &addr6;
else
#endif
addr = &addr4;
m_menuSocket->Enable(CLIENT_OPEN, false);
#if wxUSE_IPV6
m_menuSocket->Enable(CLIENT_OPENIPV6, false);
#endif
m_menuSocket->Enable(CLIENT_CLOSE, false);
// Ask user for server address
wxString hostname = wxGetTextFromUser(
_("Enter the address of the wxSocket demo server:"),
_("Connect ..."),
_("localhost"));
if ( hostname.empty() )
return;
addr->Hostname(hostname);
addr->Service(3000);
// we connect asynchronously and will get a wxSOCKET_CONNECTION event when
// the connection is really established
//
// if you want to make sure that connection is established right here you
// could call WaitOnConnect(timeout) instead
wxLogMessage("Trying to connect to %s:%d", hostname, addr->Service());
m_sock->Connect(*addr, false);
}
void MyFrame::OnTest1(wxCommandEvent& WXUNUSED(event))
{
// Disable socket menu entries (exception: Close Session)
m_busy = true;
UpdateStatusBar();
m_text->AppendText(_("\n=== Test 1 begins ===\n"));
// Tell the server which test we are running
unsigned char c = 0xBE;
m_sock->Write(&c, 1);
// Send some data and read it back. We know the size of the
// buffer, so we can specify the exact number of bytes to be
// sent or received and use the wxSOCKET_WAITALL flag. Also,
// we have disabled menu entries which could interfere with
// the test, so we can safely avoid the wxSOCKET_BLOCK flag.
//
// First we send a byte with the length of the string, then
// we send the string itself (do NOT try to send any integral
// value larger than a byte "as is" across the network, or
// you might be in trouble! Ever heard about big and little
// endian computers?)
m_sock->SetFlags(wxSOCKET_WAITALL);
const char *buf1 = "Test string (less than 256 chars!)";
unsigned char len = (unsigned char)(wxStrlen(buf1) + 1);
wxCharBuffer buf2(wxStrlen(buf1));
m_text->AppendText(_("Sending a test buffer to the server ..."));
m_sock->Write(&len, 1);
m_sock->Write(buf1, len);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Receiving the buffer back from server ..."));
m_sock->Read(buf2.data(), len);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Comparing the two buffers ..."));
if (memcmp(buf1, buf2, len) != 0)
{
m_text->AppendText(_("failed!\n"));
m_text->AppendText(_("Test 1 failed !\n"));
}
else
{
m_text->AppendText(_("done\n"));
m_text->AppendText(_("Test 1 passed !\n"));
}
m_text->AppendText(_("=== Test 1 ends ===\n"));
m_busy = false;
UpdateStatusBar();
}
void MyFrame::OnTest2(wxCommandEvent& WXUNUSED(event))
{
// Disable socket menu entries (exception: Close Session)
m_busy = true;
UpdateStatusBar();
m_text->AppendText(_("\n=== Test 2 begins ===\n"));
// Tell the server which test we are running
unsigned char c = 0xCE;
m_sock->Write(&c, 1);
// Here we use ReadMsg and WriteMsg to send messages with
// a header with size information. Also, the reception is
// event triggered, so we test input events as well.
//
// We need to set no flags here (ReadMsg and WriteMsg are
// not affected by flags)
m_sock->SetFlags(wxSOCKET_WAITALL);
wxString s = wxGetTextFromUser(
_("Enter an arbitrary string to send to the server:"),
_("Test 2 ..."),
_("Yes I like wxWidgets!"));
const wxScopedCharBuffer msg1(s.utf8_str());
size_t len = wxStrlen(msg1) + 1;
wxCharBuffer msg2(wxStrlen(msg1));
m_text->AppendText(_("Sending the string with WriteMsg ..."));
m_sock->WriteMsg(msg1, len);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Waiting for an event (timeout = 2 sec)\n"));
// Wait until data available (will also return if the connection is lost)
m_sock->WaitForRead(2);
if (m_sock->IsData())
{
m_text->AppendText(_("Reading the string back with ReadMsg ..."));
m_sock->ReadMsg(msg2.data(), len);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Comparing the two buffers ..."));
if (memcmp(msg1, msg2, len) != 0)
{
m_text->AppendText(_("failed!\n"));
m_text->AppendText(_("Test 2 failed !\n"));
}
else
{
m_text->AppendText(_("done\n"));
m_text->AppendText(_("Test 2 passed !\n"));
}
}
else
m_text->AppendText(_("Timeout ! Test 2 failed.\n"));
m_text->AppendText(_("=== Test 2 ends ===\n"));
m_busy = false;
UpdateStatusBar();
}
void MyFrame::OnTest3(wxCommandEvent& WXUNUSED(event))
{
// Disable socket menu entries (exception: Close Session)
m_busy = true;
UpdateStatusBar();
m_text->AppendText(_("\n=== Test 3 begins ===\n"));
// Tell the server which test we are running
unsigned char c = 0xDE;
m_sock->Write(&c, 1);
// This test also is similar to the first one but it sends a
// large buffer so that wxSocket is actually forced to split
// it into pieces and take care of sending everything before
// returning.
m_sock->SetFlags(wxSOCKET_WAITALL);
// Note that len is in kbytes here!
const unsigned char len = 32;
wxCharBuffer buf1(len * 1024),
buf2(len * 1024);
for (size_t i = 0; i < len * 1024; i ++)
buf1.data()[i] = (char)(i % 256);
m_text->AppendText(_("Sending a large buffer (32K) to the server ..."));
m_sock->Write(&len, 1);
m_sock->Write(buf1, len * 1024);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Receiving the buffer back from server ..."));
m_sock->Read(buf2.data(), len * 1024);
m_text->AppendText(m_sock->Error() ? _("failed !\n") : _("done\n"));
m_text->AppendText(_("Comparing the two buffers ..."));
if (memcmp(buf1, buf2, len) != 0)
{
m_text->AppendText(_("failed!\n"));
m_text->AppendText(_("Test 3 failed !\n"));
}
else
{
m_text->AppendText(_("done\n"));
m_text->AppendText(_("Test 3 passed !\n"));
}
m_text->AppendText(_("=== Test 3 ends ===\n"));
m_busy = false;
UpdateStatusBar();
}
void MyFrame::OnCloseConnection(wxCommandEvent& WXUNUSED(event))
{
m_sock->Close();
UpdateStatusBar();
}
void MyFrame::OnDatagram(wxCommandEvent& WXUNUSED(event))
{
wxString hostname = wxGetTextFromUser
(
"Enter the address of the wxSocket demo server:",
"UDP peer",
"localhost"
);
if ( hostname.empty() )
return;
TestLogger logtest("UDP");
wxIPV4address addrLocal;
addrLocal.Hostname();
wxDatagramSocket sock(addrLocal);
if ( !sock.IsOk() )
{
wxLogMessage("ERROR: failed to create UDP socket");
return;
}
wxLogMessage("Created UDP socket at %s:%u",
addrLocal.IPAddress(), addrLocal.Service());
wxIPV4address addrPeer;
addrPeer.Hostname(hostname);
addrPeer.Service(3000);
wxLogMessage("Testing UDP with peer at %s:%u",
addrPeer.IPAddress(), addrPeer.Service());
char buf[] = "Uryyb sebz pyvrag!";
if ( sock.SendTo(addrPeer, buf, sizeof(buf)).LastCount() != sizeof(buf) )
{
wxLogMessage("ERROR: failed to send data");
return;
}
if ( sock.RecvFrom(addrPeer, buf, sizeof(buf)).LastCount() != sizeof(buf) )
{
wxLogMessage("ERROR: failed to receive data");
return;
}
wxLogMessage("Received \"%s\" from %s:%u.",
wxString::From8BitData(buf, sock.LastCount()),
addrPeer.IPAddress(), addrPeer.Service());
}
#if wxUSE_URL
void MyFrame::OnTestURL(wxCommandEvent& WXUNUSED(event))
{
// Ask for the URL
static wxString s_urlname("http://www.google.com/");
wxString urlname = wxGetTextFromUser
(
_("Enter an URL to get"),
_("URL:"),
s_urlname
);
if ( urlname.empty() )
return; // cancelled by user
s_urlname = urlname;
TestLogger logtest("URL");
// Parse the URL
wxURL url(urlname);
if ( url.GetError() != wxURL_NOERR )
{
wxLogError("Failed to parse URL \"%s\"", urlname);
return;
}
// Try to get the input stream (connects to the given URL)
wxLogMessage("Establishing connection to \"%s\"...", urlname);
const std::auto_ptr<wxInputStream> data(url.GetInputStream());
if ( !data.get() )
{
wxLogError("Failed to retrieve URL \"%s\"", urlname);
return;
}
// Print the contents type and file size
wxLogMessage("Contents type: %s\nFile size: %i\nStarting to download...",
url.GetProtocol().GetContentType(),
data->GetSize());
// Get the data
wxStringOutputStream sout;
if ( data->Read(sout).GetLastError() != wxSTREAM_EOF )
{
wxLogError("Error reading the input stream.");
}
wxLogMessage("Text retrieved from URL \"%s\" follows:\n%s",
urlname, sout.GetString());
}
#endif // wxUSE_URL
void MyFrame::OnSocketEvent(wxSocketEvent& event)
{
switch ( event.GetSocketEvent() )
{
case wxSOCKET_INPUT:
wxLogMessage("Input available on the socket");
break;
case wxSOCKET_LOST:
wxLogMessage("Socket connection was unexpectedly lost.");
UpdateStatusBar();
break;
case wxSOCKET_CONNECTION:
wxLogMessage("... socket is now connected.");
UpdateStatusBar();
break;
default:
wxLogMessage("Unknown socket event!!!");
break;
}
}
// convenience functions
void MyFrame::UpdateStatusBar()
{
#if wxUSE_STATUSBAR
wxString s;
if (!m_sock->IsConnected())
{
s = "Not connected";
}
else
{
#if wxUSE_IPV6
wxIPV6address addr;
#else
wxIPV4address addr;
#endif
m_sock->GetPeer(addr);
s.Printf("%s : %d", addr.Hostname(), addr.Service());
}
SetStatusText(s, 1);
#endif // wxUSE_STATUSBAR
m_menuSocket->Enable(CLIENT_OPEN, !m_sock->IsConnected() && !m_busy);
#if wxUSE_IPV6
m_menuSocket->Enable(CLIENT_OPENIPV6, !m_sock->IsConnected() && !m_busy);
#endif
m_menuSocket->Enable(CLIENT_TEST1, m_sock->IsConnected() && !m_busy);
m_menuSocket->Enable(CLIENT_TEST2, m_sock->IsConnected() && !m_busy);
m_menuSocket->Enable(CLIENT_TEST3, m_sock->IsConnected() && !m_busy);
m_menuSocket->Enable(CLIENT_CLOSE, m_sock->IsConnected());
}