Autor Beitrag
Ccenter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Mo 26.07.10 19:19 
Ich bastel gerade weiter an dem Chatprogramm. Bis jetzt wars eigendlich nicht wirklich eins, sondern eher 2 Server und 2 Clienten. Zum Chatten sind also gerade 2 Programme nötig :D
Ich wollte mich ersteinmal so an die Grundzüge der Netzwerkprogrammierung heranwagen, deshalb hab ich das Problem vorab nicht gelöst.
Jetzt will ich das Projekt aber etwas ernster angehen, so dass ich am Ende einen wirklich funktionierenden Chat habe.
Da es aber definitiv keine Lösung ist, zwei Programme am laufen zu haben um zu Chatten, will ich diese nach Möglichkeit vereinen.

Mein erster Lösungsansatz bestand darin, die Reveiver-Methode in einem Thread auszulagern.
Jedoch ist das entweder der falsche Ansatz, oder ich habs falsch programmiert.

Edit:
Client:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace Chat
{
    class Program
    {
        public static NetworkStream Writer;
        public static NetworkStream Receiver;
        static void Main(string[] args)
        {    
       
            #region Client
            TcpClient Connector = new TcpClient();
            Console.WriteLine("Please type IP:");
            string IP = Console.ReadLine();
            restart:

            try
            {
                Connector.Connect(IP, 2000);
                Console.WriteLine("Connection etablished");
            }
            catch
            {
                Console.WriteLine("Connection failed, restart?");
                string str = Console.ReadLine();
                if (str == "yes")
                {
                    goto restart;
                }

            }

            System.Threading.Thread Rec = new System.Threading.Thread(new     
                System.Threading.ThreadStart(Receive));
            Rec.Start();
            sender();     
            #endregion          
        }


        #region Receiver
        public static void Receive()
        {
            
            while (true)
            {
                try
                {
                    byte[] RecPacket = new byte[1000];
                    Receiver.Read(RecPacket, 0, RecPacket.Length);

                    Receiver.Flush();

                    string text = Encoding.ASCII.GetString(RecPacket);
                    Console.WriteLine(text);                               
                }
                catch
                {
                                       
                }
            }
        }
        #endregion


        #region sender
        public static void sender()
        {
            while (true)
            {
                string text = Console.ReadLine();
                try
                {
                    Writer.Flush();
                    byte[] Packet = Encoding.ASCII.GetBytes(text);
                    Writer.Write(Packet, 0, Packet.Length);
                    Writer.Flush();
                }
                catch
                {
                    Console.WriteLine("Not able to send");
                }
            }
        }
        #endregion

    }
}


Server:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;

namespace Chat
{
    class Program
    {
        public static NetworkStream Writer;
        public static NetworkStream Receiver;

        static void Main(string[] args)
        {      
   
            #region server       
            Console.WriteLine("Start TcpListener");
            TcpListener l = new TcpListener(2000);
            l.Start();

            TcpClient Connection = l.AcceptTcpClient();
       
            Receiver = Connection.GetStream();
            Console.WriteLine("Connection etablished");
      
            System.Threading.Thread Rec = new System.Threading.Thread(new 
                  System.Threading.ThreadStart(Receive));
            Rec.Start();
            sender();
            #endregion         
        }


        #region Receiver
        public static void Receive()
        {
            Console.WriteLine("Start Receiver");
            while (true)
            {
                try
                {
                    byte[] RecPacket = new byte[1000];

                    Receiver.Read(RecPacket, 0, RecPacket.Length);

                    Receiver.Flush();

                    string text= Encoding.ASCII.GetString(RecPacket);
                    Console.WriteLine(text);                               
                }
                catch
                {
                                       
                }
            }
        }
        #endregion



        #region sender
        public static void sender()
        {       
            while (true)
            {
                string text = Console.ReadLine();
                try
                {
                    Writer.Flush();
                    byte[] Packet = Encoding.ASCII.GetBytes(text);
                    Writer.Write(Packet, 0, Packet.Length);
                    Writer.Flush();
                }
                catch
                {
                    Console.WriteLine("Not able to send");
                }
            }
        }
        #endregion

    }
}


Zuletzt bearbeitet von Ccenter am Mo 26.07.10 21:33, insgesamt 1-mal bearbeitet
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Mo 26.07.10 20:10 
Hallo,

für einen Chat gibt es meistens einen Server und viele Clients. Warum hast du zwei Server laufen?

Wenn der Chat immer nur zwei Teilnehmer haben soll und du nur ein Programm haben willst, kannst du einfach beide Module in ein Programm stecken. Threading sollte sowieso verwendet werden, denn andernfalls bleibt die Oberfläche bei jeder Aktion (also auch beim Update) hängen.

Zu deinem Anhang: Du hast nur die Datei hochgeladen, in der steht, dass du ein Projekt namens "ChatR00m" angelegt hast. Schau sie dir doch mal an ;) Poste die Quelltexte hier direkt, dann muss man auch keine Dateien herunterladen.

Grüße,
Yogu
Ccenter Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Mo 26.07.10 21:38 
Oh tatsächlich, tut mir leid. Ist editiert.

Da der Chat eh nur für zwei Teilnehmer gedacht ist, habe ich ja gerade versucht die Server(/Receiver)-Komponente und die Client(/Sender)-Komponente zu vereinen. Ich denke mal mein Fehler liegt im Threading, ich weiß nur nicht wo und was ich da falsch gemacht habe.

Moderiert von user profile iconNarses: Überflüssige Zeilenumbrüche/Leerzeilen entfernt.
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Mo 26.07.10 21:52 
user profile iconCcenter hat folgendes geschrieben Zum zitierten Posting springen:
Ich denke mal mein Fehler liegt im Threading, ich weiß nur nicht wo und was ich da falsch gemacht habe.

user profile iconCcenter hat folgendes geschrieben Zum zitierten Posting springen:
Jedoch ist das entweder der falsche Ansatz, oder ich habs falsch programmiert.

Ja, irgendetwas in der Richtig wird's schon sein. Oder so ähnlich. Auf jeden Fall tut's nicht. Jedenfalls nicht so, wie's soll. Naja, eigentlich tut es ja gar nix - so ein Mist aber auch. Wenn man wenigstens wüsste, woran es liegt? Aber woher soll man das schon wieder wissen... Kann man wohl nichts machen, oder?
Sorry, ich konnte es mir nicht verkneifen :mrgreen:

Das erste, was du falsch gemacht hast, ist folgendes:
user profile iconCcenter hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
try {
  /* ... */
catch {

}

Und das sogar ein paar mal. Was du damit erreichst, ist schlicht und einfach, dass du nicht mehr mitbekommst, was schief läuft. Du fängst mit diesem Konstrukt alle Hinweise auf, die auf den Fehler hindeuten könnten, und schmeißt sie weg. Lass diese abenteuerlichen Kunstwerke mal weg und schau, was das Programm dir dann zu sagen hat ;)

Außerdem hast du Benutzerschnitstelle mit Logik verwurstelt, deswegen ist das ganze Programm auch sehr kompliziert geworden.

Ich würde dir vorschlagen, Server und Client in zwei Klassen auszulagern, die in etwa so aussehen:

ausblenden C#-Quelltext
1:
2:
3:
4:
static class Server {
  public static void Open() { }
  public static event Action<string> MessageReceived;
}


ausblenden C#-Quelltext
1:
2:
3:
4:
static class Client {
  public static void Connect(string ip, int port) { }
  public static void Send(string message);
}


Die Methoden musst du natürlich noch implementieren, aber das ist gar nicht so schwer.

Eine Erklärung zu event: Damit definierst du ein Ereignis. Im Hauptprogramm kannst du einen sogenannten EventHandler registrieren - eine Funktion, die immer dann aufgerufen wird, wenn das Ereignis auftritt, also eine Nachricht angekommen ist.

Auslösen kannst du das Ereignis von Server heraus einfach so:

ausblenden C#-Quelltext
1:
2:
if (MessageReceived != null)
  MessageReceived('die nachricht');

Und den EventHandler registrierst du folgendermaßen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
void Main(string[] args) {
  // ...
  Server.MessageReceived += new Action<string>(server_MessageReceived);
  // ...
}

void server_MessageReceived(string message) {
  // mach was mit message
}


So, jetzt schreibst du am besten erst mal die beiden Klassen. Sie sollen die komplette Kommunikation durchführen. Im Hauptprogramm musst du dann nur noch den Benutzer nach der entfernten IP-Adresse fragen, den Server starten und den Client verbinden. Dann registrierst du den Event-Handler wie oben und wartest nur noch ab, ob der Benutzer eine Nachricht senden möchte. Diese sendest du dann einfach per Client.Send('...');

Hab ich dich überrumpelt? Dann lies dir erst mal alles in Ruhe durch, das war jetzt schon ziemlich fortgeschrittene .NET-Entwicklung. Frag ruhig, wo was unklar ist :)

Grüße,
Yogu
Ccenter Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Mo 26.07.10 22:05 
Ja, so ganz ohne ist das nicht :D
Wenn ich ne Frage hab, sag ich bescheid.
Danke!
Ccenter Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Di 27.07.10 15:41 
Ok, im Endeffekt habe ich das gleiche Problem wie vorher.
Nur der Code ist jetzt "schöner" in Klassen unterteilt.
Vorher wurde die Fehlermeldung von try/catch abgefangen.
Ohne try/catch erhalte ich diese Meldung:
"Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."

Diese Fehlermeldung erhalte ich an zwei Stellen. Einmal bei der Receive-Methode in der Class Server und ein zweites Mal in der Klasse Client in der SendMessage-Methode. Ich vermute mal, dass das Objekt zu dem Zeitpunkt an dem die Meldung ausgegeben wird, NULL ist. Deshalb hab ich das problem in der Receiver-Methode mit einer While-Schleife und try/catch gelöst. Das hat da auch so weit funktioniert, jedoch nicht mehr, wenn ich eine Nachricht schicken möchte. Wie löse ich jetzt das Problem in der SendMessage-Methode?

Ich lade die Projektmappen jetzt doch hoch, damit das mit den Klassen etc nicht so unübersichtlich wird.
Einloggen, um Attachments anzusehen!
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Di 27.07.10 18:42 
ausblenden volle Höhe C#-Quelltext
 
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
/* ... */
    class Client
    {
        public static NetworkStream Writer;
        public static NetworkStream Receiver;

        public static void ConnectToServer(string Ip, int Port)
        {
            TcpClient Connector = new TcpClient();
            restart:
            try
            {
                Connector.Connect(Ip, Port);
                Console.WriteLine("Connection etablished");
            }
            catch
            {
                Console.WriteLine("Nobody to connect, restart?\n");
                string str = Console.ReadLine();
                if (str == "yes")
                {
                    goto restart;
                }
                else
                    return;
            }
        }

        public static void SendMessage(string Nachricht)
        {
            byte[] Packet = Encoding.ASCII.GetBytes(Nachricht); //Hier tritt der Fehler auf (Objekt = null?)
            Writer.Write(Packet, 0, Packet.Length);
            Writer.Flush();
        }

Du hast vergessen, Writer zuzuweisen. Das solltest du in der Methode ConnectToServer tun, denn sonst bleibt es eine Nullreferenz und sorgt anschließend zu der Exception. Du erhältst den Stream über connector.getStream.

Edit: Das ist erst der nächste Fehler. Der, den du gemeldet bekommst ist ein anderer: Console.ReadLine() gibt null zurück, wenn nichts eingegeben wurde. Du darfst in dem Fall natürlich nicht Client.SendMessage(); aufrufen.
Ccenter Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Di 27.07.10 20:02 
Den Fehler den du zuerst genannt hast, habe ich so gelöst:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
public static void ConnectToServer(string Ip, int Port)
{
     TcpClient Connector = new TcpClient();
     Writer = Connector.GetStream();
     //...
}


Edit:
Funktioniert doch nicht, sobald ich mich mit dem Server verbinden möchte, bekomme ich folgende Meldung:
"Der Vorgang ist für nicht verbundene Sockets unzulässig."


Jedoch verstehe ich den zweiten Fehler bezüglich Console.Readline(); nicht. Du hast gesagt:
Zitat:
Console.ReadLine() gibt null zurück, wenn nichts eingegeben wurde.

Wenn ich nichts eingegeben habe dann darf ich natürlich nicht SendMessage aufrufen(was ich ja auch nicht getan habe).
Aber wenn ich erst Enter drücke, wenn ich Text eingegeben habe, dann darf Console.ReadLine()
doch eigendlich nicht null zurückgeben?
Oder was verstehe ich da falsch?
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Mi 28.07.10 20:20 
user profile iconCcenter hat folgendes geschrieben Zum zitierten Posting springen:
Funktioniert doch nicht, sobald ich mich mit dem Server verbinden möchte, bekomme ich folgende Meldung:
"Der Vorgang ist für nicht verbundene Sockets unzulässig."

Na, dann führe den Vorgang doch erst durch, wenn der Socket verbunden ist - also nach

ausblenden C#-Quelltext
 
20:
/* ... */
Connector.Connect(Ip, Port);

:idea:

user profile iconCcenter hat folgendes geschrieben Zum zitierten Posting springen:
Aber wenn ich erst Enter drücke, wenn ich Text eingegeben habe, dann darf Console.ReadLine()
doch eigendlich nicht null zurückgeben?
Oder was verstehe ich da falsch?

Ok, eigentlich sollte die Methode auf eine Eingabe warten. Aber setze doch einfach einen Haltepunkt auf die Anweisung und schau, was die Methode für einen Rückgabewert liefert.

Grüße,
Yogu

Edit: Ich habe mir dein Projekt jetzt noch einmal genauer angeschaut. Ich glaube, wir sollten erst einmal weg vom Code gehen und überlegen, wie das ganze ablaufen soll.

Du möchtest weg von der Server-Client-Methode gehen, weil das für den Benutzer einfacher ist. Technisch ist das aber nicht möglich, es ist immer ein Server und ein Client notwendig.

Ein Lösungsansatz wäre, dass einer der Gesprächspartner Server, der andere Client ist. Beide müssten sich darüber absprechen. Die erste Person gibt in seinem Programm an, dass es an einem bestimmten Port horchen soll. Der zweite gibt ein, dass das Programm sich zu einer bestimmten IP-Adresse verbinden soll. Beides kann in einem Programm untergebracht werden, es sind nur zwei verschiedene Modi.

Wenn du willst, kann ich dir in einer Art "interaktivem Tutorial" eine Lösung nahebringen, die ich gerade entwickelt habe. Im Anhang findest du das fertige Projekt, das ich auch ausführlich kommentiert habe. Ich kann es dir aber auch gerne Schritt für Schritt erklären.
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von Yogu am Mi 25.08.10 20:45, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: Ccenter
Ccenter Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 154

Win7
C#
BeitragVerfasst: Do 29.07.10 17:52 
Wow, danke das du dir die Mühe gemacht hast :)
Hat mir auf jeden Fall geholfen!
Mein Programm funktioniert jetzt :)

Es war eigendlich soweit funktionsfähig, es bestand ebend nur das Problem
mit Writer = Connector.GetStream();
Nachdem ich dort meinen Fehler behoben hatte, gab es eigendlich kaum noch Problem.