Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SCTP Server using boost.asio

Tags:

c++

boost-asio

I'm trying to implement sctp client-server model using boost.asio framework. Currently I have this for server:

#include <boost/asio.hpp>
#include <iostream>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
using namespace boost;


int main(int argc,char * argv[]){


    if (argc < 2){
        exit(0);
    }
    unsigned short MY_PORT_NUM = atoi(argv[1]);
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(MY_PORT_NUM);


    try {
        asio::io_context io_service;
        boost::asio::generic::seq_packet_protocol::endpoint endpoint((struct sockaddr *)&servaddr, sizeof(servaddr),IPPROTO_SCTP);
        boost::asio::basic_socket_acceptor<asio::generic::seq_packet_protocol> acceptor(io_service,endpoint);
        boost::system::error_code ec;
        asio::generic::seq_packet_protocol::socket socket(io_service);
        acceptor.accept(socket,ec);
        if (ec){
          std::cout << ec.message() << "\n";
        } 
                

    } catch ( boost::system::error_code& e){
        std::cerr << e.message() << std::endl;
    }
    return 0;
}

I got an error message "Operation not supported". I've also tried to make it this way:


    try {

        asio::io_context io_service;
        boost::asio::generic::seq_packet_protocol p(AF_INET,IPPROTO_SCTP);
        boost::asio::generic::seq_packet_protocol::endpoint endpoint((struct sockaddr *)&servaddr, sizeof(servaddr),IPPROTO_SCTP);
        boost::asio::basic_socket_acceptor<asio::generic::seq_packet_protocol> acceptor(io_service);
        acceptor.open(p);
        acceptor.bind(endpoint);
        acceptor.listen();
      
        boost::system::error_code ec;
        asio::generic::seq_packet_protocol::socket socket(io_service);
        acceptor.accept(socket,ec);

        if (ec){
          std::cout << ec.message() << "\n";
        } 


    } catch ( boost::system::error_code& e){
        std::cerr << e.message() << std::endl;
    }

And I've got exactly the same result.
You can compile this code with:
g++ sctp_server.cc -o sctp_server -lboost_thread -lboost_coroutine
I have C-version of sctp server, that's works fine:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>

static void die(const char *s) {
        perror(s);
        exit(1);
}
static void server(unsigned short port){
    
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    struct sctp_initmsg initmsg;
    initmsg.sinit_max_attempts = 4;
    initmsg.sinit_num_ostreams = 5;
    initmsg.sinit_num_ostreams = 5;

    int listen_fd = socket(AF_INET, SOCK_STREAM,IPPROTO_SCTP);
    if (listen_fd < 0)
        die("socket()");
    if ( bind(listen_fd, (struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
        die("bind()");
    if ( setsockopt(listen_fd,IPPROTO_SCTP,SCTP_INITMSG,&initmsg,sizeof(initmsg)) < 0)
        die("setsockopt()");
    if ( listen(listen_fd,initmsg.sinit_max_instreams) < 0)
        die("listen()");

    for (;;){
        char buffer[1024];
        fputs("Waiting for connection\n",stdout);
        
        int connect_fd = accept(listen_fd, (struct sockaddr *)NULL, NULL);
        if (connect_fd < 0)
            die("accept()");

        fputs("New client connected\n",stdout);
         struct sctp_sndrcvinfo sndrcvinfo;
         int flags;
        size_t bytesRcvd = sctp_recvmsg(connect_fd,buffer,sizeof(buffer),NULL,0,&sndrcvinfo,&flags);
        if (bytesRcvd > 0){
            printf("Received data : %s\n", buffer);
        }
        close(connect_fd);
    }

}
int main(int argc, char * argv[]){
    if (argc < 2)
        die("Usage ./c_sctp_server <Port>");

    unsigned short port = atoi(argv[1]);
    server(port);
    return 0;
}

You can compile this with:
gcc c_sctp_server.c -o c_sctp_server -lsctp
The question is what is wrong with my boost.asio version?

like image 410
Igor Glushkov Avatar asked Aug 22 '26 17:08

Igor Glushkov


1 Answers

Firstly, error_code is never raised. You probbably meant to catch system_error:

} catch (boost::system::system_error const& e) {
    std::cerr << e.code().message() << " from " << e.code().location() << std::endl;
}

Note that you can often learn about the source location of the error as shown. In your case (slightly redacted):

Operation not supported from boost/asio/detail/reactive_socket_service.hpp:505:5 in function 'error_code reactive_socket_service<Protocol>::accept(implementation_type&, Socket&, endpoint_type*, error_code&)'

As the message indicates, accept is not supported by seq_packet_protocol. I think you are going to have more leverage starting from generic::stream_protocol which does know about connections ("associations" for SCTP).

You might also consider going from an existing POSIX SCTP example and gradually replacing bits by Asio.

Disclaimer: I'm not versed with SCTP. A casual glance at RFC 9260 makes me think it's not trivial to adapt, as the behaviors are pretty different (e.g. changing associations or peer addresses), even assuming you will be using POSIX API directly instead of also wrapping SCTP-specific socket options.

Using Error Code

Live On Coliru

#include <boost/asio.hpp>

#include <arpa/inet.h>
#include <iostream>
#include <linux/sctp.h>
#include <netinet/in.h>
namespace asio = boost::asio;
using boost::system::error_code;

int main(int argc, char* argv[]) {
    std::vector args(argv + 1, argv + argc);
    // if (args.size() != 2) exit(0);

    uint16_t MY_PORT_NUM = atoi(args.at(0));
    struct sockaddr_in servaddr;
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port        = htons(MY_PORT_NUM);

    //using protocol = asio::generic::seq_packet_protocol;
    using protocol = asio::generic::stream_protocol;
    protocol::endpoint ep{
        reinterpret_cast<struct sockaddr*>(&servaddr),
        sizeof(servaddr),
        IPPROTO_SCTP,
    };

    asio::io_context ioc;

    asio::basic_socket_acceptor<protocol> acceptor(ioc);

    error_code ec;
    if (!ec)
        acceptor.open({AF_INET, IPPROTO_SCTP}, ec);
    if (!ec)
        acceptor.bind(ep, ec);
    if (!ec)
        acceptor.listen(protocol::socket::max_listen_connections, ec);

    protocol::socket socket(ioc);
    if (!ec)
        acceptor.accept(socket, ec);

    std::cout << ec.message() << " from " << ec.location() << "\n";
}

Using Exceptions

Live On Coliru

#include <boost/asio.hpp>

#include <arpa/inet.h>
#include <iostream>
#include <linux/sctp.h>
#include <netinet/in.h>
namespace asio = boost::asio;

int main(int argc, char* argv[]) try {
    std::vector args(argv + 1, argv + argc);
    // if (args.size() != 2) exit(0);

    uint16_t           MY_PORT_NUM = atoi(args.front());
    struct sockaddr_in servaddr;
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port        = htons(MY_PORT_NUM);

    // using protocol = asio::generic::seq_packet_protocol;
    using protocol = asio::generic::stream_protocol;
    protocol::endpoint ep{
        reinterpret_cast<struct sockaddr*>(&servaddr),
        sizeof(servaddr),
        IPPROTO_SCTP,
    };

    asio::io_context ioc;

    asio::basic_socket_acceptor<protocol> acceptor(ioc);

    acceptor.open({AF_INET, IPPROTO_SCTP});
    acceptor.bind(ep);
    acceptor.listen(protocol::socket::max_listen_connections);

    protocol::socket socket(ioc);
    acceptor.accept(socket);

} catch (boost::system::system_error const& se) {
    std::cout << se.code().message() << " from " << se.code().location() << "\n";
}
like image 52
sehe Avatar answered Aug 25 '26 06:08

sehe