Nepermjet shembujve te meposhtem do te ilustrojme komunikimin e proceseve nepermjet sockets.
Do te ndertojme nje program i cili do te degjoje ne nje socket TCP, kombinim IP:Port.
Ne shembullin tone ne adresen locale (loopback address) 127.0.0.1 ne porten 8080.
Shembujt jane ndertuar ne Visual Studio si nje Console Application Project, duke perdorur dotNet 8, ne gjuhen C#.
Me poshte po sjellim kodin e skedarit Program.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class Program
{
static async Task Main(string[] args)
{
int port = 8080;
string ipAddress = "127.0.0.1"; // Localhost
// Create a TcpListener to listen on a specific IP address and port
TcpListener listener = new TcpListener(IPAddress.Parse(ipAddress), port);
listener.Start();
Console.WriteLine($"Listening on {ipAddress}:{port}...");
// Accepting incoming client connections
while (true)
{
try
{
// Wait for a client connection
TcpClient client = await listener.AcceptTcpClientAsync();
IPEndPoint remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
if (remoteEndPoint != null)
{
Console.WriteLine($"Client connected from {remoteEndPoint.Address}:{remoteEndPoint.Port}");
}
else
{
Console.WriteLine("Client connected, but could not determine IP address.");
}
// Get the client's network stream
NetworkStream stream = client.GetStream();
// Start a task to handle communication with the client
await HandleClientCommunication(stream);
// Close the connection after communication
client.Close();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
static async Task HandleClientCommunication(NetworkStream stream)
{
byte[] buffer = new byte[1024];
int bytesRead;
var receivedData = new StringBuilder();
try
{
// Send an initial greeting message to the client
string greeting = "Welcome to the server! Type a message and press Enter. Type 'exit' to disconnect.\n";
byte[] greetingBytes = Encoding.ASCII.GetBytes(greeting);
await stream.WriteAsync(greetingBytes, 0, greetingBytes.Length);
Console.WriteLine("Sent greeting to client.");
while (true)
{
// Read from stream byte-by-byte until a newline is detected
while (true)
{
bytesRead = await stream.ReadAsync(buffer, 0, 1); // read one byte at a time
if (bytesRead == 0)
{
Console.WriteLine("Client disconnected.");
return;
}
char ch = (char)buffer[0];
if (ch == '\n')
break; // End of line
if (ch != '\r') // Ignore carriage return
receivedData.Append(ch);
}
string clientMessage = receivedData.ToString().Trim();
receivedData.Clear(); // Clear for next message
Console.WriteLine($"Received from client: {clientMessage}");
if (clientMessage.ToLower() == "exit")
{
Console.WriteLine("Client requested to exit.");
break;
}
string response = $"Server received: {clientMessage}\n";
byte[] responseBytes = Encoding.ASCII.GetBytes(response);
await stream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error during communication: {ex.Message}");
}
}
}
Ky program lejon qe nje perdorues te lidhet me te.
Cfare mund te bejme qe te lejoje disa perdorues ne te njejten kohe?
Threads.......
Kodi me poshte perdore Threads per te mundesuar disa kliente qe te komunukojne me serverin. Vini re portat e ndryshme qe sistemi operativ i cakton cdo procesi te ri qe lidhet me serverin.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
int port = 8080;
string ipAddress = "127.0.0.1";
TcpListener listener = new TcpListener(IPAddress.Parse(ipAddress), port);
listener.Start();
Console.WriteLine($"Server listening on {ipAddress}:{port}...");
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
// Get client info
IPEndPoint remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
Console.WriteLine($"Client connected from {remoteEndPoint?.Address}:{remoteEndPoint?.Port}");
// Handle each client in a separate task
_ = Task.Run(async () =>
{
await HandleClientCommunication(client);
});
}
}
static async Task HandleClientCommunication(TcpClient client)
{
using NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
var receivedData = new StringBuilder();
IPEndPoint remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
try
{
string greeting = "Welcome to the server! Type a message and press Enter. Type 'exit' to disconnect.\n";
byte[] greetingBytes = Encoding.ASCII.GetBytes(greeting);
await stream.WriteAsync(greetingBytes, 0, greetingBytes.Length);
while (true)
{
// Read one byte at a time until newline
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, 1);
if (bytesRead == 0)
{
Console.WriteLine("Client disconnected.");
return;
}
char ch = (char)buffer[0];
if (ch == '\n') break;
if (ch != '\r') receivedData.Append(ch);
}
string clientMessage = receivedData.ToString().Trim();
receivedData.Clear();
Console.WriteLine($"Message from {remoteEndPoint?.Address}:{remoteEndPoint?.Port}");
Console.WriteLine($"[Message] {clientMessage}");
if (clientMessage.ToLower() == "exit")
{
Console.WriteLine("Client requested to exit.");
break;
}
string response = $"Server received: {clientMessage}\n";
byte[] responseBytes = Encoding.ASCII.GetBytes(response);
await stream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
client.Close();
}
}
}
Vini re portat e ndryshme qe ka cdo klient qe lidhet. Serveri pret ne porten 8080 ndersa me pas per cdo klient qe lidhet sistemi operativ i cakton atij nje porte 52164, 52170, 52179, 52185...
Serveri TCP që trajton shumë klientë në mënyrë asinkrone dhe siguron numërimin e saktë të sesioneve duke përdorur sinkronizim të fijeve (thread synchronization).
Ky program në C# krijon një server TCP që:
Pret lidhje nga klientë në mënyrë asinkrone
Për secilin klient, krijon një detyrë të veçantë (Task) për ta trajtuar.
Numëron sesionet e lidhura në mënyrë të sigurt për shumë fije.
Zbulon kur klienti shkëputet.
🔹 static int sessions = 0;
Ky variabël globale ruan numrin e klientëve të lidhur. Përdoret nga shumë klient, prandaj duhet mbrojtur nga perdorimi i njekohshem.
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
listener.Start();
Serveri fillon të dëgjojë në adresën 127.0.0.1 dhe portin 8080.
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
listener.AcceptTcpClientAsync() pret që një klient të lidhet
Kur lidhet, krijohet një TcpClient.
Interlocked.Increment(ref sessions);
Përdor Interlocked për të inkrementuar në mënyrë atomike (e sigurt për shumë fije).
Kjo parandalon gabime në numërimin e sesioneve.
Interlocked është një klasë në .NET që ofron operacione atomike mbi variabla të përbashkëta të tipit int, long, etj. Qëllimi: të shmangë kushtet e konkurences pa përdorur lock, që është më i ngadaltë.
Operacionet që ofron Interlocked
Metodë Përshkrimi
Increment(ref int) Rrit vlerën me 1
Decrement(ref int) Ul vlerën me 1
Add(ref int, value) Shton një vlerë specifike
Exchange(ref int, newValue) Vendos një vlerë të re dhe kthen të vjetrën
CompareExchange(...) Krahason dhe zëvendëson në mënyrë të sigurt është më i ngadaltë.
_ = Task.Run(async () =>
{
try
{
await HandleClientCommunication(client);
}
finally
{
Interlocked.Decrement(ref sessions);
Console.WriteLine($"Client u shkëput. Sesione aktive: {sessions}");
}
});
Cdo klient trajtohet ne nje thread te vecante, qe ekzekutohet ne menyre te pavarur
Ne finally, clienti eshte shkeputur ose ka ndodhur nje gabim. Ne kete moment zvogelohet numri i sesioneve te lidhur na server.
Ky funksion trajton komunikimin me klientin. Dergon nje mesazh mireseardhje duke perdorur klasen stream.
string greeting = "Welcome to the server! ...";
await stream.WriteAsync(greetingBytes, 0, greetingBytes.Length);
int bytesRead = await stream.ReadAsync(buffer, 0, 1);
if (bytesRead == 0)
{
Console.WriteLine("Klienti u shkëput papritur.");
return;
}
Lexon nga rrjeti.
Nëse bytesRead == 0, kjo do të thotë që klienti është shkëputur.
if (clientMessage.ToLower() == "exit")
{
Console.WriteLine("Klienti kërkoi të largohej.");
break;
}
Nese klienti dergon tekstin exit atehere serveri mbyll sesionin.
catch (Exception ex)
{
Console.WriteLine($"Gabim: {ex.Message}");
}
finally
{
client.Close();
}
Kap gabime gjatë leximit/shkrimit dhe gjithmonë mbyll klientin për të liruar burimet.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static int sessions = 0;
static async Task Main(string[] args)
{
int port = 8080;
string ipAddress = "127.0.0.1";
TcpListener listener = new TcpListener(IPAddress.Parse(ipAddress), port);
listener.Start();
Console.WriteLine($"Server listening on {ipAddress}:{port}...");
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
IPEndPoint remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
Console.WriteLine($"Client connected from {remoteEndPoint?.Address}:{remoteEndPoint?.Port}");
Interlocked.Increment(ref sessions);
Console.WriteLine($"Number of sessions connected: {sessions}");
_ = Task.Run(async () =>
{
try
{
await HandleClientCommunication(client);
}
finally
{
Interlocked.Decrement(ref sessions);
Console.WriteLine($"Client disconnected. Active sessions: {sessions}");
}
});
}
}
static async Task HandleClientCommunication(TcpClient client)
{
using NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
var receivedData = new StringBuilder();
IPEndPoint remoteEndPoint = client.Client.RemoteEndPoint as IPEndPoint;
try
{
string greeting = "Welcome to the server! Type a message and press Enter. Type 'exit' to disconnect.\n";
byte[] greetingBytes = Encoding.ASCII.GetBytes(greeting);
await stream.WriteAsync(greetingBytes, 0, greetingBytes.Length);
while (true)
{
// Read one byte at a time until newline
while (true)
{
int bytesRead = await stream.ReadAsync(buffer, 0, 1);
if (bytesRead == 0)
{
Console.WriteLine("Client disconnected unexpectedly.");
return;
}
char ch = (char)buffer[0];
if (ch == '\n') break;
if (ch != '\r') receivedData.Append(ch);
}
string clientMessage = receivedData.ToString().Trim();
receivedData.Clear();
Console.WriteLine($"Message from {remoteEndPoint?.Address}:{remoteEndPoint?.Port}");
Console.WriteLine($"[Message] {clientMessage}");
if (clientMessage.ToLower() == "exit")
{
Console.WriteLine("Client requested to exit.");
break;
}
string response = $"Server received: {clientMessage}\n";
byte[] responseBytes = Encoding.ASCII.GetBytes(response);
await stream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
client.Close();
}
}
}