Skip to content

DitchOoM/socket

Repository files navigation

Contributors Forks Stargazers Issues MIT License LinkedIn


Socket

A kotlin multiplatform library that allows you send network data via sockets


Report Bug ยท Request Feature

Table of Contents
  1. About The Project
  2. Installation
  3. Usage
  4. Building Locally
  5. Getting Started
  6. Roadmap
  7. Contributing
  8. License

About The Project

Managing network calls can be slightly different based on each platform. This project aims to make it easier to manage sockets in a cross platform way using kotlin multiplatform. This was originally created as a side project for a kotlin multiplatform mqtt data sync solution.

Runtime Dependencies

Platform ๐Ÿ› Builds๐Ÿ›  + ๐Ÿ”ฌTests๐Ÿ”ฌ Native Wrapper For
JVM 1.8 ๐Ÿš€ AsynchronousSocketChannel
Node.js ๐Ÿš€ Socket
Browser (Chrome) ๐Ÿš€ unavailable
Android ๐Ÿš€ AsynchronousSocketChannel falling back to SocketChannel
iOS ๐Ÿš€ Custom wrapped NWConnection
WatchOS ๐Ÿš€ Custom wrapped NWConnection
TvOS ๐Ÿš€ Custom wrapped NWConnection
MacOS ๐Ÿš€ Custom wrapped NWConnection
Linux X64 ๐Ÿ”ฎ WIP
Windows X64 ๐Ÿ”ฎ WIP

Installation

  • Add implementation("com.ditchoom:socket:$version") to your build.gradle dependencies
  • Copy the contents of this patch.js file into your own webpack.config.d directory if you are targeting js
  • Add this to your kotlin { bracket in build.gradle.kts if you are targeting an apple platform
kotlin {
  ...
    cocoapods {
        ios.deploymentTarget = "13.0"
        osx.deploymentTarget = "11.0"
        watchos.deploymentTarget = "6.0"
        tvos.deploymentTarget = "13.0"
        pod("SocketWrapper") {
            source = git("https://github.com/DitchOoM/apple-socket-wrapper.git") {
                tag = "0.1.3"
            }
        }
    }
}

Client Socket Usage

Suspend connect read write and close

// Run in a coroutine scope
val socket = ClientSocket.connect(
    port = 80, // no default
    hostname = "example.com", // null is default which points to localhost
    timeout = 15.seconds, // default
    socketOptions = null, // default
)
val isOpen = socket.isOpen()
val localPort = socket.localPort()
val remotePort = socket.remotePort()
val stringRead = socket.readString() // read a string. utf8 is the default
val readBuffer =
    socket.read() // read a ReadBuffer as defined in the buffer module. call resetForRead() before consumption
socket.readFlow().collect { buffer -> // default charset is utf8
    buffer.resetForRead()
    // buffer is ready for consumption
}
socket.readFlowString().collect { value -> // default charset is utf8
    // read a string 
}
val bytesWritten = socket.write(buffer) // write the buffer to the socket
val bytesWrittenString = socket.writeString("hello") // write the buffer to the socket. utf8 is default
socket.close() // close the socket

Or use lambda which auto closes the socket

// Run in a suspend method, same defaults as the other `connect` method
val response = ClientSocket.connect(80, hostname = "example.com") { socket ->
    val request =
        """
GET / HTTP/1.1
Host: example.com
Connection: close

"""
    val bytesWritten = socket.write(request)
    socket.read() // can throw a SocketClosedException
}
// response is populated, no need to call socket.close()

Server Example

val server = ServerSocket.allocate()
val acceptedClientFlow = server.bind()
val isListening: Boolean = server.isListening() // returns true if server is listening for connections
val listenPort: Int = server.port() // the assigned port, -1 if unassigned or closed

launch {
    acceptedClientFlow.collect { serverToClient ->
        // new client has connected, can now read and write to it.
        // same api as ClientSocket
        val localPort = serverToClient.localPort()
        val remotePort = serverToClient.remotePort()
        val stringRead = serverToClient.readString() // read a string. utf8 is the default
        val readBuffer =
            serverToClient.read() // read a ReadBuffer as defined in the buffer module. call resetForRead() before consumption
        serverToClient.readFlow().collect { buffer -> // default charset is utf8
            buffer.resetForRead()
            // buffer is ready for consumption
        }
        serverToClient.readFlowString().collect { value -> // default charset is utf8
            // read a string 
        }
        val bytesWritten = serverToClient.write(buffer) // write the buffer to the socket
        val bytesWrittenString = serverToClient.writeString("hello") // write the buffer to the socket. utf8 is default
        serverToClient.close() // close the socket
    }
}
server.close() // stops listening for connections on the assigned port

TLS support

// Simply add tls=true to your ClientSocket.connect or ClientSocket.allocate
val response = ClientSocket.connect(port, hostname, tls = true) { socket ->
    // do something
}

Client echo example

Refer to SimpleSocketTests.kt for a tested example

val server = ServerSocket.allocate()
val text = "Sphinx of black quartz, judge my vow."
val acceptedClientFlow = server.bind()
launch {
    acceptedClientFlow.collect { serverToClient ->
        val dataReceivedFromClient = serverToClient.readString()
        serverToClient.writeString(dataReceivedFromClient)
        serverToClient.close()
    }
}
val clientToServer = ClientSocket.allocate()
clientToServer.open(server.port())
clientToServer.writeString(text)
assertEquals(text, clientToServer.readString())
clientToServer.close()
server.close()

Server echo example

Refer to SimpleSocketTests.kt for a tested example

val server = ServerSocket.allocate()
val text = "yolo swag lyfestyle"
val acceptedClientFlow = server.bind()
launch {
    acceptedClientFlow.collect { serverToClient ->
        serverToClient.writeString(text)
        assertEquals(text, serverToClient.readString())
        serverToClient.close()
    }
}
ClientSocket.connect(server.port()) { clientToServer ->
    assertEquals(text, clientToServer.readString())
    clientToServer.writeString(text)
    clientToServer.close()
}
server.close()

Building Locally

Roadmap

See the open issues for a list of proposed features ( and known issues).

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

Distributed under the Apache 2.0 License. See LICENSE for more information.

About

Simple kotlin multiplatform socket wrapper

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published