Skip to content

Commit

Permalink
net: add TcpListener & TcpStream APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Feb 22, 2023
1 parent 2702ffa commit 4d834f8
Show file tree
Hide file tree
Showing 18 changed files with 700 additions and 221 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ jobs:
run: make ARCH=${{ matrix.arch }} APP=exception
- name: Build multitask
run: make ARCH=${{ matrix.arch }} APP=multitask
- name: Build echoserver
- name: Build net/echoserver
run: make ARCH=${{ matrix.arch }} APP=echoserver
- name: Build net/httpclient
run: make ARCH=${{ matrix.arch }} APP=httpclient
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[workspace]

members = [
"apps/echoserver",
"apps/exception",
"apps/helloworld",
"apps/memtest",
"apps/multitask",
"apps/net/echoserver",
"apps/net/httpclient",

"crates/allocator",
"crates/crate_interface",
Expand Down
10 changes: 0 additions & 10 deletions apps/echoserver/src/main.rs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ authors = ["Yuekai Jia <[email protected]>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axruntime = { path = "../../modules/axruntime", features = ["paging", "net"] }
axnet = { path = "../../../modules/axnet" }
axerror = { path = "../../../modules/axerror" }
axruntime = { path = "../../../modules/axruntime", features = ["paging", "net"] }
58 changes: 58 additions & 0 deletions apps/net/echoserver/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#![no_std]
#![no_main]

#[macro_use]
extern crate axruntime;
extern crate alloc;

use alloc::vec::Vec;
use core::str::FromStr;

use axerror::AxResult;
use axnet::io::{Read, Write};
use axnet::{IpAddr, TcpListener, TcpStream};

fn reverse(buf: &[u8]) -> Vec<u8> {
let mut lines = buf
.split(|&b| b == b'\n')
.map(Vec::from)
.collect::<Vec<_>>();
for line in lines.iter_mut() {
line.reverse();
}
lines.join(&b'\n')
}

fn echo_server(mut stream: TcpStream) -> AxResult {
let mut buf = [0u8; 1024];
loop {
let n = stream.read(&mut buf)?;
if n == 0 {
return Ok(());
}
stream.write_all(reverse(&buf[..n]).as_slice())?;
}
}

fn accept_loop() -> AxResult {
let (addr, port) = (IpAddr::from_str("10.0.2.15").unwrap(), 5555);
let mut listener = TcpListener::bind((addr, port).into())?;
println!("listen on: {}", listener.local_addr().unwrap());

loop {
match listener.accept() {
Ok((stream, addr)) => {
println!("new client: {}", addr);
echo_server(stream)?;
println!("client closed");
}
Err(e) => return Err(e),
}
}
}

#[no_mangle]
fn main() {
println!("Hello, echo server!");
accept_loop().expect("test echo server failed");
}
12 changes: 12 additions & 0 deletions apps/net/httpclient/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "arceos-httpclient"
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <[email protected]>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
axnet = { path = "../../../modules/axnet" }
axerror = { path = "../../../modules/axerror" }
axruntime = { path = "../../../modules/axruntime", features = ["paging", "net"] }
37 changes: 37 additions & 0 deletions apps/net/httpclient/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#![no_std]
#![no_main]

#[macro_use]
extern crate axruntime;

use core::str::FromStr;

use axerror::AxResult;
use axnet::io::{Read, Write};
use axnet::{IpAddr, TcpStream};

const DEST_IP: &str = "49.12.234.183"; // ident.me
const REQUEST: &str = "\
GET / HTTP/1.1\r\n\
Host: ident.me\r\n\
Accept: */*\r\n\
\r\n";

fn client() -> AxResult {
let (addr, port) = (IpAddr::from_str(DEST_IP).unwrap(), 80);
let mut stream = TcpStream::connect((addr, port).into())?;
stream.write(REQUEST.as_bytes())?;

let mut buf = [0; 1024];
let n = stream.read(&mut buf)?;
let response = core::str::from_utf8(&buf[..n]).unwrap();
println!("{}", response);

Ok(())
}

#[no_mangle]
fn main() {
println!("Hello, simple http client!");
client().expect("test http client failed");
}
9 changes: 7 additions & 2 deletions modules/axdriver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ mod virtio;
use lazy_init::LazyInit;
use tuple_for_each::TupleForEach;

#[cfg(feature = "virtio-blk")]
pub use self::virtio::VirtIoBlockDev;
#[cfg(feature = "virtio-net")]
pub use self::virtio::VirtIoNetDev;

static DEVICES: LazyInit<AllDevices> = LazyInit::new();

#[derive(TupleForEach)]
pub struct BlockDevices(
#[cfg(feature = "virtio-blk")] pub self::virtio::VirtIoBlockDev,
#[cfg(feature = "virtio-blk")] pub VirtIoBlockDev,
// #[cfg(feature = "nvme")] pub nvme::NVMeDev,
);

#[derive(TupleForEach)]
pub struct NetDevices(
// TODO: move Mutex here
#[cfg(feature = "virtio-net")] pub self::virtio::VirtIoNetDev,
#[cfg(feature = "virtio-net")] pub VirtIoNetDev,
// #[cfg(feature = "e1000")] pub e1000::E1000Dev,
);

Expand Down
3 changes: 3 additions & 0 deletions modules/axerror/src/errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@
*/
#define ENOSYS 38 /* Invalid system call number */

#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ECONNREFUSED 111 /* Connection refused */

#endif
14 changes: 10 additions & 4 deletions modules/axerror/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ pub enum AxError {
AlreadyExists,
/// Bad address.
BadAddress,
/// The connection was refused by the remote server,
ConnectionRefused,
/// Invalid parameter/argument.
InvalidParam,
/// Input/output error.
Io,
/// Not enough space/cannot allocate memory.
NoMemory,
/// The network operation failed because it was not connected yet.
NotConnected,
/// The requested entity is not found.
NotFound,
/// The operation lacked the necessary privileges to complete.
Expand All @@ -34,12 +38,12 @@ pub type AxResult<T = ()> = Result<T, AxError>;
#[macro_export]
macro_rules! ax_err_type {
($err: ident) => {{
use $crate::error::AxError::*;
use $crate::AxError::*;
warn!("[AxError::{:?}]", $err);
$err
}};
($err: ident, $msg: expr) => {{
use $crate::error::AxError::*;
use $crate::AxError::*;
warn!("[AxError::{:?}] {}", $err, $msg);
$err
}};
Expand All @@ -48,10 +52,10 @@ macro_rules! ax_err_type {
#[macro_export]
macro_rules! ax_err {
($err: ident) => {
Err(rvm_err_type!($err))
Err($crate::ax_err_type!($err))
};
($err: ident, $msg: expr) => {
Err(rvm_err_type!($err, $msg))
Err($crate::ax_err_type!($err, $msg))
};
}

Expand All @@ -61,9 +65,11 @@ impl From<AxError> for LinuxError {
match e {
AlreadyExists => LinuxError::EEXIST,
BadAddress => LinuxError::EFAULT,
ConnectionRefused => LinuxError::ECONNREFUSED,
InvalidParam => LinuxError::EINVAL,
Io => LinuxError::EIO,
NoMemory => LinuxError::ENOMEM,
NotConnected => LinuxError::ENOTCONN,
NotFound => LinuxError::ENOENT,
PermissionDenied => LinuxError::EPERM,
ResourceBusy => LinuxError::EBUSY,
Expand Down
6 changes: 4 additions & 2 deletions modules/axnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ authors = ["Yuekai Jia <[email protected]>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
smoltcp = ["dep:smoltcp"]
smoltcp = []
default = ["axdriver/virtio-net", "smoltcp"]

[dependencies]
log = "0.4"
spin = "0.9"
cfg-if = "1.0"
driver_common = { path = "../../crates/driver_common" }
driver_net = { path = "../../crates/driver_net" }
lazy_init = { path = "../../crates/lazy_init" }
axhal = { path = "../axhal" }
axdriver = { path = "../axdriver" }
axerror = { path = "../axerror" }

[dependencies.smoltcp]
version = "0.9.1"
optional = true
default-features = false
features = [
"alloc", "log", # no std
Expand Down
64 changes: 64 additions & 0 deletions modules/axnet/src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use alloc::{string::String, vec::Vec};

use axerror::{ax_err, AxResult};

pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> AxResult<usize>;

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> AxResult<usize> {
let start_len = buf.len();
let mut probe = [0u8; 32];
loop {
match self.read(&mut probe) {
Ok(0) => return Ok(buf.len() - start_len),
Ok(n) => buf.extend_from_slice(&probe[..n]),
Err(e) => return Err(e),
}
}
}

fn read_to_string(&mut self, buf: &mut String) -> AxResult<usize> {
let old_len = buf.len();
let buf = unsafe { buf.as_mut_vec() };
let ret = self.read_to_end(buf)?;
if core::str::from_utf8(&buf[old_len..]).is_err() {
ax_err!(Io, "stream did not contain valid UTF-8")
} else {
Ok(ret)
}
}

fn read_exact(&mut self, mut buf: &mut [u8]) -> AxResult {
while !buf.is_empty() {
match self.read(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
ax_err!(Io, "failed to fill whole buffer")
} else {
Ok(())
}
}
}

pub trait Write {
fn write(&mut self, buf: &[u8]) -> AxResult<usize>;
fn flush(&mut self) -> AxResult;

fn write_all(&mut self, mut buf: &[u8]) -> AxResult {
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => return ax_err!(Io, "failed to write whole buffer"),
Ok(n) => buf = &buf[n..],
Err(e) => return Err(e),
}
}
Ok(())
}
}
Loading

0 comments on commit 4d834f8

Please sign in to comment.