I'm trying to develop a simple RustRust chat server. I'm no RustRust expert and come from Java/KotlinJava and Kotlin. This same server in KotlinKotlin is:
import java.io.*
import java.net.*
import kotlin.concurrent.thread
import java.util.concurrent.ConcurrentHashMap
fun main(args: Array<String>) {
val serv = ServerSocket(Integer.parseInt(args[0]))
//val users = ConcurrentHashMap<String, PrintWriter>()
val users = Collections.synchronizedMap(HashMap<String, PrintWriter>())
while (true) {
val s = serv.accept()
thread {
var sin = s.getInputStream().bufferedReader()
val sout = PrintWriter(s.getOutputStream(), true)
// read nick
val nick = sin.readLine()
users.put(nick, sout)
sin.forEachLine {
for (peer in users.values) {
if (peer == sout) continue
peer.println(it)
}
}
users.remove(nick)
}
}
}
import java.io.*
import java.net.*
import kotlin.concurrent.thread
import java.util.concurrent.ConcurrentHashMap
fun main(args: Array<String>) {
val serv = ServerSocket(Integer.parseInt(args[0]))
//val users = ConcurrentHashMap<String, PrintWriter>()
val users = Collections.synchronizedMap(HashMap<String, PrintWriter>())
while (true) {
val s = serv.accept()
thread {
var sin = s.getInputStream().bufferedReader()
val sout = PrintWriter(s.getOutputStream(), true)
// read nick
val nick = sin.readLine()
users.put(nick, sout)
sin.forEachLine {
for (peer in users.values) {
if (peer == sout) continue
peer.println(it)
}
}
users.remove(nick)
}
}
}
After many attempts I've come up with a RustRust implementation that at least works:
use std::env;
use std::io;
use std::io::Write;
use std::io::{LineWriter, BufReader, BufRead};
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::thread;
use std::ops::DerefMut;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
let server_socket = TcpListener::bind(&*format!("localhost:{}", args[1]))?;
let users: Arc<Mutex<HashMap<String, LineWriter<TcpStream>>>> = Arc::new(Mutex::new(HashMap::new()));
for socket in server_socket.incoming() {
let users = users.clone();
thread::spawn(move || -> io::Result<()> {
let socket = socket?;
let socket_copy = socket.try_clone()?;
let mut inp = BufReader::new(socket);
let out = LineWriter::new(socket_copy);
// read nick
let mut nick = String::new();
inp.read_line(&mut nick)?;
nick.pop(); // discard '\n'
let nick_copy = nick.clone();
{
let mut users = users.lock().unwrap();
users.insert(nick, out);
}
for line in inp.lines() {
{
let line = line?;
let mut users = users.lock().unwrap();
for (nick, peer) in users.deref_mut() {
if *nick == nick_copy { continue; }
writeln!(peer, "{}", line)?;
}
}
}
// remove nick
{
let mut users = users.lock().unwrap();
users.remove(&nick_copy);
}
Ok(())
});
}
Ok(())
}
use std::env;
use std::io;
use std::io::Write;
use std::io::{LineWriter, BufReader, BufRead};
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::thread;
use std::ops::DerefMut;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
let server_socket = TcpListener::bind(&*format!("localhost:{}", args[1]))?;
let users: Arc<Mutex<HashMap<String, LineWriter<TcpStream>>>> = Arc::new(Mutex::new(HashMap::new()));
for socket in server_socket.incoming() {
let users = users.clone();
thread::spawn(move || -> io::Result<()> {
let socket = socket?;
let socket_copy = socket.try_clone()?;
let mut inp = BufReader::new(socket);
let out = LineWriter::new(socket_copy);
// read nick
let mut nick = String::new();
inp.read_line(&mut nick)?;
nick.pop(); // discard '\n'
let nick_copy = nick.clone();
{
let mut users = users.lock().unwrap();
users.insert(nick, out);
}
for line in inp.lines() {
{
let line = line?;
let mut users = users.lock().unwrap();
for (nick, peer) in users.deref_mut() {
if *nick == nick_copy { continue; }
writeln!(peer, "{}", line)?;
}
}
}
// remove nick
{
let mut users = users.lock().unwrap();
users.remove(&nick_copy);
}
Ok(())
});
}
Ok(())
}
I'd prefer to use for peer in users.values_mut() instead of for (nick, peer) in users.deref_mut() but then I've problems when comparing LineWriter references to discard sender.
I'd also prefer to use RwLock instead of Mutex.
Any pointers to simplify/enhance code will be greatly apreciatedappreciated.