[C#]Wstrzymywanie wątków w określonych warunkach


(eureka 170) #1

Witam, mam następujący program:

public partial class Form1 : Form

    {

        private Delegate addDelegate;

        public delegate void AddListItem(String text);

        UdpClient udpClient;

        Form2 Okno;

        bool flaga;

        Thread t1;

        int licznik;

        public Form1()

        {

            licznik = 0;

            InitializeComponent();

            udpClient = new UdpClient(8080);

            t1 = new Thread(new ThreadStart(Nasluchiwanie));           

            addDelegate = new AddListItem(DodajTekst);

            flaga = true;

            t1.Start();

        }


        private void button1_Click(object sender, EventArgs e)

        {



        }


        public void Nasluchiwanie()

        {

            while (flaga == true)

            {

                if (licznik != 0)

                {

                    flaga = false;

                }


                licznik++;

                string text;

                IPEndPoint RemoteIPEndPoint = new IPEndPoint(IPAddress.Any, 0);

                Byte[] receivedBytes = udpClient.Receive(ref RemoteIPEndPoint);

                string returnData = Encoding.ASCII.GetString(receivedBytes);

                text = RemoteIPEndPoint.Address.ToString() + ":" + returnData;         

                listBox1.Invoke(addDelegate,text);



            }



        }

        public void DodajTekst(String text)

        {

            listBox1.Items.Add(text);

            MessageBox.Show("Komunikat");

        }

    }

Utworzyłem również aplikację, dla komputera-klienta, gdzie znajduje się jeden przycisk. Po wciśnięciu tego przycisku, na komputerze-serwerze powinien się wyświetlić komunikat. Problem polega na tym, że kiedy klient wciśnie np. 10 razy ten przycisk, to na komputerze-serwerze pojawi się 10 razy komunikat. Chciałem zrobić tak, żeby do momentu wciśnięcia w komunikacie przycisku OK, nie wyświetlało się nic, niezależnie od tego, ile razy w tym czasie na komputerze-kliencie ktoś wciśnie przycisk. Jak to napisać? Próbowałem w funkcji nasłuchiwanie wpisać Thread.Sleep(60000), ale okazuje się, że nadal te komunikaty przychodzą tylko że teraz co minutę ( a ja chciałem żeby te komunikaty gdzieś po prostu sobie przepadły). Próbowałem również zatrzymać wątek t1 metodą Abort() i znowu włączyć metodą Start(), ale dowiaduję się, że po wyłączeniu wątku nie można go włączyć ponownie. Macie jakiś pomysł?


(Ryan) #2

Masz dwie opcje.

  1. W serwerze ignorować komunikaty klienta, póki nie zostanie naciśnięte OK.

  2. Z serwera powiadamiać klienta kiedy wciśnięto ok. Póki klient nie otrzyma potwierdzenia, wciskanie w nim przycisku nie powinno wysyłać kolejnych komunikatów.

Popraw proszę literówkę w temacie.


(Tomek Matz) #3
  1. W momencie, gdy klient nawiązuje połączenie z serwerem, serwer odsyła do niego informację, czy może go obsłużyć, czy tez nie (będziesz musiał ustalić taki swój mini protokół). Klient odczytuje ten komunikat i w oparciu o niego decyduje, czy wysłać wiadomość, czy też nie.

Czemu UDP, a nie TCP? (pytam z ciekawości)


(eureka 170) #4

Właśnie chciałem coś takiego zrobić, ale tak jak pisałem próbowałem użyć Thread.Sleep, jednak mimo tego komunikaty przyjdą do komputera-serwera tyle, że w "zwolnionym tempie". Jak zrobić, by te komunikaty po prostu przepadły, jak je zignorować?

Po prostu w internecie był podobny program który wykorzystywał UdpClient i ja się na nim wzorowałem


(Tomek Matz) #5

Rozumiem. Jakbyś chciał jednak oprzeć ten swój program na protokole TCP, to możesz zacząć od przejrzenia poniższego kodu (traktuj go jako wersję < 1.0, bo dużo tutaj jeszcze trzeba dodać). Klasy Client.cs, Client.Designer.cs i ClientForm.cs wstaw w projekcie TCPClientDemo. Klasy Server.cs, ServerForm.Designer.cs i ServerForm.cs wstaw w projekcie TCPServerDemo. Klasy ServerState i ServerUtil wstaw w projekcie TCPCoreDemo. Te dwa pierwsze projekty to aplikacje okienkowe, a ten ostatni to biblioteka. W projektach TCPClientDemo i TCPServerDemo musisz ustawić referencję do projektu TCPCoreDemo.

Client.cs

using System;

using System.Net.Sockets;

using TCPCoreDemo;


namespace TCPClientDemo

{

    public class Client

    {

        private string hostname;

        private int port;

        TcpClient instance = null;


        public Client(string hostname, int port)

        {

            this.hostname = hostname;

            this.port = port;

            this.instance = null;

        }


        public void Start()

        {

            instance = new TcpClient(hostname, port);

        }


        public void Stop()

        {

            if (instance != null)

            {

                instance.Close();

                instance = null;

            }

        }


        public string SendMessage(string message)

        {

            NetworkStream stream = null;


            try

            {

                stream = instance.GetStream();


                string serverResponse = ServerUtil.Read(stream);

                if (serverResponse.CompareTo(ServerState.SERVER_NOT_BUSY.ToString()) == 0)

                {

                    ServerUtil.Write(stream, message);


                    serverResponse = ServerUtil.Read(stream);

                }


                return serverResponse;

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (stream != null)

                    stream.Close();

            }

        }

    }

}

ClientForm.cs

using System;

using System.Windows.Forms;


namespace TCPClientDemo

{

    public partial class ClientForm : Form

    {

        private Client client = null;


        public ClientForm()

        {

            InitializeComponent();

        }


        private void ClientForm_Load(object sender, EventArgs e)

        {

            client = new Client(Properties.Settings.Default.Hostname, Properties.Settings.Default.Port);

        }


        private void sendButton_Click(object sender, EventArgs e)

        {

            string serverResponse = string.Empty;

            try

            {

                client.Start();

                serverResponse = client.SendMessage(messageRichTextBox.Text);

            }

            catch (Exception ex)

            {

                serverResponse = string.Format("Wystąpił wyjątek! {0}\n", ex.Message);

            }

            finally

            {

                client.Stop();

            }


            infoRichTextBox.Text += string.Format("{0}\n", serverResponse);

        }

    }

}

ClientForm.Designer.cs

namespace TCPClientDemo

{

    partial class ClientForm

    {

        /// 

        /// Required designer variable.

        /// 

        private System.ComponentModel.IContainer components = null;


        /// 

        /// Clean up any resources being used.

        /// 

        /// true if managed resources should be disposed; otherwise, false.

        protected override void Dispose(bool disposing)

        {

            if (disposing && (components != null))

            {

                components.Dispose();

            }

            base.Dispose(disposing);

        }


        #region Windows Form Designer generated code


        /// 

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// 

        private void InitializeComponent()

        {

            this.sendButton = new System.Windows.Forms.Button();

            this.messageRichTextBox = new System.Windows.Forms.RichTextBox();

            this.infoRichTextBox = new System.Windows.Forms.RichTextBox();

            this.label1 = new System.Windows.Forms.Label();

            this.label2 = new System.Windows.Forms.Label();

            this.SuspendLayout();

            // 

            // sendButton

            // 

            this.sendButton.Location = new System.Drawing.Point(332, 22);

            this.sendButton.Name = "sendButton";

            this.sendButton.Size = new System.Drawing.Size(75, 23);

            this.sendButton.TabIndex = 1;

            this.sendButton.Text = "Send";

            this.sendButton.UseVisualStyleBackColor = true;

            this.sendButton.Click += new System.EventHandler(this.sendButton_Click);

            // 

            // messageRichTextBox

            // 

            this.messageRichTextBox.Location = new System.Drawing.Point(12, 22);

            this.messageRichTextBox.Name = "messageRichTextBox";

            this.messageRichTextBox.Size = new System.Drawing.Size(314, 96);

            this.messageRichTextBox.TabIndex = 2;

            this.messageRichTextBox.Text = "";

            // 

            // infoRichTextBox

            // 

            this.infoRichTextBox.Location = new System.Drawing.Point(12, 137);

            this.infoRichTextBox.Name = "infoRichTextBox";

            this.infoRichTextBox.Size = new System.Drawing.Size(314, 238);

            this.infoRichTextBox.TabIndex = 3;

            this.infoRichTextBox.Text = "";

            // 

            // label1

            // 

            this.label1.AutoSize = true;

            this.label1.Location = new System.Drawing.Point(12, 6);

            this.label1.Name = "label1";

            this.label1.Size = new System.Drawing.Size(50, 13);

            this.label1.TabIndex = 4;

            this.label1.Text = "Message";

            // 

            // label2

            // 

            this.label2.AutoSize = true;

            this.label2.Location = new System.Drawing.Point(12, 123);

            this.label2.Name = "label2";

            this.label2.Size = new System.Drawing.Size(30, 13);

            this.label2.TabIndex = 5;

            this.label2.Text = "Logs";

            // 

            // ClientForm

            // 

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

            this.ClientSize = new System.Drawing.Size(415, 387);

            this.Controls.Add(this.label2);

            this.Controls.Add(this.label1);

            this.Controls.Add(this.infoRichTextBox);

            this.Controls.Add(this.messageRichTextBox);

            this.Controls.Add(this.sendButton);

            this.Name = "ClientForm";

            this.Text = "Client";

            this.Load += new System.EventHandler(this.ClientForm_Load);

            this.ResumeLayout(false);

            this.PerformLayout();


        }


        #endregion


        private System.Windows.Forms.Button sendButton;

        private System.Windows.Forms.RichTextBox messageRichTextBox;

        private System.Windows.Forms.RichTextBox infoRichTextBox;

        private System.Windows.Forms.Label label1;

        private System.Windows.Forms.Label label2;

    }

}

Server.cs

using System;

using System.Net;

using System.Net.Sockets;

using TCPCoreDemo;


namespace TCPServerDemo

{

    public class Server

    {

        public class ServerEventArgs : EventArgs

        {

            public ServerEventArgs()

            {

                ClientMessage = string.Empty;

                ClientError = null;

            }


            public ServerEventArgs(string clientMessage)

                : base()

            {

                ClientMessage = clientMessage;

            }


            public ServerEventArgs(Exception clientError)

                : base()

            {

                ClientError = clientError;

            }


            public string ClientMessage { get; set; }

            public Exception ClientError { get; set; }

        }


        public delegate void ClientConnectedHandler(object source, ServerEventArgs args);

        public delegate void ClientDisconnectedHandler(object source, ServerEventArgs args);

        public delegate void ClientMessageReceivedHandler(object source, ServerEventArgs args);

        public delegate void ClientErrorHandler(object source, ServerEventArgs args);


        public event ClientConnectedHandler ClientConnected;

        public event ClientDisconnectedHandler ClientDisconnected;

        public event ClientMessageReceivedHandler ClientMessageReceived;

        public event ClientErrorHandler ClientError;


        private string ip;

        private int port;

        private TcpListener instance;


        public Server(string ip, int port)

        {

            this.ip = ip;

            this.port = port;

            this.instance = null;


            Busy = false;

        }


        public bool Busy { get; set; }


        protected void OnClientConnected(ServerEventArgs args)

        {

            if (ClientConnected != null)

                ClientConnected(this, args);

        }


        protected void OnClientDisconnected(ServerEventArgs args)

        {

            if (ClientDisconnected != null)

                ClientDisconnected(this, args);

        }


        protected void OnClientMessageReceived(ServerEventArgs args)

        {

            if (ClientMessageReceived != null)

                ClientMessageReceived(this, args);

        }


        protected void OnClientError(ServerEventArgs args)

        {

            if (ClientError != null)

                ClientError(this, args);

        }


        public void Start()

        {

            try

            {

                instance = new TcpListener(IPAddress.Parse(ip), port);

                instance.Start();


                while (Listen()) //TODO: Find a better way to break the main loop

                { }

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public void Stop()

        {

            if (instance != null)

            {

                instance.Stop();

                instance = null;

            }

        }


        private bool Listen()

        {

            TcpClient client = null;

            NetworkStream stream = null;


            try

            {

                if (instance == null)

                    return false;


                client = instance.AcceptTcpClient();

                stream = client.GetStream();


                OnClientConnected(new ServerEventArgs());


                string serverResponse = ServerState.SERVER_BUSY.ToString();

                if (!Busy)

                    serverResponse = ServerState.SERVER_NOT_BUSY.ToString();

                ServerUtil.Write(stream, serverResponse);


                if (!Busy)

                {

                    string clientMessage = ServerUtil.Read(stream);

                    OnClientMessageReceived(new ServerEventArgs(clientMessage));


                    serverResponse = ServerState.SERVER_MESSAGE_RECEIVED.ToString();

                    ServerUtil.Write(stream, serverResponse);

                }


                OnClientDisconnected(new ServerEventArgs());

            }

            catch (Exception ex)

            {

                OnClientError(new ServerEventArgs(ex));

            }

            finally

            {

                if (stream != null)

                    stream.Close();

                if (client != null)

                    client.Close();

            }


            return true;

        }

    }

}

ServerForm.cs

using System;

using System.Windows.Forms;

using System.Threading;


namespace TCPServerDemo

{

    public partial class ServerForm : Form

    {

        private delegate void SetTextCallback(string text);


        private Server server = null;


        public ServerForm()

        {

            InitializeComponent();

        }


        private void ServerForm_Load(object sender, EventArgs e)

        {

            server = new Server(Properties.Settings.Default.IP, Properties.Settings.Default.Port);

            server.ClientConnected += new Server.ClientConnectedHandler(server_ClientConnected);

            server.ClientMessageReceived += new Server.ClientMessageReceivedHandler(server_ClientMessageReceived);

            server.ClientDisconnected += new Server.ClientDisconnectedHandler(server_ClientDisconnected);

            server.ClientError += new Server.ClientErrorHandler(server_ClientError);

        }


        private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)

        {

            stopButton_Click(null, null);

        }


        private void startButton_Click(object sender, EventArgs e)

        {

            Thread t = new Thread(server.Start);

            t.Start();

            SetText("Serwer włączony.\n");

        }


        private void stopButton_Click(object sender, EventArgs e)

        {

            server.Stop();

            SetText("Serwer wyłączony.\n");

        }


        private void server_ClientConnected(object source, Server.ServerEventArgs args)

        {

            SetText("Klient połączony.\n");

        }


        private void server_ClientDisconnected(object source, Server.ServerEventArgs args)

        {

            SetText("Klient rozłączony.\n");

        }


        private void server_ClientMessageReceived(object source, Server.ServerEventArgs args)

        {

            SetText(string.Format("{0}\n", args.ClientMessage));


            Thread t = new Thread(delegate()

            {

                Pause(args.ClientMessage);

            });

            t.Start();

        }


        private void server_ClientError(object source, Server.ServerEventArgs args)

        {

            SetText(string.Format("Wystąpił wyjątek! {0}\n", args.ClientError.Message));

        }


        private void SetText(string text)

        {

            if (infoRichTextBox.InvokeRequired)

                infoRichTextBox.Invoke(new SetTextCallback(SetText), new object[] { text });

            else

                infoRichTextBox.Text += text;

        }


        private void Pause(string message)

        {

            server.Busy = true;

            MessageBox.Show("Otrzymano wiadomość o treści \"" + message + "\"!");

            server.Busy = false;

        }

    }

}

ServerForm.Designer.cs

namespace TCPServerDemo

{

    partial class ServerForm

    {

        /// 

        /// Required designer variable.

        /// 

        private System.ComponentModel.IContainer components = null;


        /// 

        /// Clean up any resources being used.

        /// 

        /// true if managed resources should be disposed; otherwise, false.

        protected override void Dispose(bool disposing)

        {

            if (disposing && (components != null))

            {

                components.Dispose();

            }

            base.Dispose(disposing);

        }


        #region Windows Form Designer generated code


        /// 

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// 

        private void InitializeComponent()

        {

            this.label2 = new System.Windows.Forms.Label();

            this.infoRichTextBox = new System.Windows.Forms.RichTextBox();

            this.startButton = new System.Windows.Forms.Button();

            this.stopButton = new System.Windows.Forms.Button();

            this.SuspendLayout();

            // 

            // label2

            // 

            this.label2.AutoSize = true;

            this.label2.Location = new System.Drawing.Point(10, 13);

            this.label2.Name = "label2";

            this.label2.Size = new System.Drawing.Size(30, 13);

            this.label2.TabIndex = 10;

            this.label2.Text = "Logs";

            // 

            // infoRichTextBox

            // 

            this.infoRichTextBox.Location = new System.Drawing.Point(10, 27);

            this.infoRichTextBox.Name = "infoRichTextBox";

            this.infoRichTextBox.Size = new System.Drawing.Size(314, 348);

            this.infoRichTextBox.TabIndex = 8;

            this.infoRichTextBox.Text = "";

            // 

            // startButton

            // 

            this.startButton.Location = new System.Drawing.Point(330, 25);

            this.startButton.Name = "startButton";

            this.startButton.Size = new System.Drawing.Size(75, 23);

            this.startButton.TabIndex = 6;

            this.startButton.Text = "Start";

            this.startButton.UseVisualStyleBackColor = true;

            this.startButton.Click += new System.EventHandler(this.startButton_Click);

            // 

            // stopButton

            // 

            this.stopButton.Location = new System.Drawing.Point(330, 54);

            this.stopButton.Name = "stopButton";

            this.stopButton.Size = new System.Drawing.Size(75, 23);

            this.stopButton.TabIndex = 11;

            this.stopButton.Text = "Stop";

            this.stopButton.UseVisualStyleBackColor = true;

            this.stopButton.Click += new System.EventHandler(this.stopButton_Click);

            // 

            // ServerForm

            // 

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

            this.ClientSize = new System.Drawing.Size(415, 387);

            this.Controls.Add(this.stopButton);

            this.Controls.Add(this.label2);

            this.Controls.Add(this.infoRichTextBox);

            this.Controls.Add(this.startButton);

            this.Name = "ServerForm";

            this.Text = "Server";

            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerForm_FormClosing);

            this.Load += new System.EventHandler(this.ServerForm_Load);

            this.ResumeLayout(false);

            this.PerformLayout();


        }


        #endregion


        private System.Windows.Forms.Label label2;

        private System.Windows.Forms.RichTextBox infoRichTextBox;

        private System.Windows.Forms.Button startButton;

        private System.Windows.Forms.Button stopButton;





    }

}

ServerState.cs

using System;


namespace TCPCoreDemo

{

    public enum ServerState

    {

        SERVER_BUSY,

        SERVER_NOT_BUSY,

        SERVER_MESSAGE_RECEIVED,

        SERVER_MESSAGE_NOT_RECEIVED

    }

}

ServerUtil.cs

using System;

using System.Text;

using System.Net.Sockets;


namespace TCPCoreDemo

{

    public static class ServerUtil

    {

        public static string Read(NetworkStream stream)

        {

            string serverResponse = string.Empty;


            byte[] buffer = new byte[BitConverter.GetBytes(0).Length];

            int bytesRead = stream.Read(buffer, 0, buffer.Length);


            buffer = new byte[BitConverter.ToInt32(buffer, 0)];

            if (buffer.Length > 0)

            {

                bytesRead = stream.Read(buffer, 0, buffer.Length);

                serverResponse = Encoding.UTF8.GetString(buffer, 0, bytesRead);

            }


            return serverResponse;

        }


        public static void Write(NetworkStream stream, string message)

        {

            if (message == null)

                message = string.Empty;


            byte[] bufferMessage = Encoding.UTF8.GetBytes(message);

            byte[] bufferMessageSize = BitConverter.GetBytes(bufferMessage.Length);


            stream.Write(bufferMessageSize, 0, bufferMessageSize.Length);

            stream.Write(bufferMessage, 0, bufferMessage.Length);

        }

    }

}

-- Dodane 07.09.2011 (Śr) 22:35 --

Doszło parę zmian. Zaktualizowałem kod :slight_smile:


(eureka 170) #6

Dzięki matzu za kod :slight_smile:

Rozwiązałem swój problem również w ten sposób, że klient wysyłając komunikat, musi zaczekać 20 sekund, zanim będzie mógł wysłać kolejny. Dzięki temu przez te 20 sek może wciskać przycisk ile chce, a na serwerze nic się nie pojawi


(Tomek Matz) #7

@eureka 170

Nie ma za co. Twój sposób może być, ale pod warunkiem, że zawsze w ciągu tych 20 sec klikniesz na serwerze w okienko komunikatu. Jeśli tego nie zrobisz, klient po 20 sec wyśle nową wiadomość i zostanie ona skolejkowana po stronie serwera jako oczekująca na przetworzenie. Poza tym i tak najlepiej byłoby zrobić połączenie dwóch rozwiązań, czyli po stronie klienta jest jakiś delay ustawiony, który nie pozwala na natychmiastowe wysyłanie wiadomości, a po stronie serwera jest sprawdzanie, czy na pewno okienko komunikatu zostało kliknięte.