Skip to content

Commit 0056d6d

Browse files
authored
doc updates + version bump (#36)
* first comments + version bump * more updates + cleanups * fix send return * small docs for writer --------- Co-authored-by: Frank Lee <>
1 parent 5fc3314 commit 0056d6d

File tree

8 files changed

+118
-114
lines changed

8 files changed

+118
-114
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bluefin"
3-
version = "0.1.5"
3+
version = "0.1.6"
44
edition = "2021"
55
description = "An experimental, secure, P2P, transport-layer protocol."
66
license = "MIT"

README.md

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# Bluefin
22

3-
`Bluefin` is an experimental, P2P, transport-layer protocol.
3+
`Bluefin` is an experimental, P2P, transport-layer protocol. Unlike TCP, `Bluefin` runs in user-space and can allow for
4+
faster development cycles, greater flexibility in new features and more performant resource management compared to
5+
kernel-space implementations.
6+
`Bluefin` is currently only supported on MacOs and Linux.
47

5-
[![Latest Version]][crates.io]
8+
[![Latest Version]][crates.io]
69
[![Documentation]][docs.rs]
710
![Github Workflow](https://github.com/franklee26/bluefin/actions/workflows/bluefin.yml/badge.svg)
811
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
912
[![codecov](https://codecov.io/github/franklee26/bluefin/graph/badge.svg?token=U0XPUZVE0I)](https://codecov.io/github/franklee26/bluefin)
1013

1114
### Example
15+
1216
#### Pack-leader
17+
1318
```rust
1419
#[tokio::main]
1520
async fn main() -> BluefinResult<()> {
@@ -19,36 +24,18 @@ async fn main() -> BluefinResult<()> {
1924
)));
2025
server.bind().await?;
2126

22-
const MAX_NUM_CONNECTIONS: usize = 5;
23-
for _ in 0..MAX_NUM_CONNECTIONS {
24-
let mut s = server.clone();
27+
while let Ok(conn) = s.accept().await {
2528
let _ = spawn(async move {
29+
let mut recv_bytes = [0u8; 1024];
2630
loop {
27-
let _conn = s.accept().await;
28-
29-
match _conn {
30-
Ok(mut conn) => {
31-
spawn(async move {
32-
loop {
33-
let mut recv_bytes = [0u8; 1024];
34-
let size = conn.recv(&mut recv_bytes, 100).await.unwrap();
35-
36-
println!(
37-
"({:x}_{:x}) >>> Received: {:?}",
38-
conn.src_conn_id,
39-
conn.dst_conn_id,
40-
&recv_bytes[..size],
41-
);
42-
sleep(Duration::from_secs(1)).await;
43-
}
44-
});
45-
}
46-
Err(e) => {
47-
eprintln!("Could not accept connection due to error: {:?}", e);
48-
}
49-
}
50-
51-
sleep(Duration::from_secs(1)).await;
31+
let size = conn.recv(&mut recv_bytes, 1024).await.unwrap();
32+
33+
println!(
34+
"({:x}_{:x}) >>> Received: {:?}",
35+
conn.src_conn_id,
36+
conn.dst_conn_id,
37+
&recv_bytes[..size],
38+
);
5239
}
5340
});
5441
}
@@ -58,34 +45,35 @@ async fn main() -> BluefinResult<()> {
5845
loop {}
5946
}
6047
```
48+
6149
#### Client
50+
6251
```rust
6352
#[tokio::main]
6453
async fn main() -> BluefinResult<()> {
65-
let task = spawn(async move {
66-
let mut client = BluefinClient::new(std::net::SocketAddr::V4(SocketAddrV4::new(
54+
let mut client = BluefinClient::new(std::net::SocketAddr::V4(SocketAddrV4::new(
55+
Ipv4Addr::new(192, 168, 1, 38),
56+
1234,
57+
)));
58+
let mut conn = client
59+
.connect(std::net::SocketAddr::V4(SocketAddrV4::new(
6760
Ipv4Addr::new(192, 168, 1, 38),
68-
1234,
69-
)));
70-
let mut conn = client
71-
.connect(std::net::SocketAddr::V4(SocketAddrV4::new(
72-
Ipv4Addr::new(192, 168, 1, 38),
73-
1235,
74-
)))
75-
.await?;
76-
77-
let bytes = [1, 2, 3, 4];
78-
let mut size = conn.send(&bytes).await?;
79-
println!("Sent {} bytes", size);
80-
81-
Ok::<(), BluefinError>(())
82-
});
61+
1235,
62+
)))
63+
.await?;
64+
65+
let bytes = [1, 2, 3, 4];
66+
let size = conn.send(&bytes);
67+
println!("Sent {} bytes", size);
68+
8369
Ok(())
8470
}
8571
```
8672

87-
8873
[Latest Version]: https://img.shields.io/crates/v/bluefin.svg
74+
8975
[crates.io]: https://crates.io/crates/bluefin
76+
9077
[Documentation]: https://docs.rs/bluefin/badge.svg
78+
9179
[docs.rs]: https://docs.rs/bluefin

src/core/header.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use crate::utils::common::BluefinResult;
33
use super::{error::BluefinError, Serialisable};
44

55
/// 4 bits reserved for PacketType => 16 possible packet types
6-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
77
pub enum PacketType {
88
UnencryptedClientHello = 0x00,
99
UnencryptedServerHello = 0x01,
1010
ClientAck = 0x02,
11+
#[default]
1112
UnencryptedData = 0x03,
1213
Ack = 0x04,
1314
}
@@ -26,7 +27,7 @@ impl PacketType {
2627
}
2728

2829
/// This struct contains the encryption flag and header-protection fields for a total of 8 bits
29-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
3031
pub struct BluefinSecurityFields {
3132
/// header_encrypted is one bit and signals whether the header contains encrypted fields
3233
header_encrypted: bool,
@@ -87,7 +88,7 @@ impl Serialisable for BluefinSecurityFields {
8788
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8889
/// ```
8990
///
90-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
91+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
9192
pub struct BluefinHeader {
9293
// The version is 4 bits
9394
pub version: u8,

src/core/packet.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,10 @@ impl Default for BluefinPacket {
5555
#[allow(invalid_value)]
5656
#[inline]
5757
fn default() -> Self {
58-
// SAFETY
59-
// Actually, this isn't safe and access to this kind of zero'd value would result
60-
// in panics. There does not exist a 'default' bluefin packet. Therefore, the
61-
// purpose of this is to quickly instantiate a 'filler' bluefin packet BUT this
62-
// default value should NEVER be read/used.
63-
unsafe { std::mem::zeroed() }
58+
Self {
59+
header: Default::default(),
60+
payload: vec![],
61+
}
6462
}
6563
}
6664

src/net/connection.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,11 @@ impl BluefinConnection {
305305
#[inline]
306306
pub async fn recv(&mut self, buf: &mut [u8], len: usize) -> BluefinResult<usize> {
307307
let (size, _) = self.reader_rx.read(len, buf).await?;
308-
return Ok(size as usize);
308+
Ok(size as usize)
309309
}
310310

311311
#[inline]
312312
pub fn send(&mut self, buf: &[u8]) -> BluefinResult<usize> {
313-
// TODO! This returns the total bytes sent (including bluefin payload). This
314-
// really should only return the total payload bytes
315-
self.writer_handler.send_data(buf)?;
316-
Ok(buf.len())
313+
self.writer_handler.send_data(buf)
317314
}
318315
}

src/worker/conn_reader.rs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ use tokio::sync::mpsc::{self};
55
use crate::core::error::BluefinError;
66
use crate::core::header::PacketType;
77
use crate::core::packet::BluefinPacket;
8+
use crate::core::Extract;
89
use crate::net::ack_handler::AckBuffer;
910
use crate::net::connection::ConnectionBuffer;
1011
use crate::net::{ConnectionManagedBuffers, MAX_BLUEFIN_BYTES_IN_UDP_DATAGRAM};
1112
use crate::utils::common::BluefinResult;
1213
use std::sync::{Arc, MutexGuard};
1314

15+
/// This is arbitrary number of worker tasks to use if we cannot decide how many worker tasks
16+
/// to spawn.
1417
const DEFAULT_NUMBER_OF_TASKS_TO_SPAWN: usize = 3;
1518

19+
/// [ConnReaderHandler] is a handle to network read-related functionalities. As the name suggests,
20+
/// we this handler is specific for *connection* reads. That is, this handler can only be used
21+
/// when a Bluefin connection has been established. This reader is fundamentally different from that
22+
/// of the [crate::worker::reader::ReaderRxChannel] as this will only read packets from the wire
23+
/// intended for the connection.
1624
pub(crate) struct ConnReaderHandler {
1725
socket: Arc<UdpSocket>,
1826
conn_bufs: Arc<ConnectionManagedBuffers>,
@@ -23,26 +31,44 @@ impl ConnReaderHandler {
2331
Self { socket, conn_bufs }
2432
}
2533

34+
/// Starts the handler worker jobs. This starts the worker tasks, which busy-polls a connected
35+
/// UDP socket for packets. Upon receiving bytes, these workers will send them to another
36+
/// channel for processing. Then second kind of worker is the processing channel, which receives
37+
/// bytes, attempts to deserialise them into bluefin packets and buffer them in the correct
38+
/// buffer.
2639
pub(crate) fn start(&self) -> BluefinResult<()> {
2740
let (tx, rx) = mpsc::channel::<Vec<BluefinPacket>>(1024);
28-
for _ in 0..Self::get_num_cpu_cores() {
41+
42+
// Spawn n-number of UDP-recv tasks.
43+
for _ in 0..Self::get_number_of_tx_tasks() {
2944
let tx_cloned = tx.clone();
3045
let socket_cloned = self.socket.clone();
3146
spawn(async move {
3247
let _ = ConnReaderHandler::tx_impl(socket_cloned, tx_cloned).await;
3348
});
3449
}
3550

51+
// Spawn the corresponding rx channel which receives bytes from the tx channel and processes
52+
// bytes and buffers them.
3653
let conn_bufs = self.conn_bufs.clone();
3754
spawn(async move {
3855
let _ = ConnReaderHandler::rx_impl(rx, &*conn_bufs).await;
3956
});
4057
Ok(())
4158
}
4259

60+
/// For linux, we return the expected number of CPU cores. This lets us take advantage of
61+
/// parallelism. For (silicon) macos, we return one. Experiments on Apple Silicon have shown
62+
/// that SO_REUSEPORT does not behave the same way as it does on Linux
63+
/// (see: https://stackoverflow.com/questions/51998042/macos-so-reuseaddr-so-reuseport-not-consistent-with-linux)
64+
/// and so we cannot take advantage of running the rx-tasks on multiple threads. For now, running
65+
/// one instance of it is performant enough.
66+
///
67+
/// For all other operating systems (which is currently unsupported by Bluefine anyways), we
68+
/// return an arbitrary default value.
4369
#[allow(unreachable_code)]
4470
#[inline]
45-
fn get_num_cpu_cores() -> usize {
71+
fn get_number_of_tx_tasks() -> usize {
4672
// For linux, let's use all the cpu cores available.
4773
#[cfg(target_os = "linux")]
4874
{
@@ -63,6 +89,10 @@ impl ConnReaderHandler {
6389
DEFAULT_NUMBER_OF_TASKS_TO_SPAWN
6490
}
6591

92+
/// This represents one tx task or one of the multiple producers in the mpsc channel. This
93+
/// function is a hot-loop; it continuously reads from a connected socket. When bytes are
94+
/// received, we attempt to deserialise them into bluefin packets. If valid packets are
95+
/// produced, them we send them to the consumer channel for processing.
6696
#[inline]
6797
async fn tx_impl(
6898
socket: Arc<UdpSocket>,
@@ -73,14 +103,12 @@ impl ConnReaderHandler {
73103
let size = socket.recv(&mut buf).await?;
74104
let packets = BluefinPacket::from_bytes(&buf[..size])?;
75105

76-
if packets.len() == 0 {
77-
continue;
78-
}
79-
80106
let _ = tx.send(packets).await;
81107
}
82108
}
83109

110+
/// This is the single consumer in the mpsc channel. This receives bluefin packets from
111+
/// n-producers. We place the packets into the relevant buffer.
84112
#[inline]
85113
async fn rx_impl(
86114
mut rx: mpsc::Receiver<Vec<BluefinPacket>>,
@@ -103,7 +131,14 @@ impl ConnReaderHandler {
103131
return Ok(());
104132
}
105133

106-
// Peek at the first packet and acquire the buffer.
134+
// Peek at the first packet and acquire the buffer. The assumptions here are:
135+
// 1. An udp datagram contains one or more bluefin packets. However, all the packets
136+
// in the datagram are for the same connection (no mix and matching different connection
137+
// packets in the same datagram).
138+
// 2. An udp datagram contains the same type of packets. This means a udp datagram either
139+
// contains all data-type packets or ack-packets.
140+
// Therefore, with these assumptions, we can just peek at the first packet in the datagram
141+
// and then acquire the appropriate lock before processing.
107142
let p = packets.first().unwrap();
108143
match p.header.type_field {
109144
PacketType::Ack => {
@@ -142,8 +177,8 @@ impl ConnReaderHandler {
142177
packets: Vec<BluefinPacket>,
143178
) -> BluefinResult<()> {
144179
let mut e: Option<BluefinError> = None;
145-
for p in packets {
146-
if let Err(err) = guard.buffer_in_bytes(p) {
180+
for mut p in packets {
181+
if let Err(err) = guard.buffer_in_bytes(p.extract()) {
147182
e = Some(err);
148183
}
149184
}

0 commit comments

Comments
 (0)