Rust Learning from Zero (20) —— Sobani Tracker in Rust

A few weeks ago, Ayaka and me started a project named Sobani, a simple cross platform UDP-based P2P audio sharing application for couples, friends, coworkers and etc. Sobani also has a public tracker server for storing all its peers' connection info.

Well, given that the last time that I wrote any Rust code was quite a while ago, I decided to rewrite the Sobani tracker in Rust. The Rust version of Sobani tracker is on BlueCocoa/sobani-tracker-rust.

In brief, this time I'd like to write this Rust project with async-std, and the tracker service will be encapsulated in SobaniTracker class. The SobaniTracker will be initialised with an IP:Port string as the listening endpoint for its UDP socket. Let's also add pretty_env_logger for beautiful logging.

Without further ado, the main.rs is shown below, which should be rather straightforward.

extern crate pretty_env_logger;
#[macro_use] extern crate log;

mod server;
use server::SobaniTracker;

#[async_std::main]
async fn main() -> std::io::Result<()> {
    pretty_env_logger::init();
    let mut server = SobaniTracker::config("0.0.0.0:3000");
    server.start().await?;
    Ok(())
}

As for the SobaniTracker (in server.rs), it should hold its listening endpoint, e.g. 0.0.0.0:3000, the internal UDP socket and all clients' connection info. And because the server will handle multiple clients in parallel, I added Mutex on clients so that the connection data can be consistent.

use async_std::net::UdpSocket;
use serde_json::json;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::sync::Mutex;
use sha2::{Sha256, Digest};

/// Sobani Tracker
///
/// This struct can be created by the [`server::SobaniTracker::config`] function. See its
/// documentation for more.
pub struct SobaniTracker {
    /// listening endpoint, `IP:Port` format
    bind: String,

    /// internal UDP socket
    socket: Option<UdpSocket>,

    /// bi-directional client mapping
    /// - maps from `IP:Port` to `ShareID`
    /// - maps `ShareID` to `IP:Port`
    clients: Mutex<HashMap<String, String>>
}

And the API of SobaniTracker goes below

impl SobaniTracker {
    /// Constructs a new `SobaniTracker` with listening endpoint at `bind`.
    ///
    /// # Example
    ///
    /// ```
    /// use server::SobaniTracker;
    ///
    /// let mut server = SobaniTracker::config("0.0.0.0:3000");
    /// ```
    pub fn config(bind: &str) -> SobaniTracker { /* */ }
    
    /// Starts the `SobaniTracker` server
    /// This function won't return
    ///
    /// # Exmaple
    /// 
    /// ```
    /// use server::SobaniTracker;
    ///
    /// let mut server = SobaniTracker::config("0.0.0.0:3000");
    /// server.start().await?;
    /// ```
    pub async fn start(&mut self) -> std::io::Result<()> { /* */ }
    
    /// Handles announce message from remote client
    /// This function will generate a `shareId` for the remote client
    async fn announce_handler(&self, remote: SocketAddr) -> Option<serde_json::Value> { /* */ }
    
    /// Handles push message from remote client
    /// The push message will be sent if a client (the requestor) wants to connect to
    /// another client (requestee) by requestee's shareId
    /// This function looks up the requested peer by `shareId` that sent in the push message
    /// And if both requestor and requestee have announced themselves on this tracker
    /// A pushed message containing the requestee's connection info will be sent back to the requestor
    /// An income message containing the requestor's connection info will be sent to the requestee
    async fn push_handler(&self, msg: serde_json::Value, remote: SocketAddr) -> Option<serde_json::Value> { /* */ }
}
Continue reading Rust Learning from Zero (20) —— Sobani Tracker in Rust

是否无力改变

很多时候能看到,在提出一些存在的问题之后,特别是关于政治上的,很多人也许先会同意问题存在,但是马上就会接一句“我们又能改变什么呢”

曾经觉得,比如有更强的力量去监督政府的话,还是可以改变的,可是这样的“更强的力量”又从哪里来呢?我们连杨永信都治不了,连遍布全国的这种所谓的“教育”机构都制裁不了,连一个有计划的强奸幼女的人都不能定罪,我们能做什么呢?

感觉到真实的无力,所有的这些残忍的事情不断的发生,发生到好多人都要麻木了甚至已经麻木,都要无所谓了,我们依旧没有什么办法……

回顾自己之前在知道某些事情的时候,要是自己有所行动的话,是不是也会不一样?只觉得自己什么都没有做到。现在去把那个地方拆了又能怎么样,站在当事人的角度,已经没有用了,唯一有用的就是那个时候能去带消息出来,能救她出来。现在再来说这些,无用也无力。

我们要拿什么去对抗这些啊……是不是这些都是自己无力改变的呢……

『側に / Sobani』 —— 在你身旁

Sobani —— Fully self-hostable and customizable media entertaining stream service based on Node.js for couples, friends, collaboration teams, and even quarantined office workers.

在几周前的一个晚上,跟猫猫闲聊时,猫猫有提到说「要是可以一起听音乐 / 看电影就好了」。虽然 Team Viewer 的音频共享可以将被连接的电脑的音频发送过来,同时也会将屏幕共享过来,在网络不太好的情况下音频会断断续续,同时视频的质量也不太高,体验会比较差QwQ 另外 Team Viewer 在开始语音的时候,就不能 forward 电脑输出的音频给对方了,因此还需要别的软件来做语音通话Σ(・□・;)

在一边想象着跟猫猫听音乐 / 看电影的时候,一边就在跟猫猫开始计划着写这个项目。每次开始写项目时,猫猫和 Cocoa 一致认为日常最难问题就是叫什么名字喵,一开始猫猫提到可以用「接続 / Setsuzoku」抽取一个部分和 link 接起来,也就是「setsulink」这样的;又或者是「cosetsu」,即 coop 和 setsuzoku。在我们纠结一段时间名字之后,

猫猫提议:把自己的心愿写进去

Cocoa:Sobani そばに?

猫猫:诶 是什么呀

Cocoa:就是在身边的意思,要说心愿的话,大概就是这个 w 想要在身边,但是又只能暂时用软件来实现啦

在决定好用这样的心愿起名字之后,就开始了这个满怀爱(狗)意(粮)的项目啦/ 头一次用 VS Code 的 Live Share 一起写代码!

那么想到的连接办法当然就是 NAT 穿透 了!在最开始的时候,我们决定使用的语言是 Go,然后本来是想用 libp2p 来完成的,但是后来验证发现,libp2p 默认使用的是 TCP 连接,虽然 TCP 的 NAT 穿透是有方法的,但是似乎实现起来会太复杂了,以及对于 Go 上跨平台的、好看的 UI 还没有什么头绪(>﹏<)

项目的地址在 nekomeowww/sobani,最新的 build 都放在 release 里面啦~同时支持 macOS / Linux / Windows 的说(*^3^)

公开的 tracker 服务器的地址是 34.80.41.119:3000,这个 Sobani 的 tracker 服务器也是可以自己搭建的/

Sobani tracker 的项目地址是 nekomeowww/sobani-tracker,里面的 deploy.sh 脚本在 Debian / Ubuntu 上验证过~

Continue reading 『側に / Sobani』 —— 在你身旁