Question

In Rust 0.9 and now in 0.10 I tried to implement a simple tcp client in Rust storing data in a ring buffer, thinking it would be a good way to work with tasks and port/channels give unpredictable inputs. But I'm snagged on the TcpStream stuff.

Here is my ring buffer implementation. It feels clunky, but I figured it would be a working proof-of-concept:

static BUFFER_SIZE: uint = 10;
static BUFFER_CELL_SIZE: uint = 128;

pub struct SimpleRingBuffer {
    //see below in SimpleRingBuffer#new for waffling on design.
    cells: ~[ Option<~[u8]> ], //see, it's still low-level because raw bytes.
    max_cell_size: uint,
    write_cursor: uint,
    read_cursor: uint,
    length: uint,
}

impl SimpleRingBuffer {

    //look out for fencepost errors.
    pub fn new(buffer_length: uint, max_cell_length: uint) -> SimpleRingBuffer {
        //should this pre-allocate the arrays to avoid resizing penalties? This would mean culling
        //Nones and dealing with sizing elsewhere I'd think. Also ahving to think about it.
        let mut none_array: ~[ Option<~[u8]> ] = ~[]; 
        for each_u8 in range(0, buffer_length) { //FIXME use proper ittorator.
            //none_array.push(std::vec::with_capacity<u8>(max_cell_length));
            none_array.push(None);
        };

        SimpleRingBuffer { 
            cells: none_array,
            max_cell_size: max_cell_length,
            write_cursor: 0, //not really cursors.
            read_cursor: 0,  //not really cursor.
            length: buffer_length
        }
    }

    //FIXME use <T> and a sanitize method.
    pub fn write_data(&mut self, incoming_data: &[u8]) {
        if self.write_cursor == self.length {
            self.write_cursor = 0;
        }

        self.cells[self.write_cursor] = Some(incoming_data.to_owned());
        self.write_cursor += 1;
    }

    pub fn read_cell(&mut self) -> ~[u8] {
        let mut read_array: ~[u8] = ~[];
        match self.cells[self.read_cursor] {
            Some(ref n) => read_array = n.clone(),
            None    => ()
        }

        self.read_cursor += 1;
        read_array
    }

    pub fn release_entire_buffer(&mut self) -> ~[ ~[u8] ] {
        self.read_cursor = 0;

        let mut buffer_array: ~[ ~[u8] ] = ~[];
        for each_cell in range(self.read_cursor, self.length) {
            buffer_array.push(self.read_cell());
        }

        self.read_cursor = 0;
        self.write_cursor = 0;

        buffer_array
    }

}

And here's my client:

pub mod simpletcpclient {

use std::io::net::ip::SocketAddr;
use std::io::net::tcp::TcpStream;
use std::str;

use simpletcpclient::simpleringbuffer::SimpleRingBuffer;


static BUFFER_SIZE: uint = 16;
static BUFFER_CELL_SIZE: uint = 1024;

pub struct SimpleClient {
    read_buffer: SimpleRingBuffer,
    write_buffer: SimpleRingBuffer, //unimplemented
    socket: SocketAddr
}

impl SimpleClient {

    pub fn new(host_addr: &str) -> SimpleClient {
        let mut write_buff = SimpleRingBuffer::new(BUFFER_SIZE, BUFFER_CELL_SIZE);
        let mut read_buff = SimpleRingBuffer::new(BUFFER_SIZE, BUFFER_CELL_SIZE);

        SimpleClient { 
            read_buffer: read_buff,
            write_buffer: write_buff,
            socket: from_str::<SocketAddr>(host_addr).unwrap()
        }
    }

    //see https://mail.mozilla.org/pipermail/rust-dev/2013-August/005111.html
    pub fn launch_client(&mut self) {
        let mut in_stream = TcpStream::connect(self.socket);

        let an_req: ~str = ~"GET / HTTP/1.1\r\n\r\n";
        //let an_req: ~str = ~"GET more-than-a-thousand.html HTTP/1.1\r\n\r\n";
        self.read_incoming(an_req);
        self.print_read_info();
    }

    fn read_incoming(&mut self, tcp_req: ~str) {
        let mut in_stream = TcpStream::connect(self.socket);
        let mut temp_cell = ~[0, .. BUFFER_CELL_SIZE];

        in_stream.write(tcp_req.as_bytes());
        //TcpStream indecision here!
        //in_stream.read(temp_cell);
        //let temp_cell = in_stream.read_to_str();
        in_stream.push_bytes(&mut temp_cell, BUFFER_CELL_SIZE);

        self.read_buffer.write_data(temp_cell);
    }

    fn print_read_info(&mut self) {
        println!("Releasing ring buffer contents");
        let released_byte_array = self.read_buffer.release_entire_buffer();
        for each_array in released_byte_array.iter() {
            if each_array.len() > 0 {
                println!("{}", str::from_utf8(each_array.clone()));
            };
        };
    }
}

This returns what I expect, if I need less than 1024 bytes.

My thinking was that because I don't know what any data I request looks like necessarily, I wanted to request a certain number of bytes, store that in a buffer as bytes, repeat until EOF and then read that as convenient. But the TcpStream methods appear to have different intended uses but it's not entirely clear to me which suits the purpose of reading in some data and storing it somewhere for later.

What am I doing wrong? Particularly, say I want to pull down a larger file over TCP, rather than all in one push_bytes(). Likewise, my SimpleRingBuffer feels rather unbufferish with nested vectors, which make me worry about penalties at reallcation/growth time.

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top