|
|
Introduction to Sockets and Basic Socket Processing
As an introduction to socket programming with wxWidgets, let's jump right in to an
The program
Figure 18-1. Socket server and client programs
The ClientThis is the code for the client program.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(CLIENT_CONNECT, MyFrame::OnConnectToServer)
EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
END_EVENT_TABLE()
void MyFrame::OnConnectToServer(wxCommandEvent& WXUNUSED(event))
{
wxIPV4address addr;
addr.Hostname(wxT("localhost"));
addr.Service(3000);
// Create the socket
wxSocketClient* Socket = new wxSocketClient();
// Set up the event handler and subscribe to most events
Socket->SetEventHandler(*this, SOCKET_ID);
Socket->SetNotify(wxSOCKET_CONNECTION_FLAG
wxSOCKET_INPUT_FLAG
wxSOCKET_LOST_FLAG);
Socket->Notify(true);
// Wait for the connection event
Socket->Connect(addr, false);
}
void MyFrame::OnSocketEvent(wxSocketEvent& event)
{
// The socket that had the event
wxSocketBase* sock = event.GetSocket();
// Common buffer shared by the events
char buf[10];
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
// Fill the arry with the numbers 0 through 9
// as characters
char mychar = '0';
for (int i = 0; i < 10; i++)
{
buf[i] = mychar++;
}
// Send the characters to the server
sock->Write(buf, sizeof(buf));
break;
}
case wxSOCKET_INPUT:
{
sock->Read(buf, sizeof(buf));
break;
}
// The server hangs up after sending the data
case wxSOCKET_LOST:
{
sock->Destroy();
break;
}
}
}
The ServerThis is the code for the server program.
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(SERVER_START, MyFrame::OnServerStart)
EVT_SOCKET(SERVER_ID, MyFrame::OnServerEvent)
EVT_SOCKET(SOCKET_ID, MyFrame::OnSocketEvent)
END_EVENT_TABLE()
void MyFrame::OnServerStart(wxCommandEvent& WXUNUSED(event))
{
// Create the address - defaults to localhost:0 initially
wxIPV4address addr;
addr.Service(3000);
// Create the socket. We maintain a class pointer so we can
// shut it down
m_server = new wxSocketServer(addr);
// We use Ok() here to see if the server is really listening
if (! m_server->Ok())
{
return;
}
// Set up the event handler and subscribe to connection events
m_server->SetEventHandler(*this, SERVER_ID);
m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);
m_server->Notify(true);
}
void MyFrame::OnServerEvent(wxSocketEvent& WXUNUSED(event))
{
// Accept the new connection and get the socket pointer
wxSocketBase* sock = m_server->Accept(false);
// Tell the new socket how and where to process its events
sock->SetEventHandler(*this, SOCKET_ID);
sock->SetNotify(wxSOCKET_INPUT_FLAG wxSOCKET_LOST_FLAG);
sock->Notify(true);
}
void MyFrame::OnSocketEvent(wxSocketEvent& event)
{
wxSocketBase *sock = event.GetSocket();
// Process the event
switch(event.GetSocketEvent())
{
case wxSOCKET_INPUT:
{
char buf[10];
// Read the data
sock->Read(buf, sizeof(buf));
// Write it back
sock->Write(buf, sizeof(buf));
// We are done with the socket, destroy it
sock->Destroy();
break;
}
case wxSOCKET_LOST:
{
sock->Destroy();
break;
}
}
}
Connecting to a ServerThis section explains how to initiate a client connection to a server using the wxSockAddress and wxSocketClient classes. Socket Addresses
All socket address classes derive from the abstract base class
wxSockAddress
, providing a common parameter type for socket
Note: When representing addresses as unsigned longs, network order is expected, and network order is always returned . Network order corresponds to big endian (Intel or AMD x86 architecture is little endian; Apple's architecture is big endian). Depending on how the unsigned long addresses are stored or entered, you can probably use the byte-order macro wxINT32_SWAP_ON_LE, which will swap the byte order only on little endian platforms. For example: IPV4addr.Hostname(wxINT32_SWAP_ON_LE(longAddress));
Hostname
takes either a
wxString
for a string address (for example,
www. wxwidgets.org
) or an IP address in 4-byte unsigned long format (in big endian, as noted previously). Without any parameters,
Hostname
returns the
Service
sets the remote port, using either a
wxString
description for a well-known port or an unsigned short for any port. Without any parameters,
Service
returns the port number currently
IPAddress
provides a
AnyAddress sets the address to any of the addresses of the current machine. This is the same as setting an address to INADDR_ANY . Socket ClientsThe wxSocketClient class derives from wxSocketBase and inherits all of the common socket methods. The only methods added to the client class are those necessary to initiate and establish a connection to a remote server. Connect takes a wxSockAddress parameter telling the socket client the address and port for the connection. As mentioned earlier, you would use a class such as wxIPV4address rather than wxSockAddress directly. The second parameter, a boolean, defaults to true , indicating that the call to Connect should block until the connection is established. If this is done from the main GUI thread, the GUI will block while connecting.
WaitOnConnect
can be used after a call to
Connect
if
Connect
was told
not
to block. The first parameter is the number of seconds to wait, and the second parameter is the number of
Socket EventsAll socket events are filtered through one event, EVT_SOCKET . EVT_SOCKET(identifier, function) sends socket events for the socket identifier to the specified function. The function should take a wxSocketEvent parameter. The wxSocketEvent class is by itself very simple, but by providing both the event type and the socket for which the event was generated, the need to manually store socket pointers is reduced. Socket Event TypesTable 18-1 lists the event types that are returned from GetSocketEvent . Table 18-1. Socket Event Types
wxSocketEvent Major Member FunctionswxSocketEvent is used as a parameter to socket event handlers. GetSocket returns a wxSocketBase pointer to the socket that generated this event. GetSocketEvent returns the event type of this socket event, as per Table 18-1. Using Socket EventsIn order to use socket events, you must provide an event handler and specify which events you want to receive for processing. The wxSocketBase class gives you several methods for using events, which you can see being used in the server example program after the socket listener is created. Note that the event handling parameters affect only the socket on which they are set , so you need to specify the events you want to receive for each socket.
SetEventHandler
takes a reference to an event handler and an event identifier. The event identifier should
SetNotify takes a bit-list of the socket events for which you want to be notified. For example, wxSOCKET_INPUT_FLAG wxSOCKET_LOST_FLAG would send an event when there is data to read on the socket or when the socket is closed. Notify takes a boolean indicating whether you want to receive events. This allows you to enable or disable events as needed without reconfiguring the events that you want to receive. Socket Status and Error NotificationsBefore discussing sending and receiving data, we describe the auxiliary methods for status and error notification so that we can refer to them from the data methods' descriptions. Close shuts down the socket, disabling further data transmission. The peer is explicitly notified that you have closed the socket. Note that events might have been queued already when you close the socket, so you must be prepared to continue receiving socket events even after closing the socket .
Destroy
is used instead of the
delete
operator because events might reach the socket after it has been deleted if
delete
were used.
Destroy
Error returns TRue if an error occurred in the last operation. GetPeer returns a wxSockAddress reference containing information about the peer side of the connection, such as IP address and port.
IsConnected
returns
true
if the socket is connected and
false
LastCount returns the number of bytes read or written by the last I/O call. LastError returns the last error. Note that a successful operation does not update the error code, so Error must be used first to determine whether an error occurred. Table 18-2 lists the error code values. Table 18-2. Socket Error Codes
Ok returns true for a socket client only when the client is connected to server, and it only returns true for a socket server if the socket could bind to the port and is listening. SetTimeout specifies how long to wait, in seconds, before a blocking socket operation times out. The default value is 10 minutes. Sending and Receiving Socket DatawxSocketBase provides a variety of basic and advanced methods for reading and writing socket data. All of the read and write operations store the results of the operation and enable you to access the number of bytes read with LastCount and the last error with LastError . ReadingDiscard deletes all incoming data from the socket buffer. Peek enables you to copy data from the socket buffer without removing the data from the buffer. You must provide a buffer for the data and the maximum number of bytes to peek.
Read
pulls data from the socket buffer and copies it to the specified buffer, up to the maximum
ReadMsg
reads data sent by
WriteMsg
into the specified buffer, up to the maximum size specified. If the buffer becomes full, the rest of the data is discarded.
ReadMsg
always waits for the full message sent with
WriteMsg
to
Unread copies data from the data buffer back into the socket buffer. You must also specify how many bytes to put back. WritingWrite sends data over the socket connection; you must specify a pointer to the data and the number of bytes to send. WriteMsg is similar to Write , except that WriteMsg adds a header to the data being sent so that the call to ReadMsg on the other end will know exactly how much data to read. Note that data sent with WriteMsg should always be read by a call to ReadMsg . Creating a ServerThe wxSocketServer class adds only a few methods to the wxSocketBase class for creating a listener and accepting connections. In order to create a server, you must specify what port to listen on for incoming connections. wxSocketServer uses the same wxIPV4address class used by wxSocketClient , except without specifying a remote host. In most cases, you should call Ok after creating a socket server to verify that the socket is bound and listening. wxSocketServer Major Member FunctionswxSocketServer accepts an address object specifying the listen port, and optional socket flags (see the "Socket Flags" section later in this chapter).
Accept
returns a new socket connection if one is available,
AcceptWith works just like Accept , but you must pass in an already existing wxSocketBase object (by reference), and a boolean is returned indicating whether a connection was accepted. WaitForAccept takes a seconds parameter and a milliseconds parameter for how long to wait for a connection, returning TRue when a connection is available, or false if the time period elapses without a connection arriving. Handling a New Connection EventWhen the listening socket detects an incoming connection, a connection event is sent for processing. From the event handler, you can accept the connection and perform any necessary immediate processing. Assuming that the connection has some longevity and isn't immediately closed, you also need to specify an event handler for the new socket. Remember that a listening socket continues to listen until closed, and new sockets are created for each new connection. In the lifetime of a server program, the same listening socket can spawn thousands of new sockets. Socket Event Recap
From the programmer's standpoint, event-based sockets are a boon for easily processing socket data, eliminating the need for creating and shutting down threads. The example program doesn't use threads, but the GUI will never block waiting for data. Because read commands are not issued until there is data to read, calls to read will immediately succeed and return the available data. If larger amounts of data need to be read, the data can be read in pieces and added to a buffer. Alternatively, a call can be made to
Peek
to determine how much data is available, and if not enough data has arrived, the application can simply wait for the
Next, we will look at how to use different socket flags to change a socket's behavior. |
|
|