【Rust】Peer情報をnode ip listに書き込む

bitcoinのIPアドレスリストは、ipアドレスとUnix timeが対になっている
(参考↓)
https://qiita.com/onokatio/items/04f23b7300dec7e287cb
その他にも、ipv4/ipv6や、ping, lastsend, lastrec, id, versionなどの情報も含まれる。

handshake後にpeers.datのファイルに書き込むようにして、ping, pongの箇所は、enumでactive/inactiveのstatusで表現することにした。

use serde::{Serialize, Deserialize};
use std::{io::Write};
use chrono::{Utc,DateTime};
use std::fs::OpenOptions;

#[derive(Serialize, Deserialize, Clone, Debug)]
enum Status {
    ACTIVE,
    INACTIVE,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
struct Peer {
    ip: String,
    unixtime: i64,
    nodetype: String,
    version: String,
    status: Status,
}

fn main(){
    let utc_datetime: DateTime<Utc> = Utc::now();
    println!("{}", utc_datetime.timestamp());

    let peer = Peer {ip: "192.168.33.10".to_string(), unixtime: utc_datetime.timestamp(), nodetype: "c".to_string(), version: "1.0.1".to_string(), status: Status::ACTIVE};
    println!("{:?}", peer);
    let serialized: Vec<u8> = serde_json::to_vec(&peer).unwrap();
    let mut file_ref = OpenOptions::new()
                        .append(true)
                        .open("./data/peers.dat")
                        .expect("Unable to open file");
    file_ref.write_all(&serialized).expect("write failed");
    file_ref.write_all(b"\n").expect("write failed");
}

Running `target/debug/sample`
1740366182
Peer { ip: “192.168.33.10”, unixtime: 1740366182, nodetype: “c”, version: “1.0.1”, status: ACTIVE }

nodeipリストをどうゆう風にするか、ファイルを分割するか、データ型などずっと悩んでいたけど、他のチェーンの設計は参考になりますね。

ちなみに取り出す際↓

fn get_peers() -> Result<Vec<Peer>, Box<dyn std::error::Error>>  {

    let mut peers: Vec<Peer> = Vec::new();
    for result in BufReader::new(File::open("./data/peers.dat")?).lines() {
        let line: Peer = serde_json::from_str(&result?).unwrap();
        peers.push(line);
    }
    Ok(peers)
}

【Rust】reqwestでVPSのaxumにPOSTできるかテストする

reqwestでipを指定して送る場合は、client.post(“***.**.***.**:3000/post”) だと送れないが、client.post(“http://***.**.***.**:3000/post”) だと問題なく送ることができる。

送る側(ローカル開発環境)

async fn test()-> Result<(), Box<dyn std::error::Error>> {
    let n = Name { family: "tanaka".to_string(), first:"taro".to_string(), age: 20 };
    let json = json!(n);
    println!("{:?}", &json);
    let client = reqwest::Client::new();
    let resp = client.post("http://***.**.***.**:3000/post")
        .json(&json)
        .send()
        .await?;
    let body = resp.text().await?;   
    println!("{}", body);
    Ok(())
}

受け取る側(VPS: ***.**.***.**)

async fn main() {

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/post", post(handle_post))
        .route("/test", get(handle_test));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn handle_post(extract::Json(name): extract::Json<Name>) ->impl IntoResponse{
    println!("{:?}", name);
    return "All OK".into_response();
}

受け取る側
$ cargo run
Compiling axum v0.1.0 (/home/ubuntu/test/rust_test)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.90s
Running `target/debug/axum`
Name { family: “sato”, first: “taro”, age: 50 }
Name { family: “tanaka”, first: “taro”, age: 20 }

送る側
Compiling axum v0.1.0 (/home/vagrant/dev/rust/axum)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.59s
Running `target/debug/axum`
Object {“age”: Number(20), “family”: String(“tanaka”), “first”: String(“taro”)}
All OK

reqwestが使えるというのがわかったのは収穫だが、IPだけでなく、httpsかhttpかというプロトコルも一緒に取得する必要がある。

discussion
https://github.com/tokio-rs/axum/discussions/858

ちなみに、VPSでweb socketで待ち受けて、ws://***.**.***.**:3000/ws で接続しても問題なく接続できた。

async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>, ws: WebSocketUpgrade) -> Response {
    println!("{}", addr);
    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    while let Some(msg) = socket.recv().await {
        let mut msg = if let Ok(mut msg) = msg {
            msg
        } else {
            return;
        };
        if socket.send(msg).await.is_err() {
            // client disconnected
            return;
        }
    }
}

なるほど、reqwestが試せるだけで大分進んだ気がする。

【Rust】ダイクストラ法

fn dijkstra(edges: Vec<Vec<[usize; 2]>>, num_v: usize)  -> Vec<usize>{
    let inf = 999;
    let mut dist = Vec::new();
    for _ in 0..num_v {
        dist.push(inf);
    }
    dist[0] = 0;

    let mut q = Vec::new();
    for i in 0..num_v {
        q.push(i);
    }
    
    while *&q.len() > 0 {
        let mut r = q[0];
        for i in &q{
            if dist[*i] < dist[r] {
                r = *i;
            }
        } 
        let n = q.iter().position(|n| *n == r).unwrap();
        let u = q[n];
        q.retain(|&x| x != u);
        
        for i in edges[u].clone() {
            if dist[i[0]] > dist[u] + i[1] {
                dist[i[0]] = dist[u] + i[1];
            }
        }      
    }
    return dist;
}

fn main() {
    let edges = vec![
        vec![[1, 4], [2, 3]],
        vec![[2, 1], [3, 1], [4, 5]],
        vec![[5, 2]],
        vec![[4, 3]],
        vec![[6, 2]],
        vec![[4, 1], [6, 4]],
        vec![],
    ];
    println!("{:?}", edges);

    println!("{:?}", dijkstra(edges, 7));
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/rust`
[[[1, 4], [2, 3]], [[2, 1], [3, 1], [4, 5]], [[5, 2]], [[4, 3]], [[6, 2]], [[4, 1], [6, 4]], []]
[0, 4, 3, 5, 6, 5, 8]

おおおお、これは凄いな…

【Rust】bellman fordアルゴリズム

最短距離を探すアルゴリズム

fn bellman_ford(edges: Vec<[usize; 3]>, num_v: usize)  -> Vec<usize>{
    let inf = 999;
    let mut dist = Vec::new();
    for _ in 0..num_v {
        dist.push(inf);
    }
    

    dist[0] = 0;
    let mut changed = true;

    while changed == true {
        changed = false;
        for edge in &edges {
            if dist[edge[1]] > dist[edge[0]] + edge[2] {
                dist[edge[1]] =  dist[edge[0]] + edge[2];
                changed = true;
            }
        }
    }
    return dist;
}

fn main() {
    let edges = vec![[0, 1, 4], [0, 2, 3], [1, 2, 1], [1, 3, 1], 
    [1, 4, 5], [2, 5, 2], [4, 6, 2], [5, 4, 1], 
    [5, 6, 4]];

    let dist = bellman_ford(edges, 7);
    println!("{:?}", dist);
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/rust`
[0, 4, 3, 5, 6, 5, 8]

いまいちよーわからん…

【Python】ipv4の操作

import socket

host = socket.gethostname()
print(host)

ip = socket.gethostbyname(host)
print(ip)

$ python3 test.py
vagrant
127.0.1.1

$ pip3 install netaddr

from netaddr import *

ip = IPAddress('127.0.1.1')
print(ip.version)

print(IPAddress('127.0.1.1').ipv6())

rustの場合、is_ipv4でipv4の判定、is_ipv6判定などができる。
なるほど、ipv4, v6は深掘りする必要がありそう。

【Rust】UDPのやり取り

ncコマンドの場合、”-u”がUDPを指定している
$ nc -u -l 8888
$ echo “hoge” | nc -u 127.0.0.1 8888

use std::net::UdpSocket;

fn main() -> std::io::Result<()> {
    {
        let socket = UdpSocket::bind("127.0.0.1:8080")?;

        let mut buf = [0; 100];
        let (amt, src) = socket.recv_from(&mut buf)?;

        let buf = &mut buf[..amt];
        buf.reverse();
        socket.send_to(buf, &src)?;
    }
    Ok(())
}

$ echo “hoge” | nc -u 127.0.0.1 8080

egoh

UDPのbindはできるけど、レスポンスが何かおかしい…

use std::net::UdpSocket;
use std::thread;
use std::str;

fn main() -> std::io::Result<()> {
    {
        let socket = UdpSocket::bind("127.0.0.1:8080")?;
        let mut buf = [0; 2048];
        loop {
            match socket.recv_from(&mut buf) {
                Ok((buf_size, src_addr)) => {
                    thread::spawn(move || {
                        let buf = &mut buf[..buf_size];
                        let req_msg = str::from_utf8(&buf).unwrap();
                        println!("request message: {:?}", req_msg);
                    });
                },
                Err(e) => {
                    println!("couldn't receive request: {:?}", e);
                }
            }
        }
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
Running `target/debug/app`
request message: “hoge\n”

なるほど、TCPだけでなく、UDPも割と簡単にできるのね。。音声、動画streamingはUDPが優れているが…

【Rust】Tokioとは?

Node.jsとGolangを合体させたもの
膨大なリクエストをシングルスレッド&イベントループ&非同期I/Oで効率よく処理するランタイム(ライブラリ)
tokioではOSではなくランタイムがタスクのスケジューリングを行う。タスク生成はtokio::spawnを呼び出す

use std::time::Duration;

#[tokio::main]
async fn main() {
    tokio::task::spawn(async {
        tokio::time::sleep(Duration::from_secs(2)).await;
        println!("Done");
    });

    std::thread::sleep(Duration::from_secs(3));
    println!("wake up");
}

シングルスレッド

use std::time::Duration;

#[tokio::main(flavor="current_thread")]
async fn main() {
    tokio::task::spawn(async {
        tokio::time::sleep(Duration::from_secs(2)).await;
        println!("Done");
    });

    std::thread::sleep(Duration::from_secs(3));
    println!("wake up");
}
use std::time::Duration;

#[tokio::main(flavor="multi_thread", worker_threads = 1)]
async fn main() {
    let cpus = num_cpus::get();
    println!("logical cores: {}", cpus);

    tokio::task::spawn(async move {
        println!("task 1 started..");
        tokio::time::sleep(Duration::from_secs(3)).await;
        println!("task 1 wake up");
    });

    tokio::task::spawn(async move {
        println!("task 2 started..");
        tokio::time::sleep(Duration::from_secs(1)).await;
        println!("task 2 wake up");
    });

    tokio::time::sleep(Duration::from_secs(5)).await;
    println!("wake up");
}

task 1 started..
task 2 started..
task 2 wake up
task 1 wake up
wake up

use std::time::Duration;
use futures::{stream::FuturesUnordered, StreamExt};
use std::thread;

#[tokio::main(flavor="multi_thread", worker_threads = 1)]
async fn main() {
    // console_subscriber::init();

    let mut tasks = FuturesUnordered::new();

    let h1 = tokio::spawn(async move {
        thread::sleep(Duration::from_secs(10)).await;
        println!("0 wake up");
    });
    tasks.push(h1);
    println!("0 started...");

    for i in 1..100 {
        let h2 = tokio::spawn(async move {
            tokio::time::sleep(Duration::from_secs(1)).await;
            println!("{i} woke up!");
        });
        tasks.push(h2);
        println!("{i} ...");
    }
    while let Some(item) = tasks.next().await {
        let () = item.unwrap();
    }
    println!("Done");
}

【Rust】axumでパスパラメータを取得して使用する

Postではなく、Getでデータを渡したいときに、パスパラメータを使用する
route(“/withdrawal/{param}” でパスパラメータを取得できる。以前は、route(“/withdrawal/:param” だったので変わったみたい。

html

<button onclick="location.href='./withdrawal/eyJ2ZXJzaW9uIjoiMC4xLjAiLCJ0aW1lIjoiMjAyNS0wMi0xNVQwNzoxNzo0Mi4xMDU3Njk4NDNaIiwic2VuZGVyIjoiMDQxMGY4NzQyOWU4OTQ5OGU5MjhkMDBiNmE2MTg2ZmRjOWNjYmRlMmNhNTVkNTc0NmYwZTFiZjhmMWExZmJkYjc'">ボタン</button>

axum

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/withdrawal/{param}", get(handle_withdrawal))

//

async fn handle_withdrawal(axum::extract::Path(param): axum::extract::Path<String>) {

    println!("{}", param);
}

なるほど〜