< 返回版块

OU_L 发表于 2021-06-10 03:39

Tags:Unix socket, 范形值传递, I/O buf声明

本人rust刚入门小菜鸡一枚

最近在尝试使用rust做socket编程的东西,有一个关于Unix socket之间传递范型值的问题,绞尽脑汁试了好多种方法也没想明白怎么解决,所以来请教各位大佬。

问题是这样的:

1.现在客户端进程和服务器进程的unix socket绑定地址且连接完毕(已验证是全双工)。

2.我需要从客户端发送一个数值给服务端,例如一个整数66,服务端收到这个整数之后进行奇偶判断,然后返回一个Bool值给客户端,客户端收到Bool值之后进行打印。所以我希望发送通道能够发送一个范型数据,接收通道能够接收它平且正确返回。
我写的发送函数和接受函数代码思路大致如下:

let (sender_socket, receiver_socket) = (UnixSocket::new(libc::AF_UNIX, 0).unwrap(), UnixSocket::new(libc::AF_UNIX, 0).unwrap());
    //bind connect listen accept ...
unsafe fn send<A:marker::Send + 'static>(sender_socket,x:A){
    let buffer = [Box::into_raw(Box::new(x)) as *const _];
    //let buffer = [x];
    sender_socket.write(&buffer).unwrap();
    println!("send buffer: {:?}", buffer);
    }
unsafe fn recv<A: marker::Send + 'static>(receiver_socket) -> A {
    let mut buffer = [0];
    let a = receiver_socket.recv(&mut buffer).unwrap();
    println!("a: {}", a);
    println!("buffer:{:?}", buffer);
    *Box::from_raw( buffer[0] as *mut A)
    }

3.但是当我运行的时候却出现了错误:

send buffer: [0x7f8170000c90]  

a: 1  

buffer:[144]  

Segmentation fault (core dumped)  

发送端能够正确发送指针,但是接收端却不能正确接收指针。

我觉得可能是我的接受buffer还有最后的解引用步骤不正确,尝试了很多方法也没有解决。

我也尝试过不通过指针,直接发送数据,虽然能够正确接收数据,但是最后转换为A类型返回时出现问题。

想问一下有什么办法能够正确的收到并解引用范型数据的指针并取得数据?? 或者直接传递范型数值之后如何在接收端正确return??

因为是刚开始学rust,很多东西理解的还不是很深,也不知道该从哪里出发解决问题,所以来求助各位大佬。。。 时间有点晚脑子比较混乱我也不确定我问题描述的是否准确。。。。。。 还请各位见谅。。。。

评论区

写评论
作者 OU_L 2021-06-10 15:57

好的好的非常感谢~我去做一下。

因为刚学习Rust对socekt编程也比较感兴趣,想着实践出真知哈哈,所以就参照了好多标准库,练习写了一个自己的socket库,想着加深自己对这方面细节的理解。INET domain和UNIXdomain都实现了,后期也会po出来和大家一起分享讨论,毕竟我觉得新手写的东西还是很不完善的。

操作系统进程间传递指针这个确实不了解,还是需要多学习,哈哈非常感谢大佬,又学习到了~

--
👇
ruby: 另外,千万不要因为是一台机器的两个进程,所以就传递指针。

因为现在操作系统每个进程都是不一样的「虚拟内存映射」

你把进程1的指针地址0xff传递给进程2,肯定会segfault段错误

由于不同进程虚拟内存映射不同,进程1的指针地址0xff假设映射到物理内存0x3f,但是进程2的虚拟内存地址0xff就不一定映射成物理内存0x3f。我看过那么多FFI调用的代码都没见过你这种传指针的骚操作

这种基本的操作系统常识要有,建议多看看书多看看论文

ruby 2021-06-10 15:09

另外,千万不要因为是一台机器的两个进程,所以就传递指针。

因为现在操作系统每个进程都是不一样的「虚拟内存映射」

你把进程1的指针地址0xff传递给进程2,肯定会segfault段错误

由于不同进程虚拟内存映射不同,进程1的指针地址0xff假设映射到物理内存0x3f,但是进程2的虚拟内存地址0xff就不一定映射成物理内存0x3f。我看过那么多FFI调用的代码都没见过你这种传指针的骚操作

这种基本的操作系统常识要有,建议多看看书多看看论文

ruby 2021-06-10 15:05

我直接按你思路用标准库的UnixListener写了个客户端请求一个i32类型server返回false(不是奇数)的示例,你可以参考下。

话说你用的 UnixSocket::new 这是哪个库?标准库没有这个API,好奇问下

Server Incomming Request: I32(66)

Server Preprare Response: Bool(false)

use std::{io::{Read, Write}, os::unix::net::{UnixListener, UnixStream}};

#[derive(serde::Serialize, serde::Deserialize, Debug)]
enum Message {
    I32(i32),
    Bool(bool)
}

const UNIX_SOCK_ADDR: &str = "/home/w/temp/my_sock.sock11";

#[test]
fn unix_socket_server() -> Result<(), Box<dyn std::error::Error>> {
    let listener = UnixListener::bind(UNIX_SOCK_ADDR)?;
    for mut stream in listener.incoming().flatten() {
        let mut read_buffer = Vec::new();
        stream.read_to_end(&mut read_buffer)?;
        let request: Message = bincode::deserialize(&read_buffer[..])?;
        println!("Server Incomming Request: {:?}", request);
        let response = match request {
            Message::I32(int) => Message::Bool(int % 2 == 1),
            _ => panic!("unsupported message type")
        };
        println!("Server Preprare Reponse: {:?}", response);
        // stream.write(&bincode::serialize(&response)?)?;
        // stream.flush()?;
        drop(stream);
    }
    Ok(())
}

#[test]
fn unix_socket_client() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = UnixStream::connect(UNIX_SOCK_ADDR)?;
    let request = Message::I32(66);
    stream.write(&bincode::serialize(&request)?)?;
    stream.flush()?;
    Ok(())
}

总之我这么写server和client共用一个enum Message,所以可以支持不同类型

作者 OU_L 2021-06-10 13:03

好的好的我去试试,现在是用rust Unix socket在同一台主机的两个进程间通信,谢谢你的建议~

--
👇
ruby: 你应该类似protobuf那样定义数据格式,如果两端都是rust推荐用bincode

然后你就可以用枚举了

enum Message { Bool(bool), I32(i32), bytes(Vec) }

例如发送端发送Message::I32(66),接受端用match判断数据是枚举中的哪一种类型,这就是你想要的泛型效果

作者 OU_L 2021-06-10 12:41

因为Unix socket是同主机两个进程间通信,所以就想着是不是可以传递指针?

--
👇
Grobycn: 不能通过指针发送数据。

打个比方,我忘记带钥匙,叫你帮我拿一下,对你说“钥匙忘在卧室床上了,帮我拿一下”,你在你自己家里面找钥匙,那肯定找不到啊。

ruby 2021-06-10 09:46

你应该类似protobuf那样定义数据格式,如果两端都是rust推荐用bincode

然后你就可以用枚举了

enum Message { Bool(bool), I32(i32), bytes(Vec) }

例如发送端发送Message::I32(66),接受端用match判断数据是枚举中的哪一种类型,这就是你想要的泛型效果

Grobycn 2021-06-10 08:33

不能通过指针发送数据。

打个比方,我忘记带钥匙,叫你帮我拿一下,对你说“钥匙忘在卧室床上了,帮我拿一下”,你在你自己家里面找钥匙,那肯定找不到啊。

1 共 7 条评论, 1 页