{"id":19719966,"url":"https://github.com/timboudreau/scamper","last_synced_at":"2025-09-12T17:36:56.256Z","repository":{"id":24918573,"uuid":"28335517","full_name":"timboudreau/scamper","owner":"timboudreau","description":"A toolkit for creating SCTP servers and clients","archived":false,"fork":false,"pushed_at":"2022-05-08T06:15:25.000Z","size":185,"stargazers_count":22,"open_issues_count":1,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-29T21:39:14.860Z","etag":null,"topics":["async","bson","java","netty","sctp"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timboudreau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-22T11:11:01.000Z","updated_at":"2021-11-10T17:50:16.000Z","dependencies_parsed_at":"2022-08-23T07:50:52.442Z","dependency_job_id":null,"html_url":"https://github.com/timboudreau/scamper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/timboudreau/scamper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fscamper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fscamper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fscamper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fscamper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timboudreau","download_url":"https://codeload.github.com/timboudreau/scamper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timboudreau%2Fscamper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274847727,"owners_count":25360978,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-12T02:00:09.324Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","bson","java","netty","sctp"],"created_at":"2024-11-11T23:09:49.960Z","updated_at":"2025-09-12T17:36:56.218Z","avatar_url":"https://github.com/timboudreau.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Scamper\n=======\n\nA toolkit to make it easy to write Java servers and clients that use \n[SCTP](http://en.wikipedia.org/wiki/Stream_Control_Transmission_Protocol)\ninstead of TCP as their network\ntransport, using [Netty](http://netty.io) under the hood.\n\nAn example chat application can be [found here](https://github.com/timboudreau/scamper-chat).\n\nServers and clients can register `MessageType`s and `MessageHandler`s that\nreceive messages of those types.  Each message sent or received by this\nlibrary has a 3-byte header identifying the message type, which is used to\nroute them to the handler you provide for that message type.\n\nThe remaining payload of a message is up to you.  You define message types \nand write handlers that receive those messages, or use a `Sender` to send \nthem elsewhere.\n\nBy default message payloads\nare encoded using [BSON](http://en.wikipedia.org/wiki/BSON) using \n[BSON4Jackson](https://github.com/michel-kraemer/bson4jackson).  So sending\nmessages is as easy as passing a Java object to a `Sender`.\n\nIf you want to write your own payloads, simply make the payload a\n[ByteBuf](http://netty.io/5.0/api/io/netty/buffer/ByteBuf.html) and\nno encoding or decoding will be done.\n\nFor debugging, BSON can be turned off and JSON will be used instead.\n\nJavadoc is [available here](http://timboudreau.com/builds/job/scamper/lastSuccessfulBuild/artifact/scamper/target/apidocs/index.html)\nand the library is available via Maven [as described here](http://timboudreau.com/builds/):\n\n```xml\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.mastfrog\u003c/groupId\u003e\n        \u003cartifactId\u003escamper\u003c/artifactId\u003e\n        \u003cversion\u003e1.3-dev\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\nWhat It Does\n------------\n\nAllows you to create clients and servers that can pass messages very simply.\nAt this point the benefits of SCTP are small (multi-homing is TBD), but one\naspect can be seen in that, if you run the date-demo project, you can stop\nand restart the server while the client is running, without the client\neither failing or needing to do anything to reconnect.\n\nSCTP is message-oriented, like UDP, as opposed to stream-oriented like TCP,\nand has the benefit that messages do not block each other, and multiple messages\ncan be on the wire on the same connection at the same time.  Strict order is\noptional.\n\nRequirements\n------------\n\nOn Linux, you need `lksctp-tools` installed, at least with JDK 8.  If you see\nan error about not being able to load a native library, that's the problem.\nIn other situations, you may need to make sure SCTP support is compiled into\nyour OS kernel.\n\nIf you use the BSON support (i.e. you want to pass POJOs as messages), those\nclasses will need to be serializable/deserializable by Jackson.\n\n\nWriting A Server\n----------------\n\nYou need to code two things:\n\n * A `MessageType`, which simply defines a pair of bytes at the head of a\nmessage to mark it as that flavor of message\n\n```java\n    static final MessageType WHAT_TIME_IS_IT = new MessageType(\"dateQuery\", 1, 1);\n    static final MessageType THE_TIME_IS = new MessageType(\"dateResponse\", 1, 2);\n```\n\n * A `MessageHandler` which can receive messages, and optionally reply to them\n```java\n    static class DateQueryHandler extends MessageHandler\u003cDateRecord, Map\u003e {\n\n        DateQueryHandler() {\n            super(Map.class);\n        }\n\n        @Override\n        public Message\u003cDateRecord\u003e onMessage(Message\u003cMap\u003e data, ChannelHandlerContext ctx) {\n            DateRecord response = new DateRecord();\n            return RESPONSE.newMessage(response);\n        }\n\n        public static class DateRecord {\n            public long when = System.currentTimeMillis();\n        }\n    }\n```\n\nThe builder class `SctpServerAndClientBuilder` makes it simple to bind these and\ncreate a server:\n\n```java\n    public static void main(String[] args) throws IOException, InterruptedException {\n        Control\u003cSctpServer\u003e control = new SctpServerAndClientBuilder(\"date-demo\")\n                .onPort(8007)\n                .withWorkerThreads(3)\n                .bind(WHAT_TIME_IS_IT, DateQueryHandler.class)\n                .bind(THE_TIME_IS, DateResponseHandler.class)\n                .buildServer(args);\n        SctpServer server = control.get();\n        ChannelFuture future = server.start();\n        future.sync();\n    }\n```\n\nWhat this does:\n\n * Configure a server that understands our two MessageTypes, and passes handler\nclasses for both of them\n * Get back a `Control` object which can be used to shut down that server\n * Get the actual server instance\n * Start it, getting back a `ChannelFuture` which will complete when the \nconnection is closed\n * Wait forever on that future, blocking the main thread\n\n[Full source code for the server demo](https://github.com/timboudreau/scamper/blob/master/scamper-date-demo/src/main/java/com/mastfrog/scamper/demo/dates/DateDemo.java)\n\n\nWriting a Client\n----------------\n\n`Sender` is a simple class which maintains a set of connections to clients;\nyou simply call it with a message and the address you want to send it to.  All\nyou need is a `MessageType` and an object that Jackson can serialize.  Then\nyou just `send()` the message to an `Address`.\n\n```java\nstatic final MessageType MY_MESSAGE_TYPE = new MessageType(\"dateQuery\", 1, 1);\n...\nMessage\u003c?\u003e message = MY_MESSAGE_TYPE.newMessage(new MyObject());\nsender.send(new Address(\"127.0.0.1\", 8007), message);\n```\n\nA client for the server above looks like:\n\n```java\n    public static void main(String[] args) throws IOException, InterruptedException {\n        Control\u003cSender\u003e control = new SctpServerAndClientBuilder(\"date-demo\")\n                .withHost(\"127.0.0.1\")\n                .onPort(8007)\n                .bind(WHAT_TIME_IS_IT, DateDemo.DateQueryHandler.class)\n                .bind(THE_TIME_IS, DateDemo.DateResponseHandler.class)\n                .buildSender(args);\n\n        Sender sender = control.get();\n\n        for (int i = 0;; i++) {\n            Address addr = new Address(\"127.0.0.1\", 8007);\n            // Just put some random stuff in the inbound message\n            Map msg = new MapBuilder().put(\"id\", i).put(\"client\", true).build();\n            Message\u003c?\u003e message = DateDemo.WHAT_TIME_IS_IT.newMessage(msg);\n            sender.send(addr, message).addListener(new LogResultListener(i));\n            Thread.sleep(5000);\n        }\n    }\n```\n\nWhat this does:\n\n * Configure a Sender with handlers for our message types\n * Loop forever sending a `WHAT_TIME_IS_IT` message every 5 seconds\n    * A Message object is simply a wrapper for a message type and a payload\n * You will see the response logged\n\nSee the subproject `scamper-date-demo` to build and run this.\n\n[Full source code for the client demo](https://github.com/timboudreau/scamper/blob/master/scamper-date-demo/src/main/java/com/mastfrog/scamper/demo/dates/DateClientDemo.java)\n\n### About SCTP Channels\n\nAn SCTP association is like a TCP connection, but may refer to a *list of host/port pairs*\nrather than just one - such connections are called multi-homed, and rely on the\nunderlying network to find the connection in that list which is the shortest distance\nfrom the sender.\n\nWithin that association, there are some number of SCTP \"channels\" available, each\nof which is independent of the others.  This allows multiple messages to be on\nthe wire at once, without one message blocking the other from being sent.\n\nBy default, when a connection is created, this library will ask the implementation\nhow many channels are available, and each new message is sent, round-robin style\non the next available channel.  If that is not the desired behavior (say, the \ncaller is expecting a response on the same channel), you can\nexplicitly pass a channel number to \u003ccode\u003eSender.send()\u003c/code\u003e.\n\nOn Linux + JDK 8, at the time of this writing, the range of available channels\nthrough the loopback interface is 0-65535.\n\n\n### About Netty's ChannelFuture\n\nNetty is asynchronous.  That means that network operations are not completed in\nthe thread they are invoked in, and notifications of their success or failure\nis called asynchronously when the socket is flushed or the operation fails.\n\nCalls that perform network operations return a \n\u003ca href=\"http://netty.io/5.0/api/io/netty/channel/ChannelFuture.html\"\u003eChannelFuture\u003c/a\u003e\nyou can listen on to check the status of the operation,\nor allow you to pass a \n\u003ca href=\"http://netty.io/5.0/api/io/netty/channel/ChannelFutureListener.html\"\u003eChannelFutureListener\u003c/a\u003e\nwhich will be notified when the operation is completed.\n\nIt is important to check \n\u003ca href=\"http://netty.io/5.0/api/io/netty/util/concurrent/Future.html#cause()\"\u003eChannelFuture.cause()\u003c/a\u003e\nto see that the operation actually succeeded.  If it is null, the operation did\nsucceed.\n\nYou can also implement and bind \u003ca href=\"ErrorHandler.html\"\u003eErrorHandler\u003c/a\u003e to\nreceive uncaught exceptions while processing messages (which can legitimately\nhappen if, say, a client sends bogus data).  It is always preferable to use a\nlistener on a specific operation, since that listener is likely to have enough\ncontext to do something more intelligent than just log an error.\n\n\n### Memory Usage\n\nBy default, uses Netty's [PooledByteBufAllocator](http://netty.io/5.0/api/io/netty/buffer/PooledByteBufAllocator.html).\nTo change this, pass a different allocator to the builder's \u003ccode\u003eoption()\u003c/code\u003e method for\n\u003ccode\u003eChannelOption.ALLOCATOR\u003c/code\u003e.  This uses a pool of off-heap direct memory storage\nwith reference-counting to recycle memory - resulting in a server that, once it reaches\na steady state, should allocate little or no more memory at runtime.\n\nIf you use Netty's ByteBufs directly, you may need to ensure you call \u003ccode\u003erelease()\u003c/code\u003e\non them when you're done with them, as they are reference-counted.\n\nStatus\n======\n\nThis library is fairly embryonic, but is usable at this point for experimenting\nwith SCTP.\n\n\nTo-Do\n-----\n\n * Support for multi-homing (right now you could grab the Java SCTP connection after the fact\nand add them perhaps - haven't tried it) - should be a first-class feature\n   * Implement by extending the Address class to contain multiple `InetSocketAddress`es\n * Expire long-unused connections in `Associations` on a timeout\n   * Requires some plumbing to touch a timestamp on each one when it is used\n * Implement compression using a different magic first byte\n * Implement encryption (key-exchange mechanism TBD)\n\nLicense\n=======\n\n[GNU Affero license, version 3](http://www.gnu.org/licenses/agpl-3.0.html)\n\n\nWhy is it called Scamper?\n-------------------------\n\nWell, I was shooting for something that incorporated SCTP, but Scamtper is,\nunpronouncable, Scampter would get the order wrong, and Scatup didn't \nsound very nice at all.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimboudreau%2Fscamper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimboudreau%2Fscamper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimboudreau%2Fscamper/lists"}