PrevNextUpHome SophiaFramework UNIVERSE 5.3

16.3. Socket Communication

There are three types of classes for socket communication.

Table 16.2. Socket communication classes

Class Name Description
SFXTCPSocket Class for the TCP socket communication. This class provides the function to implement both of the TCP client and the TCP server.
SFXSSLSocket Class for the SSL socket communication. This class provides the function to implement only the SSL client.
SFXUDPSocket Class for the UDP socket communication. This class provides the function to implement both of the UDP client and the UDP server.
[Note] Sending and receiving data using the stream

In case of the SFXTCPSocket / SFXSSLSocket class, data can be sent and received using the stream. In addition, data can be sent and received using the Write / Read function without the stream.

However, in case of the SFXUDPSocket class, data can not be sent and received using the stream. In this case, the SFXUDPSocket::Send / SFXUDPSocket::Receive function will be used for sending and receiving data.

[Caution] Setting of the MIF file

To use the SFXTCPSocket / SFXSSLSocket / SFXUDPSocket class, the network privilege option must be turned on in the MIF file's setting.

16.3.1. TCP Socket Communication

The SFXTCPSocket class provides the functions to implement both of the TCP client and the TCP sever.

In this class, data can be sent and received using the stream.

When the stream is not used, data can be sent and received with the SFXTCPSocket::Write / SFXTCPSocket::Read function.

16.3.1.1. Implementation of the TCP Client

The TCP client can be implemented by taking the following procedures with the SFXTCPSocket class.

  1. Create the SFXTCPSocket instance, that is, the TCP socket.
  2. Open the TCP socket using the SFXTCPSocket::Open funtion.
  3. Bind the local IP address and port number to the TCP socket with the SFXTCPSocket::Bind function, if necessary. If binding is omitted, default port number will be bound to the TCP socket.
  4. Connect(or send connection request) to the TCP server using the SFXTCPSocket::Connect function.
  5. The connection result will be notified to the callback function registered with the SFXTCPSocket::Connect function.
  6. To receive data, get the input stream. When the stream is not used, data can be received with the SFXTCPSocket::Read function.
  7. To send data, get the output stream. When the stream is not used, data can be sent with the SFXTCPSocket::Write function.
  8. Step 6. and 7. are repeated until no other data needs to be sent or received.
  9. C Close the TCP socket using the SFXTCPSocket::Close function.

Example 16.12. Implementation of the TCP Client

// get the time from the NTP server
// The _socket variable is defined as class member variable since used in the callback function
class NetworkTime {
private:
    SFXTCPSocket _socket;
    SFXBinaryStreamReader _reader;
    SFXTimer _timer;

public:
    Void Start(Void); // start to connect to the NTP server
    Void Stop(Void);  // stop connecting to the NTP server
    
    // declare callback function
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXBINARYSTREAMREADER(OnFetch)
    XALLBACK_DECLARE_SFXTIMER(OnTimer)
};


#define     NTP_SERVER              ("www.example.com:37")
#define     TIMER_INTERVAL          (5000)


// start to connect to the NTP server
Void NetworkTime::Start(Void)
{
    SFCError error(SFERR_NO_ERROR);
    // set the NTP server address
    SFXSocketAddress address(NTP_SERVER);

    // set the timer callback function
    _timer.Set(XALLBACK_INTERNAL(OnTimer));

    // open socket
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {

        // connect to the NTP server
        // * the connection result will be notified to the OnConnect function
        if ((error = _socket.Connect(address, XALLBACK_INTERNAL(OnConnect))) == SFERR_NO_ERROR) {
            
            TRACE(">> connecting...");

        }
        else {

            // if an error occurs, close TCP socket
            _socket.Close();
        }
    }
	
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        // error handling
        ...

        // set the timer. after the TIMER_INTERVAL milliseconds,  the OnTimer function will be called
        _timer.Schedule(TIMER_INTERVAL);
    }
    return;
}

// stop connecting to the NTP server
Void NetworkTime::Stop(Void)
{
    // finalization
    _reader.Release();
    _socket.Close();
    return;
}

// callback function notified of the connection result
XALLBACK_IMPLEMENT_SFXTCPSOCKET(NetworkTime, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        // get input stream for receiving data from TCP socket
        if ((error = _socket.GetStreamReader(64, &_reader)) == SFERR_NO_ERROR) {

            // perform fetch: receive 4 bytes of data from TCP socket into to input stream  buffer actually
            // * the fetch result will be notified to the OnFetch function
            if ((error = _reader.Fetch(4, XALLBACK_INTERNAL(OnFetch))) == SFERR_NO_ERROR) { 

                TRACE(">> fetching...");
            }
            if (error != SFERR_NO_ERROR) { 

                // if an error occurs
                // release resource
                _reader.Release();
            }
        }
    }
	
    if (error != SFERR_NO_ERROR) {  

        // if an error occurs
        _socket.Close();
    }
    return;
}

// callback function notified of the fetch result
XALLBACK_IMPLEMENT_SFXBINARYSTREAMREADER(NetworkTime, OnFetch, error)
{
    SFXDate date; // date class
    UInt32 time;

    if (error == SFERR_NO_ERROR) {

        // read data as big endian
        _reader.SetEndian(SFXBinaryStreamReader::ENDIAN_BIG);
        
        // read data as the UInt32 type from input stream buffer into the time variable
        if ((error = _reader.ReadUInt32(&time)) == SFERR_NO_ERROR) {
            
            // set value of the SFXDate instance
            date.Set(time);
            
            // adjust time from January 1, 1900
            date -= SFXDateDuration::Offset19000101();
            
            // convert time into local time
            date += SFXDateDuration::LocalTimeOffset();

            // output time in specified format
            TRACE("%s", date.Format("YYYY/MM/DD hh:mm:ss").GetCString());
        }
    }
	
    if (error != SFERR_NO_ERROR) {

        // if an error occurs
        // error handling
        ...
    }

    // finalization
    _reader.Release();
    _socket.Close();
    return;
}

// timer callback function
XALLBACK_IMPLEMENT_SFXTIMER(NetworkTime, OnTimer)
{
    Start();
    return;
}
[Note] Note

For more details on the above code, see the networktime applet in the Example/networktime folder of the SophiaFramework UNIVERSE package, which supports suspend and resume functions.

16.3.1.2. Implementation of the TCP Server

The TCP server can be implemented by taking the following procedures with the SFXTCPSocket class.

  1. Create the SFXTCPSocket instance.
  2. Open the TCP Socket with the SFXTCPSocket::Open funtion.
  3. Bind the local IP address and port number to the TCP socket with the SFXTCPSocket::Bind function.
  4. Start to wait for the connection request from the TCP client with the SFXTCPSocket::Listen function.
  5. Get one from the connection request queue of this TCP socket and connect to the TCP client with the SFXTCPSocket::Accept function. At this time, the TCP socket for sending and receiving data after this step will be returned to the argument of this function.
  6. To receive data, get the input stream. When the stream is not used, data can be received with the SFXTCPSocket::Read function.
  7. To send data, get the output stream. When the stream is not used, data can be sent with the SFXTCPSocket::Write function.
  8. Step 6. and 7. are repeated until no other data needs to be sent or received.
  9. C Close the TCP socket using the SFXTCPSocket::Close function.

The code below is that the TCP client will communicate with the TCP server through loopback.

Example 16.13. Implementation of the TCP server

// The _socket variable is defined as class member variable since used in the callback function
SFMTYPEDEFCLASS(MyClass)
class MyClass {
    SFMSEALCOPY(MyClass)
public:
    static SFCInvokerPtr Factory(Void);
private:
    SFXTCPSocket _socket1;  // socket for waiting for connection request at the TCP server
    SFXTCPSocket _socket2;  // socker fot communication at the TCP server
    SFXTCPSocket _socket3;  // socker fot communication at the TCP client

    Void ServerStart(Void);  // start the TCP server
    Void ClientStart(Void);  // start the TCP client
    Void Stop(Void);         // stop the TCP server and the TCP client

    // callback function
    XALLBACK_DECLARE_SFXTCPSOCKET(/OnBind)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnListen)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnAccept)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnWrite)
    XALLBACK_DECLARE_SFXTCPSOCKET(OnRead)
};

// start the TCP server
Void MyClass::ServerStart(Void)
{
    SFCError error(SFERR_NO_ERROR);

    TRACE("Start!");  

    // open the socket for waiting for connection request at the TCP server
    if ((error = _socket1.Open()) == SFERR_NO_ERROR) {

        TRACE("Server Socket for Accepting the Client Requests Is Opened!"); 
        // perform binding
        OnBind(error);
    } else {
        
        // if an error occurs
        Stop();
        TRACE("Fatal Open Error of the Server Socket for Accepting the Client Requests = %d", error);
    } 
}

// start the TCP client
Void chelloworld::ClientStart(Void)
{
    SFCError error(SFERR_NO_ERROR);
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);  // address of the TCP sever
    // * when connecting to the external server, specify its IP address or domain name

    TRACE("Now, TCP Client Has Started.");  

    if ((error=_socket3.Open())== SFERR_NO_ERROR) {

        TRACE("Client Socket for Communicating with the Sever Has Opened!");

        // connect to the TCP server
        if ((error = _socket3.Connect(address, XALLBACK_INTERNAL(OnConnect))) == SFERR_NO_ERROR) {

            TRACE("connecting...");
        } else {

             // if an error occurs
             TRACE("Fatal Connect Error = %d", error);
             Stop();
        }
    } else {

        // if an error occurs
        Stop();
        TRACE("Fatal Client Socket Open Error = %d", error);
    }
    return;
}

// stop the TCP server and the TCP client
Void MyClass::Stop(Void)
{
    _socket1.Close();
    _socket2.Close();
    _socket3.Close();
    return;
}

// callback function notified that binding is ready to be performed
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnBind, error)
{
    TRACE("Error at the Start of OnBind() = %d", error);

    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024); // address of the TCP server
    // * when accepting connection request from the external client, specify SFXInetAddress::AnyInetAddress()

    if (error == SFERR_NO_ERROR) {

        switch (error = _socket1.Bind(address)) {

            case SFERR_NO_ERROR:

                // perform litening
                OnListen(error);
                break;

            case AEE_NET_WOULDBLOCK:
                // when binding is blocked

                // schedule to bind with registering the OnBind callback function
                // * the OnBind callback function will be called by BREW AEE when data is ready to be sent
                TRACE("Schedule Bind.");
                _socket1.ScheduleBind(XALLBACK_INTERNAL(OnBind));
                break;

            default:

                // if an error occurs
                Stop();
                TRACE("Fatal OnBind Error = %d", error);
                break;
        }
    } else {

        // if an error occurs
        Stop();
        TRACE("Fatal OnBind Error = %d", error);
    }
    return;
}

// callback function notified that liteninging is ready to be performed
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnListen, error)
{
    TRACE("Error at the Start of OnListen() = %d", error);

    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024); // address of the TCP server

    if (error == SFERR_NO_ERROR) {

        // perform listening
        switch (error = _socket1.Listen()) {

            case SFERR_NO_ERROR:

                // start the TCP client
                StartClient();   
                // perform accepting
                OnAccept(error);
                break;

            case AEE_NET_WOULDBLOCK:
                // when listening is blocked

                // schedule to listen with registering the OnListen callback function
                // * the OnListen callback function will be called by BREW AEE when data is ready to be sent
                TRACE("Schedule Listen.");
                _socket1.ScheduleListen(XALLBACK_INTERNAL(OnListen));
                break;

            default:

                // if an error occurs
                Stop();
                TRACE("Fatal OnListen Error = %d", error);
                break;
        }
    } else {

        // if an error occurs
        Stop();
        TRACE("Fatal OnListen Error = %d", error);
    }
    return;
}

// callback function notified that accepting is ready to be performed
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnAccept, error)
{
    TRACE("Error at the Start of OnAccept() = %d", error);

    if (error == SFERR_NO_ERROR) {

        switch (error = _socket1.Accept(&_socket2)) {

            case SFERR_NO_ERROR:

                // receive data from the TCP client
                OnRead(error); 
                break;

            case AEE_NET_WOULDBLOCK:
                // when accepting is blocked

                // schedule to accept with registering the OnAccept callback function
                // * the OnAccept callback function will be called by BREW AEE when data is ready to be sent
                TRACE("Schedule Accept.");
                _socket1.ScheduleAccept(XALLBACK_INTERNAL(OnAccept));
                break;

            default:

                // if an error occurs
                Stop();
                TRACE("Fatal OnAccept Error = %d", error);
                break;
        }
    } else {

        // if an error occurs
        Stop();
        TRACE("Fatal OnAccept Error = %d", error);
        }
    return;
}


// callback function notified of the connection result
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnConnect, error)
{
    TRACE("Error at the Start of OnConnect() = %d", error);

    switch (error) {

        case SFERR_NO_ERROR:

            // succeed to connect
            TRACE("Connected!");
            // send data
            OnWrite(error);
            break;

        default:

            // if an error occurs
            Stop();
            TRACE("Fatal OnConnect Error = %d", error);
            break;
    }
    return;
}

// callback function notified that data is ready to be sent
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnWrite, error)
{
    TRACE("Error at the Start of OnWrite() = %d", error);

    static ACharConst data[] = "hello world!";
    UInt32 size = sizeof(data) - 1;
 
    if (error == SFERR_NO_ERROR) {

       // send data to the TCP server
       switch (error = _socket3.Write(data, &size)) {

            case SFERR_NO_ERROR:

                // succeed to send data
                TRACE("Data has been sent!");
                break;

            case AEE_NET_WOULDBLOCK:
                // when sending data is blocked

                // schedule to send data with registering the OnWrite function
                // * the OnWrite function will be called by BREW AEE when data is ready to be sent
                TRACE("Schedule Write.");
                _socket3.ScheduleWrite(XALLBACK_INTERNAL(OnWrite));
                break;

            default:

                // if an error occurs
                Stop();
                TRACE("Fatal OnWrite Error = %d", error);
                break;
        }
    }
    return;
}

// callback function notified that data is ready to be received
XALLBACK_IMPLEMENT_SFXTCPSOCKET(MyClass, OnRead, error)
{
    TRACE("Error at the Start of OnRead() = %d", error);

    SFXBuffer buffer;
    UInt32 size(1024);
 
    buffer.SetSize(size);

    if (error == SFERR_NO_ERROR) {

        // receive data from the TCP client
        switch (error = _socket2.Read(buffer.GetBuffer(), &size)) {

            case SFERR_NO_ERROR:

                // succeed to receive data
                TRACE("Data has been reveived!");
                // display the received data on BREW Output Window
                buffer.SetSize(size + 1);
                buffer[buffer.GetSize() - 1] = '\0';
                TRACE("Received Data: %s", SFXAnsiString(buffer).GetCString());
                Stop();
                break;

            case AEE_NET_WOULDBLOCK:
                // when receiving data is blocked

                // schedule to receive data with registering the OnRead callback function
                // * the OnRead callback function will be called by BREW AEE when data is ready to be sent
                TRACE("Schedule Read.");
                _socket2.ScheduleRead(XALLBACK_INTERNAL(OnRead));
                break;

            default:

                // if an error occurs
                Stop();
                TRACE("Fatal OnRead Error = %d", error);
                break;
        }
    }
    return;
}
[Note] When the client and the server are not on the same device

When the client and the server are not on the same device, implement the TCP client and the TCP server as follows:

  1. Extract the socket for the TCP client(_socket3), the StartClient function, the OnConnect function, and the OnWrite function from the above code and implement the TCP client. The remaining code will become the TCP server.
  2. Specify SFXInetAddress::AnyInetAddress() instead of SFXInetAddress::LoopbackInetAddress() as the IP address that the TCP server binds.
  3. Specify the IP address or the domain name of the TCP server instead of SFXInetAddress::LoopbackInetAddress() as the IP address that the TCP client connects.

16.3.2. SSL Socket Communication

The SFXSSLSocket class provides the functions to implement the SSL client only.

In this class, data can be sent and received using the stream.

When the stream is not used, data can be sent and received with the SFXSSLSocket::Write / SFXSSLSocket::Read function.

16.3.2.1. Implementation of the SSL Client

The SSL client can be implemented by taking the following procedures with the SFXSSLSocket class.

  1. Create the SFXSSLSocket instance, that is, the SSL socket.
  2. Open the SSL socket using the SFXSSLSocket::Open function.
  3. Bind the local IP address and port number to the TCP socket with the SFXTCPSocket::Bind function, if necessary. If binding is omitted, default port number will be bound to the TCP socket.
  4. Set the trust mode(default: SSL_TRUST_MODE_FAIL) using the SFXSSLSocket::SetTrustMode function.
  5. Connect(or send connection request) to the SSL server using the SFXSSLSocket::Connect funtion, and also regiter the callback function.
  6. The connection result will be notified to the callback function registered with the SFXSSLSocket::Connect function.
  7. Negotiate with the SSL server using the SFXSSLSocket::Negotiate function, and also regiter the callback function.
  8. The negotiation result will be notified to the callback function registered with the SFXSSLSocket::Negotiate function.
  9. To receive data, get the input stream. When the stream is not used, data can be received with the SFXSSLSocket::Read function.
  10. To send data, get the output stream. When the stream is not used, data can be sent with the SFXSSLSocket::Write function.
  11. Step 9. and 10. are repeated until no other data needs to be sent or received.
  12. Close the SSL socket using the SFXSSLSocket::Close function.

Example 16.14. Implementation of the SSL Client

// *** segments different from TCP are in bold
// The _socket variable is defined as class member variable since used in the callback function
class MyClass {
private:
    SFXSSLSocket _socket;
    SFXAnsiStringStreamWriter _writer; // output stream
    SFXAnsiStringStreamReader _reader; // input stream

public:
    Void Start(Void);

    // callback functions
    XALLBACK_DECLARE_SFXSSLSOCKET(OnConnect)
    XALLBACK_DECLARE_SFXSSLSOCKET(OnNegotiate)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMWRITER(OnFlush)
    XALLBACK_DECLARE_SFXANSISTRINGSTREAMREADER(OnFetch)
};

Void MyClass::Start(Void)
{
    SFCError error;
    SFXSocketAddress host("www.example.com:995");

    // open SSL socket
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {
 
        // connect to www.example.com:443
        // *1 the connection result will be notified to the OnConnect function
        // *2 host name is automatically resolved
        error = _socket.Connect(host, XALLBACK_INTERNAL(OnConnect));
    }
    if (error != SFERR_NO_ERROR) { 
        // if an error occurs
        _socket.Close();
    }
    return;
}

// callback function notified of the connection result
XALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) { 

        // perform SSL Negotiation with server
        // * result of SSL negotiation will be notified to the OnNegotiate function
        error = _socket.Negotiate(XALLBACK_INTERNAL(OnNegotiate));
    }
    if (error != SFERR_NO_ERROR) { 

        // if an error occurs
        _socket.Close();
    }
    return;
}

// callback function notified of result of SSL negotiation
XALLBACK_IMPLEMENT_SFXSSLSOCKET(MyClass, OnNegotiate, error)
{
    static AChar message[] = "GET / HTTP/1.0\r\n\r\n";

    if (error == SFERR_NO_ERROR) {

        // get output stream for sending data to SSL socket (buffer size: 1024)
        if ((error = _socket.GetStreamWriter(1024, &_writer))
            == SFERR_NO_ERROR) {

            // write data from the message variable into output stream buffer
            if ((error = _writer.Write(message, lengthof(message))) == SFERR_NO_ERROR) {

                // perform flush: send data from output stream buffer to SSL socket actually
                // * the flush result will be notified to the OnFlush function
                error = _writer.Flush(XALLBACK_INTERNAL(OnFlush));
            }
            if (error != SFERR_NO_ERROR) { 
                // if an error occurs
                 _writer.Release();
            }
        }
    }
    if (error != SFERR_NO_ERROR) { 
       // if an error occurs
       _socket.Close();
    }
    return;
}

// callback function notified of the flush result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMWRITER(MyClass, OnFlush, error)
{
    // release output stream since all data is sent
    _writer.Release();

    if (error == SFERR_NO_ERROR) {

        // get input stream for receive data from SSL socket (buffer size: 1024)
        if ((error = _socket.GetStreamReader(1024, &_reader)) == SFERR_NO_ERROR) {

            // perform fetch: receive data from SSL socket to input stream  buffer actually 
            // * the fetch result will be notified to the OnFetch function
            if ((error = _reader.Fetch(XALLBACK_INTERNAL(OnFetch))) != SFERR_NO_ERROR) { 

                // if an error occurs
               _reader.Release(); 
            }
        }
    }

    if (error != SFERR_NO_ERROR) { 
        // if an error occurs
        _socket.Close();
    }

    return;
}

// callback function notified of the fetch result
XALLBACK_IMPLEMENT_SFXANSISTRINGSTREAMREADER(MyClass, OnFetch, error)
{
    SFXAnsiString string;

    if (error == SFERR_NO_ERROR) {

        // read data from input stream buffer into the string variable
        _reader >> string;

        // display the received data on BREW Output Window
        TRACE("%s", string.GetCString());
    }

    // release input stream since all data is received
    _reader.Release();

    // close SSL socket
    _socket.Close();
    return;
}

16.3.3. UDP Socket Communication

The SFXUDPSocket class provides the functions to implement both of the UDP client and the UDP sever.

In this class, data cannot be sent nor received using the stream. Instead, data can be sent and received with the SFXUDPSocket::Send / SFXUDPSocket::Receive function.

16.3.3.1. Implementation of the UDP Client

The UDP client can be implemented by taking the following procedures with the SFXUDPSocket class.

  1. Create the SFXUDPSocket instance, that is, the UDP socket.
  2. Open the UDP socket using the SFXUDPSocket::Open function.
  3. If necessary, bind local IP address and port number with the UDP Socket using the SFXUDPSocket::Bind function.
  4. Send data asynchronously to the specified UDP server using the SFXUDPSocket::Send function.
  5. Close the UDP socket using the SFXUDPSocket::Close function.

16.3.3.2. Implementation of the UDP Sever

The UDP server can be implemented by taking the following procedures with the SFXUDPSocket class.

  1. Create the SFXUDPSocket instance, that is, the UDP socket.
  2. Open the UDP socket using the SFXUDPSocket::Open function.
  3. Bind local IP address and port number with the UDP Socket using the SFXUDPSocket::Bind function.
  4. Send data asynchronously to the specified UDP server using the SFXUDPSocket::Send function.
  5. Receive data asynchronously from the UDP client using the SFXUDPSocket::Receive function.
  6. Step 4. and 5. are repeated until all data is sent or received.
  7. Close the UDP Socket using the SFXUDPSocket::Close function.
[Note] Note

The UDP sever can send data to another UDP server as the UDP client.

Example 16.15. Implemetation of the UDP server with the UDP client's function

// The _socket variable is defined as class member variable since used in the callback function
class MyClass {
private:
    SFXUDPSocket _socket;

public:
    Void Start(Void);

    // callback functions
    XALLBACK_DECLARE_SFXUDPSOCKET(OnBind)
    XALLBACK_DECLARE_SFXUDPSOCKET(OnSend)
    XALLBACK_DECLARE_SFXUDPSOCKET(OnReceive)
};

Void MyClass::Start(Void)
{
    SFCError error;

    // open UDP socket
    if ((error = _socket.Open()) == SFERR_NO_ERROR) {

        // bind local IP address and port number to UDP socket
        OnBind(SFERR_NO_ERROR);
    
    }
    return;
}

// callback function notified that binding is ready to be performed
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnBind, error)
{
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);

    // check whether or not an error occurs
    if (error == SFERR_NO_ERROR) {

        error = _socket.Bind(address);

        switch (error) {
            case SFERR_NO_ERROR:

                // send data asynchronously
                OnSend(SFERR_NO_ERROR);
                break;
            case AEE_NET_WOULDBLOCK:

                // schedule to perform binding: register the OnBind callback function
                // * the OnBind callback function will be called by BREW AEE when binding is ready to be performed
                _socket.ScheduleBind(XALLBACK_INTERNAL(OnBind));
                break;
        }
    }
    return;
}

// callback function notified that data is ready to be sent
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnSend, error)
{
    static ACharConst data[] = "udp!";
    SFXSocketAddress address(SFXInetAddress::LoopbackInetAddress(), 1024);
    UInt32 size;

    // check error
    if (error == SFERR_NO_ERROR) {
        size = sizeof(data) - 1;

        // send data
        error = _socket.Send(address, data, &size);

        switch (error) {
            case SFERR_NO_ERROR:

                // check whether data of specified size is written or not
                // SFXUDPSocket::Send function may not send data of specified size at a time
                // here for simple explanation, display error message
                if (size == sizeof(data) - 1) {

                    // receive data asynchronously
                    OnReceive(SFERR_NO_ERROR);
                }
                else {
                    TRACE("...failed to send ...");
                }
                break;
            case AEE_NET_WOULDBLOCK:
                // if sending data is blocked

                // schedule to send data: register the OnSend callback function
                // * the OnSend callback function will be called by BREW AEE when data is ready to be sent
                _socket.ScheduleSend(XALLBACK_INTERNAL(OnSend));
                break;
        }
    }
    return;
}

// callback function notified that data is ready to be received
XALLBACK_IMPLEMENT_SFXUDPSOCKET(MyClass, OnReceive, error)
{
    SFXSocketAddress socket;
    SFXBuffer buffer;
    UInt32 size;

    // check error
    if (error == SFERR_NO_ERROR) {
        buffer.SetSize(4);

        size = static_cast<UInt16>(buffer.GetSize());

        // receive data
        switch (_socket.Receive(&socket, buffer.GetBuffer(), &size)) {
            case SFERR_NO_ERROR:

                // check whether data of specified size is read or not
                // SFXUDPSocket::Receive function may not receive data of specified size at a time
                // here for simple explanation, display error message
                if (size == buffer.GetSize()) {

                    // display received data
                    buffer.SetSize(buffer.GetSize() + 1);
                    buffer[buffer.GetSize() - 1] = '\0';
                    TRACE(":%s", SFXAnsiString(buffer).GetCString());

                    // close socket
                    _socket.Close();
                }
                else {
                    TRACE("...failed to receive...");
                }
                break;
            case AEE_NET_WOULDBLOCK:
                // if receiving data is blocked

                // schedule to receive data: register the OnReceive callback function
                // * the OnReceive callback function will be called by BREW AEE when data is ready to be received
                _socket.ScheduleReceive(XALLBACK_INTERNAL(OnReceive));
                break;
        }
    }
    return;
}