Program klient - serwer. Zamykanie połączenia.

0

Witam. Mam do napisania program w C#. Klient ma nawiązać połączenie z serwerem, wysłać identyfikator i dane, serwer odsyła odpowiedź i serwer ma zainicjować rozłączenie - i tu jest problem. Połączenie działa, serwer odpowiada itd. tylko, że zawsze rozłączenie inicjuje klient. Jak ustawić aby to serwer inicjował zakończenie połączenia z klientem?

Klient:

class Client
    {
        private TcpClient tcpClient;
        private BinaryWriter Output;
        private BinaryReader Input;
        private int Client_ID;

        public Client()
        {
            openConnection("xxx", 9999);

            Console.Write("\nPodaj dlugość tekstu (n): ");
            String length1 = Console.ReadLine();
            int length = Int32.Parse(length1);
            Console.Write("\nPodaj tekst: ");
            String tekst = Console.ReadLine();

            Output = new BinaryWriter(tcpClient.GetStream());
            Input = new BinaryReader(tcpClient.GetStream());

            Client_ID = 1;
            Output.Write(Client_ID);
            bool server_ID_Answer = Input.ReadBoolean();
            while (server_ID_Answer != true)
            {
                Client_ID+=1;
                if (Client_ID > 10)
                {
                    closeConnection();
                    Console.WriteLine("Błąd połączenia. Serwer nie akceptuje połączenia.");
                    Console.Read();
                    Environment.Exit(0);
                }
                Output.Write(Client_ID);
                server_ID_Answer = Input.ReadBoolean();
            }

            Output.Write(length);
            Output.Write(tekst);
            bool serverAnswer = Input.ReadBoolean();
            Console.WriteLine("Odpowiedź serwera: " + serverAnswer);

            closeConnection();
            Console.Read();
        }

        private void openConnection(String address, int port)
        {
            try {
                tcpClient = new TcpClient();
                tcpClient.Connect(IPAddress.Parse(address), port);
                Console.WriteLine("Otwarto połączenie.");
            }catch(ArgumentNullException arg)
            {
                Console.WriteLine(arg);
            }catch(ArgumentOutOfRangeException arg)
            {
                Console.WriteLine(arg);
            }catch(ArgumentException arg)
            {
                Console.WriteLine(arg);
            }catch(SocketException arg)
            {
                Console.WriteLine(arg);
            }
        }
        private void closeConnection()
        {
            tcpClient.GetStream().Close();
            tcpClient.Close();
        }
    }

Serwer:

 class Server
    {
        private TcpListener listener; 
        private TcpClient client;
        private BinaryReader Input;
        private BinaryWriter Output;
        private int[] Client_ID = new int[10];

        public Server()
        {
            for (int i = 0; i < 10; i++) Client_ID[i] = 0;

            openConnection(9999);
            client = listener.AcceptTcpClient();

            Input = new BinaryReader(client.GetStream());
            Output = new BinaryWriter(client.GetStream());

            int ID = 0;
            ID = Input.ReadInt32();
            Console.WriteLine("Odebrano połączenie. ID = " + ID);
            bool status = check_Client_ID(ID);
            while (status != true)
            {
                Output.Write(false);
                ID = Input.ReadInt32();
                status = check_Client_ID(ID);
            }
            Output.Write(true);

            int n = Input.ReadInt32();
            Console.WriteLine("Odebrano n = " + n);
            String Text = Input.ReadString();
            Console.WriteLine("Odebrano tekst: " + Text);

            if (Text != null && n > 0)
            {
                if (Text.Length == n)
                {
                    Output.Write(true);
                }
                else Output.Write(false);
            }

            client.GetStream().Close();
            client.Close();
            listener.Stop();
            listener.Server.Close();
            Console.WriteLine("Zamknieto połączenie.");
            Console.Read();
        }

        private void openConnection(int port)
        {
            try {
                listener = new TcpListener(IPAddress.Any, port);
                listener.Start(); // Start nasłuchiwania
                Console.WriteLine("Otwarto połączenie: ");
            }catch (ArgumentNullException arg)
            {
                Console.WriteLine(arg);
            }
            catch (ArgumentOutOfRangeException arg)
            {
                Console.WriteLine(arg);
            }
            catch (ArgumentException arg)
            {
                Console.WriteLine(arg);
            }
            catch (SocketException arg)
            {
                Console.WriteLine(arg);
            }
        }

        private bool check_Client_ID(int ID)
        {
            if (ID == 0 || ID > 10) return false;
            else if (Client_ID.Contains(ID)) { Console.WriteLine("Jest juz taki ID w tablicy."); return false; }
            else{
                for(int i = 0; i < 10; i++)
                {
                    if(Client_ID[i] == 0)
                    {
                        Client_ID[i] = ID;
                        break;
                    }
                }
                return true;
            }
        }
    }
0

Utwórz sobie klasę sesji np:

public class Session{
	public TcpClient Client { get; set; }
	public string Data { get; set; }
}

Teraz niech serwer trzyma sobie listę takich sesji i dodaje doń pozycję ilekroć ktoś nowy się podłączy. Jak już masz listę użytkowników to możesz zapić i-tego gościa robiąc mu this.Clients[i].Close();. A na jakiej podstawie będziesz ubijał klientów to już sam sobie określasz np: możesz ich po prostu wybrać prawoklikiem z jakiejś listy i dać Logout etc... do wyboru do koloru.

Aha, nowego klienta dodajesz do listy po client = listener.AcceptTcpClient(); , która, nazwijmy to, jest linijką blokującą pętlę do czasu podłączenia kolejnego klienta. W ogóle wrzuć obsługę każdego klienta do osobnego wątku, bo tak to Twój serwer potrafi obsłużyć tylko jednego na raz.

TcpClient'ów powinno być x, a nie tylko jeden tak jak u Ciebie.

0

Możesz nasłuchiwać po stronie serwera czy klient wysyła jakiekolwiek informacje, jeśli nie, zamknij połączenie. Więcej w linku:

http://stackoverflow.com/questions/4879341/tcpclient-close-doesnt-close-the-connection

1

Chodziło mi bardziej o coś takiego:

Serwer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace Server {
    class TCPServer {
        private class Session {
            public TcpClient Client { get; set; }
            public string Data { get; set; }
        }

        private List<Session> _sessions;
        private TcpListener _server;

        public TCPServer() {
            this._sessions = new List<Session>();
            this._server = null;
        }

        public void Start(int port) {
            try {
                this._server = new TcpListener(IPAddress.Any, port);
                this._server.Start();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        public void Listen() {
            TcpClient client = null;
            while (true) {
                client = this._server.AcceptTcpClient();
                ThreadPool.QueueUserWorkItem(delegate {

                    // przykładowe kryterium dla programu konsolowego.
                    if (this._sessions.Count == 1) {
                        this.KillSession(0);
                    }
                    else {
                        try {
                            var session = new Session();
                            session.Client = client;
                            this._sessions.Add(session);

                            var stream = client.GetStream();
                            var reader = new StreamReader(stream);

                            string line = "";
                            while ((line = reader.ReadLine()) != null) {
                                session.Data = line;
                                Console.WriteLine(session.Data);
                            }
                        }
                        catch (Exception) {
                        // celowo wyłaczone wyjątki żeby zbytnio nie zaśmiecać konsoli.
                        }
                    }
                });
            }
        }

        private void KillSession(int id) {
            for(int i=0;i<this._sessions.Count;i++)
                if (id == i) {
                    this.Send(this._sessions[i].Client, "<Kicked/>");
                    Console.WriteLine("Kicking: " + this._sessions[i].Client.Client.RemoteEndPoint.ToString());
                    this._sessions[i].Client.Close();
                    this._sessions.RemoveAt(i);
                    break;
                }
        }

        public void Send(TcpClient client, string data) {
            try {
                var stream = client.GetStream();
                var writer = new StreamWriter(stream);
                writer.WriteLine(data);
                writer.Flush();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }
    }

    class Program {
        static void Main(string[] args) {
            var server = new TCPServer();
            server.Start(9999);
            server.Listen();
        }
    }
}

Klient:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace Client {
    class TCPClient {
        private class Session {
            public TcpClient Client { get; set; }
            public string Data { get; set; }
        }

        private Session _session;

        public TCPClient() {
            this._session = new Session();
        }

        public void Connect(string address, int port) {
            try {
                this._session.Client = new TcpClient();
                this._session.Client.Connect(IPAddress.Parse(address),port);
                this.Send("New client: " + this._session.Client.Client.LocalEndPoint.ToString());
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        public void Recieve() {
            try {
                bool stan = true;
                while (stan) {
                    try {
                        var stream = this._session.Client.GetStream();
                        var reader = new StreamReader(stream);

                        string line = "";
                        while ((line = reader.ReadLine()) != null) {
                            this._session.Data = line;

                            if (this._session.Data.Equals("<Kicked/>")) {
                                Console.WriteLine("Kicked!");
                                stan = false;
                                break;
                            }
                            else Console.WriteLine(this._session.Data);

                        }
                    }
                    catch (Exception) {
                        // celowo wyłaczone wyjątki żeby nie zaśmiecać meritum problemu.
                    }
                }
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }

        public void Send(string text) {
            try {
                var stream = this._session.Client.GetStream();
                var writer = new StreamWriter(stream);
                writer.WriteLine(text);
                writer.Flush();
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }
    }

    class Program {
        static void Main(string[] args) {
            var client = new TCPClient();
            client.Connect("127.0.0.1", 9999);
            client.Recieve();
            Console.ReadLine();
        }
    }
}

Serwer ma ustawione, że może obsłużyć maks jednego klienta. Jeżeli kolejny będzie chciał się podłączyć to poprzedni podłączony dostanie kick'a, żeby wpuścić kolejnego. Tym samym to serwer decyduje jak i kogo ma wywalić. Dla programu konsolowego trochę ciężko mi było cokolwiek wymyślić. :) Przyzwyczajony jestem do tworzenia takich rzeczy w GUI gdzie robi się to jakoś bardziej intuicyjnie hehe.

Oczywiście dobrze byłoby połapać wyjątki po obu stronach. Teraz np. kiedy zamkniesz okno klienta to połączenie zostanie brutalnie zerwane co na serwerze wygeneruje wyjątek. I widzisz... teraz musiałbyś połapać zdarzenie zamknięcia okna konsoli i w tym zdarzeniu wysłać do serwera np. komunikat <DisconnectRequest/>. Serwer na taki komunikat mógłby odłączyć ładnie klienta i wyrzucić go z listy. Stosowanie GUI ułatwia sprawę, bo masz do takich rzeczy gotowe eventy.

0

Aha, nowego klienta dodajesz do listy po client = listener.AcceptTcpClient(); , która, nazwijmy to, jest linijką blokującą pętlę do czasu podłączenia kolejnego klienta. W ogóle wrzuć obsługę każdego klienta do osobnego wątku, bo tak to Twój serwer potrafi obsłużyć tylko jednego na raz.

TcpClient'ów powinno być x, a nie tylko jeden tak jak u Ciebie.

Zgadzam się z tym tylko, że to jest zadanie które mam zrobić wg danych punktów tzn:

  • nawiązanie połączenia przez klienta
  • wysłanie identyfikatora klienta
  • wysłanie przez klienta liczby znaków n
  • wysłanie przez klienta ciągu znaków
  • odesłanie przez serwer informacji czy ciąg składał się z n znaków
  • rozłączenie zainicjowane przez serwer

W dodatku to ma być prosty program który tylko to zrobi i przechwycić to połączenie w Wiresharku. Wiec dopóki spełnia te punkty to raczej nie ma sensu rozbudowywać go.

0

No to stary... cały program Ci przecież praktycznie napisałem. :) Wystarczy, że zrobisz komunikację, której przykład również masz pokazany.

1

Wykładowca się nie obrazi jeśli włożysz w to więcej pracy. Zawsze to trochę więcej wiedzy dla Ciebie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1