初学rust,使用协程
Tag rust, 协程, on by view 18

最近想使用rust写个tcp透明代理转发服务,这中间涉及到socket监听以及连接处理逻辑中连接后端服务并处理连接后的相关逻辑。

监听端口

fn main() {
    let skt = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();
    let address: SocketAddr = "0.0.0.0:3333".parse().unwrap();
    skt.set_ip_transparent(true).unwrap();
    skt.set_reuse_address(true).unwrap();
    skt.set_reuse_port(true).unwrap();
    skt.bind(&address.into()).unwrap();
    skt.listen(128).unwrap();

    let listener: TcpListener = skt.into();
    println!("Server listening on port 3333");

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                println!("peer addr: {}", stream.peer_addr().unwrap());
                thread::spawn(move || {
                    // connection succeeded
                    handle_client(stream)
                });
            }
            Err(e) => {
                println!("Error: {}", e);
                /* connection failed */
            }
        }
    }
    // close the socket server
    drop(listener);
}

处理客户端连接

fn handle_client(mut stream: TcpStream) {
    let x = get_original_destination_addr(&stream).unwrap();
    println!("target addr: {:?}", x);

    let mut client_stream = get_rs_connect_stream_by_ipport(x.to_string().as_str());
    println!("client stream: {:?}", client_stream);

    let mut data = [0 as u8; 50]; // using 50 byte buffer
    while match stream.read(&mut data) {
        Ok(size) => {
            if size == 0 {
                false
            } else {
                // echo everything!
                println!("len: {:?}", size);
                stream.write(&data[0..size]).unwrap();
                true
            }
        }
        Err(_) => {
            println!(
                "An error occurred, terminating connection with {}",
                stream.peer_addr().unwrap()
            );
            stream.shutdown(Shutdown::Both).unwrap();
            false
        }
    } {}
}

可以看到这里每接受一个连接,就会起一个线程,如果在handle_client里再连接后端的话,就需要针对client_stream再起一个线程在线程中执行死循环,否则死循环会卡主stream这个死循环。这样一来,一个链接就得起2个线程了,那么连接一多,线程就更多了,一个进程能创建的线程数是有限的,因为线程对资源占用相对比较大,这样连接数一多,系统资源就不够用,性能就会很差。

对比到golang中的协程,我们能否在rust中使用协程呢?答案是肯定的。下面简单介绍一下rust中如何使用协程。rust中使用“协程”,我们用到tokio这个包。

对于单个异步,我们可以用async和await就可以了

#[tokio::main]
async fn main() {
    let skt = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();
    let address: SocketAddr = "0.0.0.0:3333".parse().unwrap();
    skt.set_ip_transparent(true).unwrap();
    skt.set_reuse_address(true).unwrap();
    skt.set_reuse_port(true).unwrap();
    skt.bind(&address.into()).unwrap();
    skt.listen(128).unwrap();

    let listener: TcpListener = skt.into();
    println!("Server listening on port 3333");

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                println!("peer addr: {}", stream.peer_addr().unwrap());
                handle_client(stream).await  // 其中 handle_client 改为了 async 异步函数
            }
            Err(e) => {
                println!("Error: {}", e);
                /* connection failed */
            }
        }
    }
    // close the socket server
    drop(listener);
}

其中 handle_client 是 async 异步函数,但是如果我想在handle_client中再起一个异步函数,直接使用async是不行的。我们可以这样处理,使用tokio::spawn就可以了,这里tokio::spawn有点类似golang中的go关键词,调用tokio::spawn的地方可以起一个异步函数。

async fn rs_handle(rs_stream: Arc<TcpStream>, stream: Arc<TcpStream>) {
    let mut client_data = [0 as u8; 50]; // using 50 byte buffer
    while match rs_stream.as_ref().read(&mut client_data) {
        Ok(client_size) => {
            if client_size == 0 {
                false
            } else {
                println!("len: {:?}", client_size);
                stream.as_ref().write(&client_data[0..client_size]).unwrap();
                true
            }
        }
        Err(_) => {
            println!("client error occurred.");
            false
        }
    } {}
}

async fn handle_client(stream: TcpStream) {
    let x = get_original_destination_addr(&stream).unwrap();
    println!("target addr: {:?}", x);

    let stream = Arc::new(stream);
    let rs_stream = Arc::new(get_rs_connect_stream_by_ipport(x.to_string().as_str()));
    println!("rs stream: {:?}", rs_stream);

    let rsh_rs_stream = rs_stream.clone();
    let rsh_stream = stream.clone();
    tokio::spawn(rs_handle(rsh_rs_stream, rsh_stream)); // 这里相当于起一个线程
    println!("rs handle setted.");

    let mut data = [0 as u8; 50]; // using 50 byte buffer
    while match stream.as_ref().read(&mut data) {
        Ok(size) => {
            if size == 0 {
                false
            } else {
                rs_stream.as_ref().write(&data[0..size]).unwrap();
                true
            }
        }
        Err(_) => {
            println!(
                "An error occurred, terminating connection with {}",
                stream.peer_addr().unwrap()
            );
            stream.shutdown(Shutdown::Both).unwrap();
            false
        }
    } {}
}

如上代码,首先需要定义一个异步函数async fn rs_handle(...)然后在调用的地方使用tokio::spawn(rs_handle(...))来调用。就可以实现同等于golang中go关键词的效果,起一个协程。


如何投诉快递公司
Tag 投诉, 快递, on by view 22

快递不派件,丢件。找快递公司,快递公司不理会怎么办?相信很多人都遇到过这种情况。简单讲一下我遇到的情况,我有一个快递14号显示派送中,然后就显示疫情原因派送失败,然后我就想既然是疫情导致的,就等等吧,这一等就等到了月底,都两周多了。于是我打电话给快递总公司,找人工服务,对面说他们会联系一下快递网点公司,然后就没消息了,于是等了一个星期我再打电话给快递总公司,他给我发了一个快递网点公司的电话,让我联系快递网点。我联系了快递网点,对面又说他问一下派件业务员,然后就又是没消息了,于是又等了一个星期。

upcqvz8r

我终于不愿意再等了,没有哪个网点的疫情防控防控半个月的吧。接下来我这样操作

首先,到国家邮政业申诉服务平台(https://sswz.spb.gov.cn/index.html)注册账号并登录。

然后,点我要申诉,填写申诉相关信息。申诉单创建了,如下图

p9qx2eek

当天我给快递总公司去电三次,没能接通,给网点去电两次,没能接通。直到我 12:56 在邮政投诉平台提单投诉,然后,13:00 立马就收到了快递公司的短信,说会有专人跟进处理,14:38 立马快递总公司来电联系我,不出2小时就立马响应我的申诉单了。当天快递公司和网点先后电话联系我5次,最终告诉我快递没下落了,等他查一下监控,然后又说加我微信,如果找不到的话,就给我理赔,加了微信最后告诉我快递找不到了,因为有业务员离职了交接没搞清楚导致的,然后给我按原价赔偿了120块钱。

这快递公司是真的狗,我先后好声好气的联系他们,都如同石沉大海,不给我反馈。快递状态上拿疫情糊弄我。如果不是我了解到邮政投诉,估计这单快递就黄了,损失就是我的了。特此记录一下,有遇到相同情况的人,可以试一下邮政投诉。注意这里的邮政是政府机构,邮件政务管理部门的简称,并不是邮政快递。邮政是一个政务部门,在我国早期叫做邮电,也分政务部门和业务部门,政务部门负责管理,业务部门负责信件和电报业务,后来随着国家发展,邮电被拆分为邮政和电信,电信又发转为三大运营商,所以现在邮政快递是业务部门,邮政是政务部门负责管理包括邮政快递在内的所有快递公司,工信部是三大运营商的政务部门负责管理三大运营商。去政务部门“邮政”去投诉快递公司,一投一个准。快递公司绝对不敢怠慢。