I'm trying to get started with network and parallel programming in C#. I'm almost a complete novice in programming, so I'm looking for feedback on a small script I'm writing.
The goal of the programming is to:
- Accept incoming connections and store them as users which are sent into
Room
s. Users are expected to provide occasional input, maybe once every second. - After a certain time, the input from all users in a room is collected and then sent back to all users in that room.
In total there are 3 classes:
- Server
- Room
- User
The below code is far from complete and, and I haven't decided on startup and close down mechanisms yet. As of now, I just want the connections and asynchronous to work.
class Server {
private bool ServerIsRunning;
private const int port = 12321;
private TcpListener _listener;
private List<Room> _rooms;
private const int speed = 100;
public Server()
{
_rooms = new List<Room>();
startServer();
}
// Start the server
private void startServer()
{
try {
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
ServerIsRunning = true;
listenForClients();
}
catch (SocketException e) {
Console.Write(e.Message);
}
}
// Listen for clients while server is on, if found, send them to a room
async private void listenForClients()
{
while (ServerIsRunning) {
TcpClient client = await _listener.AcceptTcpClientAsync();
new User(client, getARoom());
}
}
// Get a room thats not full(less then 10 users)
private Room getARoom()
{
int index = _rooms.Count - 1;
if ((index < 0) || (_rooms[index].NumberOfUsers > 9)){
_rooms.Add(new Room(speed));
index++;
}
return _rooms[index];
}
}
class Room
{
private Task _sending;
private static Timer timer;
private List<User> _users;
private List<Task> _readers;
public Task Sending { get { return _sending; } }
public int NumberOfUsers { get { return _users.Count; } }
public Room(int time)
{
this._users = new List<User>();
this._readers = new List<Task>();
this._sending = new Task();
setupTimer(time);
}
public void start()
{
timer.Start();
}
public void stop()
{
timer.Stop();
}
// set up timer
private void setupTimer(int time)
{
timer = new Timer(time);
timer.Elapsed += new ElapsedEventHandler(sendNewOutput);
}
// Add user to room and start if full
public void registerUser(User newUser)
{
_users.Add(newUser);
_readers.Add(newUser.Reading);
if (_users.Count > 0) { start(); }
}
// remove user from room
public void deRegisterUser(User user)
{
_users.Remove(user);
_readers.Remove(user.Reading);
}
// waits for all users to finish recieving and then send the total input back to all
async private void sendNewOutput(object s, ElapsedEventArgs a)
{
await Task.WhenAll(_readers); // Waits for all clients to read
try {
_sending = Task.Run(() => {
sendOutput(interpretInput());
});
}
catch (Exception e) { Console.WriteLine(e.Message); }
}
// gathers all input
private List<byte[]> interpretInput()
{
List<byte[]> input = new List<byte[]>();
_users.ForEach((user) => { input.AddRange(user.Input); })
return input;
}
// Sends out output
private void sendOutput(List<byte[]> output)
{
_users.ForEach((user) => { user.send(output); });
}
}
class User {
private TcpClient _connection;
private NetworkStream _stream;
private Room _room;
private Task _reading;
private List<byte[]> _input;
public Task Reading { get { return _reading; } } // So that others can see if the task is complete
public List<byte[]> Input { get { return _input; } } // making the input available
public User(TcpClient connection, Room room)
{
this._connection = connection;
this._stream = connection.GetStream();
this._room = room;
this._reading = new Task();
this._room.registerUser(this);
listen();
}
// Listen for data
private void listen()
{
Task.Run(async () => {
while (_connection.Connected) {
if (_stream.CanRead && _stream.DataAvailable) {
_reading = read();
await _reading; // finish reading before starting again
}
Thread.Sleep(50); // i dont want to read all the time
}
cleanUp();
});
}
// wait for the room to finish sending out data and then read the new thats waiting
async private Task read()
{
await _room.Sending; // Waits for the server to finish sending out info to all clients before reading in new
int length;
byte[] input = new byte[1024];
while ((length = await _stream.ReadAsync(input, 0, 1024)) != 0) {
byte[] data = new byte[length];
Array.Copy(input, data, length);
_input.Add(data);
}
}
// sends output to client
public void send(List<byte[]> output)
{
output.ForEach((message) => {
_stream.Write(message, 0, message.Length);
});
}
// dergisters the user from the room and closes stream and connection
private void cleanUp()
{
_room.deRegisterUser(this);
try {
_stream.Close();
_connection.Close();
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
}