Below are my server codes.
public class Networking
{
private readonly ILogger logger;
private readonly TcpListener listener;
private readonly List connectedClients = new List();
private bool isListening;
public Networking(ILogger logger)
{
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
this.logger = loggerFactory.CreateLogger();
listener = new TcpListener(IPAddress.Any, 333);
}
public async Task StartListeningAsync()
{
if (isListening)
{
return;
}
listener.Start();
isListening = true;
logger.LogInformation("Server started listening on {address}:{port}.", IPAddress.Any, 8080);
while (isListening)
{
TcpClient client;
try
{
client = await listener.AcceptTcpClientAsync();
}
catch (ObjectDisposedException)
{
break;
}
// Check if the same client is already connected from another computer
foreach (TcpClient connectedClient in connectedClients)
{
if (connectedClient.Client.RemoteEndPoint.ToString() == client.Client.RemoteEndPoint.ToString())
{
// If the same client is already connected, close the new connection and notify the client
byte[] response = Encoding.ASCII.GetBytes("You are already connected from another computer.");
NetworkStream stream = client.GetStream();
await stream.WriteAsync(response, 0, response.Length);
client.Close();
logger.LogWarning("Client {remoteEndPoint} attempted to connect, but is already connected from another computer.", client.Client.RemoteEndPoint);
continue;
}
}
connectedClients.Add(client);
await HandleClientAsync(client);
logger.LogInformation("Client {remoteEndPoint} connected.", client.Client.RemoteEndPoint);
}
}
private async Task HandleClientAsync(TcpClient client)
{
try
{
using (NetworkStream stream = client.GetStream())
{
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
string[] credentials = data.Split(',');
if (IsUserValid(credentials[0], credentials[1]))
{
byte[] response = Encoding.ASCII.GetBytes("Success");
await stream.WriteAsync(response, 0, response.Length);
// Send a message to the client after successful connection
byte[] connectMsg = Encoding.ASCII.GetBytes("You have successfully connected to the server.");
await stream.WriteAsync(connectMsg, 0, connectMsg.Length);
}
else
{
byte[] response = Encoding.ASCII.GetBytes("Failure");
await stream.WriteAsync(response, 0, response.Length);
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error handling client.");
}
finally
{
client.Close();
connectedClients.Remove(client);
logger.LogInformation("Server is shutting down.");
}
}
private bool IsUserValid(string username, string password)
{
// Add database validation here
return true;
}
public async Task StopServerAsync()
{
isListening = false;
if (listener != null)
{
listener.Stop();
}
// Notify connected clients to disconnect
byte[] disconnectMsg = Encoding.ASCII.GetBytes("Server is shutting down. Please disconnect.");
foreach (TcpClient client in connectedClients)
{
try
{
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(disconnectMsg, 0, disconnectMsg.Length);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error sending disconnect message to client.");
}
}
// Wait for 1 minute for clients to disconnect
var timeout = TimeSpan.FromSeconds(60);
var tasks = new List();
foreach (TcpClient client in connectedClients)
{
tasks.Add(Task.Run(async () =>
{
try
{
if (client.Connected)
{
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(disconnectMsg, 0, disconnectMsg.Length);
client.Close();
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error sending disconnect message to client.");
}
}));
}
await Task.WhenAll(tasks);
}
public async Task> GetConnectedList()
{
List connectedList = new List();
foreach (TcpClient connectedClient in connectedClients)
{
string clientName = connectedClient.Client.RemoteEndPoint.ToString();
string clientIP = ((IPEndPoint)connectedClient.Client.RemoteEndPoint).Address.ToString();
connectedList.Add(clientName + "|" + clientIP);
}
return connectedList;
}
private string GetUserName(TcpClient client)
{
// TODO: Implement code to retrieve the user name from the client
// For example, you can send the user name to the server after a successful connection,
// and store it in the connectedClients list along with the TcpClient instance.
// Then you can retrieve it here using the TcpClient instance.
return "Unknown";
}
}
public partial class Server : Form
{
private readonly ILogger logger;
private readonly Networking networking;
public Server()
{
InitializeComponent();
logger = LoggerFactory.Create(builder =>
{
builder.AddConsole();
}).CreateLogger();
networking = new Networking(logger);
}
private async void btnStart_Click(object sender, EventArgs e)
{
try
{
btnStart.Enabled = false;
btnStop.Enabled = true;
await networking.StartListeningAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Error starting server.");
}
}
private async void btnStop_Click_1(object sender, EventArgs e)
{
try
{
btnStart.Enabled = true;
btnStop.Enabled = false;
await networking.StopServerAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Error stopping server.");
}
}
private async void Server_Load(object sender, EventArgs e)
{
try
{
btnStart.Enabled = false;
btnStop.Enabled = true;
await networking.StartListeningAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Error starting server.");
}
}
private async void Server_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
await networking.StopServerAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Error stopping server.");
}
}
private async void btnGetConecctedList_Click(object sender, EventArgs e)
{
List connectedList = await networking.GetConnectedList();
foreach (string connectedClient in connectedList)
{
// Split the connected client information by '|'
string[] info = connectedClient.Split('|');
// Do something with the connected client
string clientEndPoint = info[0] + " - " + info[1];
lstClients.Items.Add(clientEndPoint);
}
}
}
Below are my client codes.
namespace Client_Test
{
public partial class Login : Form
{
public Login()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
string username = txtUsername.Text;
string password = txtPassword.Text;
using (TcpClient client = new TcpClient())
{
try
{
await client.ConnectAsync("127.0.0.1", 333);
using (NetworkStream stream = client.GetStream())
{
byte[] buffer = Encoding.ASCII.GetBytes($"{username},{password}");
await stream.WriteAsync(buffer, 0, buffer.Length);
buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
if (response == "Success")
{
this.Hide();
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
this.Close();
}
else
{
MessageBox.Show("Invalid username or password.");
}
}
}
catch (SocketException ex)
{
if (ex.ErrorCode == 10061)
{
MessageBox.Show("Server is not running.");
}
else
{
MessageBox.Show($"Error connecting to server: {ex.Message}");
}
}
catch (Exception ex)
{
MessageBox.Show($"Error connecting to server: {ex.Message}");
}
}
}
}
}
The things I want to do are listed below in order.
1. Add User name and IP address in the list on the server side when each client is connected
2. If the server is down, when the client wants to connect, it will send a message that it is down
3. When I want to shut down the server while the clients are connected to the server, send a message to the clients "You need to exit the program, the server will be connected". If there is a client connected to the server, the server should watch for a change, then remove all the clients from the server and shut down the server.
4. While the server is removing the clients, a message is sent to the clients that they quit the program because the server was shut down.
5. Let me disconnect any user from the server if necessary
Tuhin PaulPosted Mar 15, 2023, 1:40 AM
Hello Vusal,
This code implements a TCP server in C# using the TcpListener class. The server listens for incoming connections on port 333 and handles client connections and disconnections.
The connected clients are stored in a List collection, and the server can retrieve a list of currently connected clients and their IP addresses by calling the GetConnectedList() method.
The server can be started by calling the StartListeningAsync() method, which starts listening for incoming connections on a separate thread. The server can be stopped by calling the StopServerAsync() method, which sends a disconnect message to all connected clients and waits for them to disconnect before stopping the listener.
The HandleClientAsync() method handles client connections and disconnections, as well as client requests. When a client connects, it sends its credentials (username and password), which are validated by the IsUserValid() method. If the credentials are valid, the server sends a success message to the client and a connect message to all other connected clients. If the credentials are invalid, the server sends a failure message to the client and disconnects it.
The server logs events using the ILogger interface, which is implemented using the Microsoft.Extensions.Logging framework. The logs are written to the console, but can be configured to write to other targets as well.
1. Add User name and IP address in the list on the server side when each client is connected
In the AcceptClient method, you can get the user name and IP address of the connected client using the TcpClient and IPAddress objects respectively. Then, you can add this information to a list of connected clients on the server side.
private static List<(TcpClient client, string userName, IPAddress ipAddress)> connectedClients = new List<(TcpClient client, string userName, IPAddress ipAddress)>();
2. If the server is down, when the client wants to connect, it will send a message that it is down
You can use a try-catch block to handle the case where the server is down. If an exception is thrown while trying to connect to the server, you can assume that the server is down and send a message to the client.
3. When I want to shut down the server while the clients are connected to the server, send a message to the clients "You need to exit the program, the server will be connected". If there is a client connected to the server, the server should watch for a change, then remove all the clients from the server and shut down the server.
You can create a method that sends a message to all connected clients and then closes the connection. Then, call this method before shutting down the server.
4. While the server is removing the clients, a message is sent to the clients that they quit the program because the server was shut down.
You can modify the ShutdownServer method to send a message to each client before closing the connection.
5. Let me disconnect any user from the server if necessary
You can add a method that removes a client from the list of connected clients and closes the connection.