LINUX.ORG.RU

Дублирование информации в TcpStream

 , ,


0

5

Я новичок в сетевом программировании, пытаюсь разобраться с TcpStream в rust. Я пытаюсь связать клиент и сервер. Для этого создаю один канал TcpStream и использую его и на сервере и на клиенте на чтение и запись. Пишу на клиенте через метод write сообщение 'A', вызываю flush, на сервер приходит 'A', отсылаю ответ клиенту. После этого, с клиента в тот же TcpStream пишу сообщение 'B', вызываю fush, и на сервер приходит не 'B', а 'AB'. ЧЯДНТ? Как убрать дублирование прочитанной инфы? Надо как-то руками очищать TcpStream? Открывать 2 TcpStream(в один писать, из другого читать)? Или надо переконнекчиваться после каждого запрос/ответ?

★★★★★

Код показать не хочешь?

Открывать 2 TcpStream(в один писать, из другого читать)?

Про TcpStream ничего не знаю, но с «каналами» (channel) в расте так и работают - создаётся два: один для отправки, другой для получения. Хотя посмотрел примеры для TcpStream - не похоже на это.

DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)

Я пытаюсь связать клиент и сервер. Для этого создаю один канал TcpStream и использую его и на сервере и на клиенте на чтение и запись.

У тебя клиент и сервер в одном процессе?

Открывать 2 TcpStream(в один писать, из другого читать)? Или надо переконнекчиваться после каждого запрос/ответ?

Я буду очень удивлен, если так нужно делать.

В общем, покажи код.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Процессы разные
сервер:

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::io::{Write, Read, Bytes};
use std::iter::Iterator;
use std::string::String;
//use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use std::str::FromStr;

#[allow(dead_code)]
struct Player {
	name     : String,
	out_chanel: TcpStream,
	stream   : Bytes<TcpStream>
}

fn get_str(chanel : &mut Bytes<TcpStream>, stop : char) -> Option<String> {
	let mut res = vec![];
	let make_res = |res| {
		match String::from_utf8(res) {
			Ok(s) => return Some(s),
			_     => return None
		}
	};
	loop {
		let c = chanel.next();
		match c {
			None         => return make_res(res),
			Some(Ok(c))  =>
				if c as char == stop
					{return make_res(res)}
				else
					{res.push(c)},
			Some(Err(_)) => return None
		}
	}
}

#[allow(unused_must_use)]
fn get_player(listener : &TcpListener) -> Player {
	loop {
		match listener.accept() {
			Ok((in_chanel,_)) => {
				let mut out_chanel = match in_chanel.try_clone() {
					Ok(a) => a,
					_     => panic!()
				};
				let mut stream = in_chanel.bytes();
				out_chanel.write(b"hi client!.");
				out_chanel.flush();
				match get_str(&mut stream, '.') {
					Some(ref s) if s == "hi server!" => {
						out_chanel.write(b"say your name.");
						out_chanel.flush();
						let name = get_str(&mut stream, '.').unwrap();
						return Player{
							name     : name,
							out_chanel: out_chanel,
							stream   : stream
						}
					},
					Some(s) => {
						println!("has: '{}'", s);
						drop(out_chanel)
					},
					None => drop(out_chanel)
				}
			},
			Err(e) => panic!(e)
		};
	}
}

struct State {
	fld      : Vec<Vec<char>>,
	step_now : char,
	changed  : bool
}

fn get_win(fld : &Vec<Vec<char>>) -> Option<char> {
	let wins = [
				[[0,0],[0,1],[0,2]],
				[[1,0],[1,1],[1,2]],
				[[2,0],[2,1],[2,2]],
				[[0,0],[1,0],[2,0]],
				[[0,1],[1,1],[2,1]],
				[[0,2],[1,2],[2,2]],
				[[0,0],[1,1],[2,2]],
				[[0,2],[1,1],[2,0]]
			];
	for line in wins.iter() {
		let a = fld[line[0][0]][line[0][1]];
		let b = fld[line[1][0]][line[1][1]];
		let c = fld[line[2][0]][line[2][1]];
		if (a == b) && (b == c) && a != ' ' {
			return Some(a)
		}
	}
	return None;
}

fn print_fld(fld : &Vec<Vec<char>>) -> String {
	let divider = "-------";
	let mut result = format!("{}",divider);
	for line in fld {
		let mut res = format!("|");
		for c in line {
			res = format!("{}{}|",res,c);
		}
		result = format!("{}\n{}\n",result,res);
	}
	result
}

#[allow(unused_must_use)]
fn apply(mut player : Player, state : Arc<Mutex<State>>, me : char, enemy : char) {
	loop {
		match get_str(&mut player.stream, '.') {
			Some(cmd) => {
				println!("input from {}: '{}'", player.name, cmd);
				if cmd.len() == 0
					{continue};
				let mut cmd = cmd.split(' ');
				match cmd.nth(0) {
					Some("show") => {
						let st = state.lock().unwrap();
						player.out_chanel.write_fmt(format_args!("{}.",print_fld(&st.fld)));
						player.out_chanel.flush();
						()
					},
					Some("put") => {
						let i = usize::from_str(cmd.nth(0).unwrap()).unwrap();
						let j = usize::from_str(cmd.nth(0).unwrap()).unwrap();
						let mut st = state.lock().unwrap();
						if st.step_now == me {
							if i < 2 && j < 2{
//								let mut fld = &mut st.fld;
								if st.fld[i][j] == ' ' {
									st.fld[i][j] = me;
									st.step_now = enemy;
									st.changed = true;
									player.out_chanel.write(b"OK.");
								}
								else {
									player.out_chanel.write(b"ERR.");
								}
							}
							else{
								player.out_chanel.write(b"ERR.");
							}
						}
						else {
							player.out_chanel.write(b"NOT YOU.");
						};
						()
					},
					_ => {
						player.out_chanel.write(b"ERR.");
						()
					}
				};
				player.out_chanel.flush();
				()
			},
			None => ()
		}
	}
}

fn main(){
	let listener =
		match TcpListener::bind("127.0.0.1:8080") {
			Ok(a)  => a,
			Err(e) => panic!(e)
		};
	println!("wait for players");
	let player1 = get_player(&listener);
	println!("found {}", player1.name);
//	let mut player2 = get_player(&listener);
//	println!("found {}\nstart", player2.name);
	let state = State{
		fld      : vec![vec![' ',' ',' '],vec![' ',' ',' '],vec![' ',' ',' ']],
		step_now : 'x',
		changed  : true
	};
	let state = Arc::new(Mutex::new(state));
	let name1 = player1.name.clone();
//	let name2 = player2.name.clone();
	let state1 = state.clone();
//	let state2 = state.clone();
	thread::spawn(move||{apply(player1, state1, 'x', 'o')});
//	thread::spawn(move||{apply(player2, state2, 'o', 'x')});
	loop {
		match state.lock() {
			Ok(mut st) =>
				if st.changed {
					st.changed = false;
					println!("{}",print_fld(&st.fld));
					match get_win(&st.fld) {
						None => (),
						Some(a) => {
							let winner = if a == 'x' {name1} else {String::new()};//{name2};
							println!("winnner found: '{}', {}", a, winner);
							break;
						}
					}
				},
			Err(_) => {
				println!("lock error");
				break;
			}
		}
	}
}
клиент:
use std::net::{TcpStream};
use std::io;
use std::io::{Read, Write, Bytes};

fn get_str(chanel : &mut Bytes<TcpStream>, stop : char) -> Option<String> {
	let mut res = vec![];
	let make_res = |res| {
		match String::from_utf8(res) {
			Ok(s) => return Some(s),
			_     => return None
		}
	};
	loop {
		let c = chanel.next();
		match c {
			None         => return make_res(res),
			Some(Ok(c))  =>
				if c as char == stop
					{return make_res(res)}
				else
					{res.push(c)},
			Some(Err(_)) => return None
		}
	}
}

#[allow(unused_must_use)]
fn main(){
	let mut line = String::new();
	let mut stdin = io::stdin();
	stdin.read_line(&mut line);
	let name = line.clone();
	let res = TcpStream::connect("127.0.0.1:8080");
	if res.is_err() {
		println!("connect fail");
		return ()
	};
	let chanel = res.unwrap();
	let mut output = match chanel.try_clone() {
		Ok(a) => a,
		_ => panic!()
	};
	let mut input = chanel.bytes();
	match get_str(&mut input, '.') {
		Some(ref s) if s == "hi client!" => (),
		_ => {
			println!("nope!");
			return()
		}
	};
	output.write(b"hi server!.");
	output.flush();
	match get_str(&mut input, '.') {
		Some(ref s) if s == "say your name" => (),
		_ => return()
	};
	output.write_fmt(format_args!("{}.",name));
	output.flush();
	println!("OK");
	loop {
		println!("enter yout cmd:");
		stdin.read_line(&mut line);
		output.write_fmt(format_args!("{}.", line));
		output.flush();
		match get_str(&mut input, '.') {
			None => println!("retry"),
			Some(s) => println!("> '{}'", s)
		}
	}
}

Aswed ★★★★★
() автор топика
Ответ на: комментарий от quantum-troll

Написал упрощенный вариант, а там все работает. Пойду искать проблему. Спасибо за наводящий вопрос.

Aswed ★★★★★
() автор топика
Ответ на: комментарий от Aswed

Всегда пожалуйста. Вообще, локализовывать проблему — хорошая практика.

quantum-troll ★★★★★
()
Ответ на: комментарий от Aswed

Ребята старались, делали немутабельные переменные по-умолчанию, а народ как писал все с мутабельными, так и пишет.

maloi ★★★★★
()

О кстати... а еще «channel» пишется с двумя «n» %)

tailgunner ★★★★★
()
Ответ на: комментарий от maloi

Кстати да, автору предлагаю для общего развития попробовать по максимуму избавиться от mut-ов, выполняя для этого все самые низменные желания компилятора. А вторым шагом - от unwrap-ов.

ovk48 ★★★
()
Ответ на: комментарий от Legioner

ХЗ, мне например не очень нравится, что они паникуют, когда встретят None.

ovk48 ★★★
()
Ответ на: комментарий от O02eg

Вроде panic это завершение не программы, а потока в общем случае. На сервере, наверное, при panic в потоке клиенту вернётся HTTP 500 или что-то аналогичное, а другие потоки продолжат выполнение. Что ещё может код сделать, если произошло что-то непредвиденное для него? panic часто это корректная реакция, имхо. Если вызывающий код может сделать что-то осмысленное, то надо вернуть ошибку. А если не может, то возвращать везде ошибки — будет тот же panic, прокинутый до верхнего уровня, только с кучей лишнего кода.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

500 не вернётся, по идее, должно в лучшем случае порваться соединение, в худшем случае, соединение просто останется незакрытым.

O02eg ★★★★★
()
Ответ на: комментарий от Legioner

На сервере, наверное, при panic в потоке клиенту вернётся HTTP 500 или что-то аналогичное

Через астрал, ага.

Что ещё может код сделать, если произошло что-то непредвиденное для него?

Сообщить побольше информации для расследования инцидента.

tailgunner ★★★★★
()
Ответ на: комментарий от Legioner

При панике тред падает, и используемые ресурсы не зачищаются. Т.е. никаких деструкторов не вызывается и память течет. По этому unwrap и все что может провоцировать панику вместо нормальной обработки - плохо. Тут я ничего серьезного не писал, по этому подзабил на такие вещи, а так да, нехорошо.

Для меня большая загадка, почему разрабы сделали так, что бы panic ронял тред а не всю программу. Он же все равно используется как assert в С. Т.е. вызывается если все совсем плохо и дальнейшего корректного выполнения кода не будет.

Aswed ★★★★★
() автор топика
Some(Ok(c))  =>
	if c as char == stop
		{return make_res(res)}
	else
		{res.push(c)},

Это рекомендуемый code style? ОМГ.

anonymous
()
Ответ на: комментарий от Aswed

При панике тред падает, и используемые ресурсы не зачищаются.

Чего? Деструкторы вообще-то вызываются при панике.

Для меня большая загадка, почему разрабы сделали так, что бы panic ронял тред а не всю программу.

Всё правильно сделали.

DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)
Ответ на: комментарий от anonymous

Это рекомендуемый code style? ОМГ.

А что именно не нравится? Ну и рекомендованный стиль - открывающая скобка на этой же строке, закрывающая на новой.

Хотя бы я этот кусок переписал бы иначе:

match c {
    None         => return make_res(res),
    Some(Ok(c))  =>
        if c as char == stop
            {return make_res(res)}
        else
            {res.push(c)},
        Some(Err(_)) => return None
    }
match channel.next() {
    Some(Err(_))                     => return None,
    Some(Ok(c)) if c as char != stop => res.push(c),
    _                                => return make_res(res),
}

DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.