C# client server

Status
Niet open voor verdere reacties.

henkjan111

Gebruiker
Lid geworden
9 jan 2011
Berichten
221
Hallo,

Ik probeer een (multi) client server applicatie te maken in C#. Het lukt allemaal vrij aardig. Totdat ik simpel een ProgressBar een value wil geven (met een label lukt het wel). Dan krijg ik deze foutmelding:
Code:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll

Additional information: Het is niet toegestaan een bewerking uit te voeren via verschillende threads: er werd vanaf een andere thread toegang gekregen tot het besturingselement Progress_Startup dan de thread waarop het element is gemaakt.

Dit is de client code:

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace Globbic_Workstation
{
    public partial class Frm_StartupWS : Form
    {
        private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        byte[] receivedBuf = new byte[1024];

        private void ReceiveData(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            int received = socket.EndReceive(ar);
            byte[] dataBuf = new byte[received];
            Array.Copy(receivedBuf, dataBuf, received);
            lb_stt.Text = (Encoding.ASCII.GetString(dataBuf));
            if (Encoding.ASCII.GetString(dataBuf) == "Start")
            {
                Progress_Startup.Value = 100;
            }
            
            _clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
        }

        private void SendLoop()
        {
            while (true)
            {
                byte[] receivedBuf = new byte[1024];
                int rev = _clientSocket.Receive(receivedBuf);
                if (rev != 0)
                {
                    byte[] data = new byte[rev];
                    Array.Copy(receivedBuf, data, rev);
                   lb_stt.Text = ("Received: " + Encoding.ASCII.GetString(data));
                }
                else _clientSocket.Close();

            }
        }

        private void LoopConnect()
        {
            int attempts = 0;
            while (!_clientSocket.Connected)
            {
                try
                {
                    attempts++;
                    _clientSocket.Connect(IPAddress.Loopback, 55555);
                }
                catch (SocketException)
                {
                    MessageBox.Show("Connection attempts: " + attempts.ToString());
                }
            }
            MessageBox.Show("Connected!");
        }


        public Frm_StartupWS()
        {
            InitializeComponent();
        }

        private void Frm_StartupWS_Load(object sender, EventArgs e)
        {
            LoopConnect();
            _clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
            byte[] buffer = Encoding.ASCII.GetBytes("Workstation");
            _clientSocket.Send(buffer);
            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {

        }
    }
}
 
async je form updaten is nog steeds hetzelfde natuurlijk ;). in het derde blokje van bovenstaande link vind je een form die async wordt geupdate mbv van een "delegate" en "invoke"

Code:
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        delegate void ListUpdate(); //delegate voor het Asynchroon updaten van de listbox
        
        private void update_listbox() //listbox updaten met actieve clients
        {
            listBox1.Items.Clear();
            listBox1.Items.AddRange(MyServer.namelist());
        }

        void MyServer_OnDisconnect(object sender, EventArgs e)
        {
            listBox1.Invoke(new ListUpdate(this.update_listbox)); //listbox updaten als er een disconnect is
        }

        void MyServer_OnConnection(object sender, EventArgs e)
        {
            listBox1.Invoke(new ListUpdate(this.update_listbox)); //listbox updaten als en een connect is
        }


}
 
Ligt het aan mij, of zit er geen verschil tussen het OnDisconnect en OnConnection updaten? Ik snap het even niet...

Code:
void MyServer_OnDisconnect(object sender, EventArgs e)
        {
            listBox1.Invoke(new ListUpdate(this.update_listbox)); //listbox updaten als er een disconnect is
        }

        void MyServer_OnConnection(object sender, EventArgs e)
        {
            listBox1.Invoke(new ListUpdate(this.update_listbox)); //listbox updaten als en een connect is
        }
 
voor de bovenstaande code inderdaad niet. De lijst moet ververst worden als er clients bijkomen en weggaan. Het verversen gebeurt in de eigenlijke routine

Voor jouw specifieke code wil je de Invoke methode van je progressbar aanroepen.

mogelijk dat dit werkt:

Code:
progressBar.Invoke(new Action(() => progressBar.Value = count));

maar is natuurlijk afhankelijk waar de waarde vandaan moet komen in de verschillende gevallen
 
(Late reactie vanuit mij...)

JE BENT EEN HELD! :) Werkt (met aanpassingen naar eigen waarde) perfect voor mij!
Super.

Heb je toevallig ergens een tutorial/uitleg/etc voor het Async gebeuren? Anders ga ik eens op het internet rondneuzen.

Nogmaals bedankt!!
 
Het is sowieso nuttig om je erop in te lezen, want het is nagenoeg onmogelijk om dit in een enkele reply te stoppen. Ook is het een beetje afhankelijk van hoeveel je weet van threading en computers algemeen.

Het belangrijkste is dat de meeste visuele elementen van windows niet "thread-safe" zijn. ze kunnen dus niet veilig gebruikt worden vanuit meerdere processen. Een "delegate" (afgevaardigde) is een functie binnen de thread waarin het element dat je wil aanspreken draait. Omdat de delegate "lokaal" is, kan die wel veilig worden uitgevoerd.

Elke thread op je computer heeft zijn eigen state en ook een eigen "message-queue". Wat dit eigenlijk doet is een lokaal bekende functie (delegate) op de queue zetten van de thread vanuit een andere thread. vervolgens wordt de queue netjes in de juiste volgorde uitgevoerd.
 
Nog een vervolg vraag. Als de server disconnect (door internet interruptie/server shutdown, etc) en de client wilt reconnecten. Kan dit ook niet zomaar. Waarschijnlijk zal dit ook Assync moeten.
Nu snap ik alleen nog niet hoe.
Bij het reconnecten doe ik:
Code:
LoopConnect();
                _clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
                byte[] buffer = Encoding.ASCII.GetBytes("Workstation");
                _clientSocket.Send(buffer);

Hier de connect code:
Code:
private void LoopConnect()
        {
            int attempts = 0;
            while (!_clientSocket.Connected)
            {
                try
                {
                    attempts++;
                    _clientSocket.Connect(IPAddress.Loopback, 55555);
                }
                catch (SocketException)
                {
                    MessageBox.Show("Connection attempts: " + attempts.ToString());
                }
            }
            MessageBox.Show("Connected!");
        }

Alvast bedankt
 
Wat triggered na een disconnect een nieuwe poging? Als je client een disconnect "event" krijgt kun je vanuit dat event vervolgens weer proberen terug te connecten.

Hoewel ongerelateerd zou ik in je try blok van Loopconnect een kleine sleep toevoegen om de belasting op de client onder controle te houden.
 
De disconnect wordt gedetecteerd door een try - catch.
Code:
 private void ReceiveData(IAsyncResult ar)
        {
            try
            {
            Socket socket = (Socket)ar.AsyncState;
            int received = socket.EndReceive(ar);
            byte[] dataBuf = new byte[received];
            Array.Copy(receivedBuf, dataBuf, received);
            lb_stt.Text = (Encoding.ASCII.GetString(dataBuf));
            if (Encoding.ASCII.GetString(dataBuf) == "Start")
            {
                //Progress_Startup.Invoke(new Action(() => Progress_Startup.Value = 100));
                MessageBox.Show("Startedddd");
            }
            
            _clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
            }
            catch
            {
                MessageBox.Show("Server disconnected");
                //Hier begint die dus opnieuw met connecten, echter krijg ik de foutmelding dat het alleen Asynchroon kan
                LoopConnect();
                _clientSocket.BeginReceive(receivedBuf, 0, receivedBuf.Length, SocketFlags.None, new AsyncCallback(ReceiveData), _clientSocket);
                byte[] buffer = Encoding.ASCII.GetBytes("Workstation");
                _clientSocket.Send(buffer);
            }
        }

Foutmelding:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.dll

Additional information: Zodra de socket is verwijderd, kunt u alleen op asynchrone wijze opnieuw verbinding maken, en alleen met een ander EndPoint. BeginConnect moet worden aangeroepen voor een thread die niet wordt afgesloten tot de bewerking is voltooid.


Wat je zegt met het try blok van Loopconnect, was ik van plan om later nog toe te voegen
 
Gezien de foutmelding moet je inderdaad de oude thread afsluiten, een disconnect event naar de main thread geven en opnieuw een nieuwe thread maken vanaf het begin vanuit de main thread.

Mogelijk zijn er in de laatste .NET functies die gebruik maken van de nieuwe ASYNC die werken zonder een callback, maar dat is niet iets waar ik zelf al ingedoken ben.
 
Het werkt inderdaad als ik hem sluit en opnieuw open:

Code:
_clientSocket.Close();
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan