Co si myslíte o této verzi? Dalo by se tam ještě něco vylepšit? Stává se mi, že když se klient odpojí, tak zůstave stále ve frontě :/ A něco by šlo možná elegantněji vyřešit..
Pokud by někdo měl čas, tak bych byl rád, jinak ta verze od Kyryho je celkem dobrá, ale jakmile jsem poslal zprávu na server, tak vlakna začla hned cyklit do konzole :/
Potřeboval bych vyřešit ten server, aby to bylo stabilní, rychlý a jednoduchý pro hodně klientů... pomalu se nejspíš blížim do konce :)
Kdyžtak se na to koukněte a řekněte, co by se mohlo opravit / změnit / vylepšit apod. kdyžtak ICQ 204-517-399, kdo by měl čas teda, bylo by to pro mě rychlejší :)
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server
{
//! Trida pro spusteni serveru
internal class TCP
{
// spusti naslouchani na portu
public static void StartOnPort(int port, int countThreads = 2)
{
// vytvorime frontu klientu
ClientPool clientPool = new ClientPool();
// stara se o klienty
ClientService clientService = new ClientService(clientPool, countThreads);
clientService.start(); // spustime
// naslouchna na vsech dostupnych IP na portu XYZ
TcpListener listener = new TcpListener(IPAddress.Any, port);
try
{
listener.Start(); // zkusime spustit naslouchani na portu
while (true)
{
// nasloucha prichozimu pripojeni
TcpClient client = listener.AcceptTcpClient();
if (client != null)
{
Console.WriteLine("The client was connected!");
// spracuje prichozi pripojeni (prida klienta do fronty)
clientPool.addClient(new ClientHandler(client));
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
//! Trida pro tvorbu fronty klientu
internal class ClientPool
{
// vytvoří synchronizovaný obal, kolem fronty klientu
private Queue syncQueue = Queue.Synchronized(new Queue());
// pridava klienty do fronty
public void addClient(ClientHandler client)
{
syncQueue.Enqueue(client);
}
// vrati a odebere klienta z fronty
public ClientHandler getAndremoveClient()
{
return (ClientHandler)(syncQueue.Dequeue());
}
// vrati velikost fronty (pocet klientu ve fronte)
public int totalClients
{
get { return syncQueue.Count; }
}
// vrati objekt, ktery lze pouzit jako synchronizovane pripojeni do fronty
public object getSyncRoot
{
get { return syncQueue.SyncRoot; }
}
}
//! Trida pro spravu klientu pomoci vlaken
internal class ClientService
{
private List<Thread> clientsThreads;
private ClientPool clientPoolService;
private int clientThreadSpeed;
private bool serviceRunning = false;
// vytvorime do fonty urcity pocet vlaken, ktere se budou starat o klienty
// zaroven nastavuje rychlost vlaken (vychozi je 10x za sekundu)
public ClientService(ClientPool clientPool, int countThreads, int threadSpeed = 100)
{
clientPoolService = clientPool;
clientsThreads = new List<Thread>(countThreads);
clientThreadSpeed = threadSpeed;
}
// spustime spravu klientu (vytvorime vlakna)
public void start()
{
serviceRunning = true;
// vytvori a spusti vsechna vlakna
for (int i = 0; i < clientsThreads.Capacity; i++)
{
clientsThreads.Add(new Thread(new ThreadStart(threadProcess)));
clientsThreads[i].Start();
}
}
// proces, ktery bude kazde vlakno vykonavat
private void threadProcess()
{
// pokud bude spustena sprava vlaken
while (serviceRunning)
{
ClientHandler client = null;
// ziska pres synchronizovane pripojeni ke klientu
lock (clientPoolService.getSyncRoot)
{
// pokud ve fronte budou nejaci klienti, tak vyjme posledniho
if (clientPoolService.totalClients > 0) client = clientPoolService.getAndremoveClient();
}
if (client != null)
{
client.clientProcess(); // vyvola klienta
// pokud klient bude stale pripojen, tak ho opet prida do fonty
if (client.isAlive) clientPoolService.addClient(client);
}
Thread.Sleep(clientThreadSpeed);
}
}
// zastavuje spustena vlakna
public void Stop()
{
serviceRunning = false;
for (int i = 0; i < clientsThreads.Capacity; i++)
{
// blokuje volani vlaken do doby, dokud se neukonci
if (clientsThreads[i] != null && clientsThreads[i].IsAlive) clientsThreads[i].Join();
}
// ukonci pripojeni se vsemi klienty
while (clientPoolService.totalClients > 0)
{
// ukoncuje klienty od posledniho pripojeneho
ClientHandler client = clientPoolService.getAndremoveClient();
client.close();
Console.WriteLine("Connecting with the client was terminated!");
}
}
}
//! Trida pro drzeni kazdeho klienty (informace o nem)
internal class ClientHandler
{
private TcpClient clientSocket;
private NetworkStream clientStream;
private bool clientState = false;
private byte[] bufferOfBytes;
private StringBuilder dataTemp = new StringBuilder();
private string clientData = null;
// pres konstruktor nastavuje kazdeho klienta
public ClientHandler(TcpClient tcpClient)
{
tcpClient.ReceiveTimeout = 100; // v milisekundach
clientSocket = tcpClient;
clientStream = tcpClient.GetStream();
bufferOfBytes = new byte[tcpClient.ReceiveBufferSize];
clientState = true;
}
// cte data od klienta a uklada do stringu
public void clientProcess()
{
try
{
// ziska velikost streamu
int BytesRead = clientStream.Read(bufferOfBytes, 0, (int)bufferOfBytes.Length);
if (BytesRead > 0)
{
// uklada obdrzena data od klienta
dataTemp.Append(Encoding.ASCII.GetString(bufferOfBytes, 0, BytesRead));
}
else dataReceived(); // prijme prazdny paket
}
catch (IOException)
{
// pokud nastane chyba v IO, tak opet prijme prazdny paket
dataReceived();
}
catch (SocketException)
{
// pokud nastane chyba v pripojeni klienta, tak ho odpoji
clientStream.Close();
clientSocket.Close();
clientState = false;
Console.WriteLine("The client has a problem with the connection and been disconnected!");
}
}
// prijima data od klienta (pouze pokud neprisel prazdny paket)
private void dataReceived()
{
if (dataTemp.Length > 0)
{
// pokud paket bude obsahovat zpravu "quit", tak ukonci pripojeni klienta
bool stop = (String.Compare(dataTemp.ToString(), "quit", true) == 0);
clientData = dataTemp.ToString(); // ulozi build do stringu
dataTemp.Length = 0; // vyprazdni temp
Console.WriteLine(clientData); // vypise zpravu klienta
// vytvori zpravu pro klienta
StringBuilder response = new StringBuilder();
response.Append("Received at ");
response.Append(DateTime.Now.ToString());
response.Append("\r\n");
response.Append(clientData);
sendDataBack(response.ToString()); // odesla zpravu klientovi
// pokud klient odesle paket se zpravou "quit", tak ukonci svoje pripojeni
if (stop)
{
clientStream.Close();
clientSocket.Close();
clientState = false;
}
}
}
// odesila data zpet ke klientu
private void sendDataBack(string message)
{
byte[] sendBytes = Encoding.ASCII.GetBytes(message.ToString());
clientStream.Write(sendBytes, 0, sendBytes.Length);
}
// uzavira klientske pripojeni
public void close()
{
clientStream.Close();
clientSocket.Close();
}
// zjistuje, zda je klient pripojen
public bool isAlive
{
get { return clientState; }
}
}
}