Search code examples
androidc++iosc++11cocos2d-x

Create UDP server using C++ to embed in cross platform iOS and Android app


I am developing a cross-platform mobile game (iOS and Android) using cocos2d-x. Most of my code is written in C++, with OS specific code in Objective-C / Java / Swift using a bridge.

I was wondering if anyone has used any C++ library to host a UDP server within their app ?

EDIT: So far I have found many platform specific solutions (using Java for Android, and cocoaasync etc for iOS), but nothing specifically in C++ which has been used for a cross platform app.

Edit: I would prefer a solution without boost. Preferably something simple to include like adding a couple of files to a project.


Solution

  • Here is what I ended up with:

    h file:

    #include "Queue.h"
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    
    #include <array>
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    #define MAXBUFFER_SIZE 1024
    
    class UDPServer {
    public:
        /**
         * Constructor
         *
         * @port the port on which the UDP server is listening for packets.
         */
        explicit UDPServer(unsigned short port);
    
        /**
         * Destructor
         */
        ~UDPServer() = default;
    
        /**
         * Setup the server.
         */
        void setupServer();
    
        /**
         * Get a single message.
         * For demonstration purposes, our messages is expected to be a array of int
         */
        bool getMessage(std::array<int, 4>& message);
    
        bool getIPAddress(std::array<int, 4>& message);
    
        void setFoundIP();
    
        bool isReady();
    
        void nextPort();
    
        int getPort();
    
    private:
        bool _isBoundToPort = false;
        /**
         * The server port.
         */
        unsigned short port_;
        bool isFoundIP = false;
        /**
         * The thread-safe message queue.
         */
        Queue queue_;
        Queue _ipAddresses;
        /**
         * The UDP server function.
         */
        int UDPServerFunc();
    };
    

    cpp file:

    #include "UDPServer.h"
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    
    using namespace std;
    
    /**
     * This function parses an incoming message with the following format: 1;234;-89;-53;
     *
     * A valid message consists of 4 integer values separated by semicolons.
     */
    inline std::array<int, 4> parseMessage(const std::string& input);
    inline std::array<int,4> parseIp(const std::string& input);
    
    UDPServer::UDPServer(unsigned short port)   {
        port_ = port;
    }
    
    bool UDPServer::getMessage(std::array<int, 4>& message) {
        return queue_.pop(message);
    }
    
    bool UDPServer::getIPAddress(std::array<int, 4>& message) {
        return _ipAddresses.pop(message);
    }
    
    void UDPServer::setFoundIP(){
        isFoundIP = true;
    }
    
    bool UDPServer::isReady(){
        return _isBoundToPort;
    }
    
    void UDPServer::nextPort(){
        port_++;
    }
    
    int UDPServer::getPort(){
        return port_;
    }
    
    void UDPServer::setupServer() {
        // Launch the server thread.
        std::thread t([this](){
            UDPServerFunc();
        });
        t.detach();
    }
    
    int UDPServer::UDPServerFunc() {
    
        // Creating socket file descriptor
        int sockfd;
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        // Filling server information
        struct sockaddr_in servaddr, cliaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        memset(&cliaddr, 0, sizeof(cliaddr));
        servaddr.sin_family = AF_INET; // IPv
    
        servaddr.sin_addr.s_addr = INADDR_ANY;
        servaddr.sin_port = htons(port_);
    
        // Bind the socket with the server address
        if (::bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
            perror("bind failed");
            exit(EXIT_FAILURE);
        }
    
        _isBoundToPort = true;
        while (true)  {
            // Read the next message from the socket.
            char message[MAXBUFFER_SIZE];
            socklen_t len = sizeof(struct sockaddr);
            ssize_t n = recvfrom(sockfd, (char *)&message, MAXBUFFER_SIZE, MSG_DONTWAIT,
                         (struct sockaddr *)&cliaddr, (socklen_t*)&len);
            if (n > 0) {
                message[n] = '\0';
                // Parse incoming data and push the result on the queue.
                // Parsed messages are represented as a std::array<int, 4>.
    
                if(!isFoundIP){
                    _ipAddresses.push(parseIp(message));
                }else{
                    queue_.push(parseMessage(message));
                }
            } else {
                // Wait a fraction of a millisecond for the next message.
                usleep(100);
            }
        }
    
        return 0;
    }
    

    I removed any unnecessary code from my answer, as the meat really is just the code above. If anyone needs the extraneous functions as well, I shared the code on Github, and will add some examples later on too.

    The above code is really simple and has a couple of parse functions for extracting an IP address, or a set of four numbers delimited by semicolons. The above code is simple enough to modify yourself for your own custom messages.

    Queue.h is just a simple thread safe queue.