Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An easy-to-use client mode #205

Open
dizda opened this issue Dec 12, 2019 · 8 comments
Open

An easy-to-use client mode #205

dizda opened this issue Dec 12, 2019 · 8 comments
Labels
p:wont-have Priority: not appropriate at that time

Comments

@dizda
Copy link

dizda commented Dec 12, 2019

Hi there,

First of all, thanks for the lib.
I'm able to connect to different peers, then send messages from one to another.
One thing I couldn't achieve so far is, to use a the underlying stream (or substream) in order to forward the data via (io::copy()), would is this possible?

Thank you.

@driftluo
Copy link
Collaborator

From the current architecture, io::copy cannot be used.

In order to allow users to implement different protocols in the same way, from the user's perspective, the first object is no longer a stream, but a user-defined protocol trait.

Therefore, at the framework level, the underlying stream needs to be hidden, and the session id is provided as an index for the protocol trait operation.

Therefore, the operation object of io::copy has been hidden inside the framework. However, where the protocol sends and receives messages, we use the data structure Bytes, which can actually be regarded as an Arc < u8> , the cost of direct forwarding after receiving the message is not much different from io::copy, because in fact, the data has already been copied from the kernel to the user mode.

@dizda
Copy link
Author

dizda commented Dec 12, 2019

That's what I thought, thanks.

What would be neat for my use case is to have some kind of callback, eg.:

    let callback: Bytes = service_control
        .send_message_to(1.into(), 0.into(), Bytes::from("query"))
        .await?;
    
    assert_eq!(Bytes::from("query_result: blablabla"), callback);

I wonder how difficult would it be to implement this? Or do you have better idea?

@driftluo
Copy link
Collaborator

I think I probably know what you want.

At present, you need to encapsulate it yourself. The easiest way is probably like this, you can change the channel to asynchronous, and then combine it with service_control to form a new structure, impl Future for it, so that you can await callback like your code

In fact, the essence of this problem is that at present, the framework only provides the writing of the server mode, and when you want to write a client directly based on the server, you will find that it is more cumbersome.

A simple and easy-to-use client requires some work to implement, temporarily not available, maybe after the upgrade and refactor, will consider this

@dizda
Copy link
Author

dizda commented Dec 12, 2019

Thanks @driftluo, I'll explore these possibilities.

@doitian
Copy link
Member

doitian commented Jan 13, 2020

Thanks for the reporting. We'll consider how to refactor the code to ease reusing in such scenarios. Please subscribe nervosnetwork/ckb#1912 if you want to know the latest progress.

@doitian doitian closed this as completed Jan 13, 2020
@driftluo driftluo reopened this Jan 13, 2020
@driftluo
Copy link
Collaborator

driftluo commented Jan 13, 2020

@doitian This issue has nothing to do with ckb, it is a missing implementation of p2p itself

@doitian
Copy link
Member

doitian commented Jan 13, 2020

@doitian This issue has nothing to do with ckb, it is a missing implementation of p2p itself

Got it. Could you help to revise the title to make it more clear?

@driftluo driftluo changed the title Use a stream An easy-to-use client mode Jan 13, 2020
@doitian doitian added the p:wont-have Priority: not appropriate at that time label Mar 2, 2020
@doitian
Copy link
Member

doitian commented Aug 12, 2020

That's what I thought, thanks.

What would be neat for my use case is to have some kind of callback, eg.:

    let callback: Bytes = service_control
        .send_message_to(1.into(), 0.into(), Bytes::from("query"))
        .await?;
    
    assert_eq!(Bytes::from("query_result: blablabla"), callback);

I wonder how difficult would it be to implement this? Or do you have better idea?

This is a good candidate to be implemented as a middleware. The tentacle has no concept of request and response. It just knows incoming and outgoing messages streams. But the application layer can add logic to associate an incoming message with a pending outgoing message as the response. For example, the middleware stores all the pending outgoing messages as requests as well as the corresponding replying oneshot channel. Once a message is received, the middleware matches it against the requests. If a matching request is found, the response is sent via the replying oneshot channel and the request is removed from the waiting queue.

The application is responsible to implement the request-response matching method. There are different strategies:

  • In the protocol design, all the request has a nonce field which must be unique in a long range of time. The response also has the nonce field and it is copied from the request.
  • The application has some other keys can be used as the identifier, such as the block hash in get_block message.
  • The application scan all the requests against the received the response.
  • The application uses a simple first in first out strategy to match requests and responses.

Since it is an extension of tentacle, we have no plan to implement such middleware yet. However, we'll keep this issue open to see who is interested to implement it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p:wont-have Priority: not appropriate at that time
Projects
None yet
Development

No branches or pull requests

3 participants