Xamarin TcpClient Chat

0

Witam,
Napisałem działającą tcp czat lan aplikacje na windows. Następnie chciałem przenieść aplikacje na androida poprzez xamarina by połączyć komputer(server) i telefon(client)
Ustawilem na xamarinie permission Internet oraz operuję na wifi 192.168.x.x

Program się łączy z serwerem( serwer na kompie):

private void buttonClickConnect(object sender, EventArgs e)
                {
                    client = new TcpClient();
                    IPEndPoint IP_End = new IPEndPoint(IPAddress.Parse("192.168.1.1"), int.Parse("13000"));
                    try
                    {
                        client.Connect(IP_End);
                        if (client.Connected)
                        {
                            textviewConversation.Text += "Connected to server" + "\n";
                            STR = new StreamReader(client.GetStream());
                            STW = new StreamWriter(client.GetStream());
                            STW.AutoFlush = true;
                            worker1.RunWorkerAsync();
                            worker2.WorkerSupportsCancellation = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }

Łączy się z serwerem ale nie może wysłać wiadomości. Raz wciskam send to nic sie nie dzieje, drugi raz klikam to wywala że thread jesy busy.

private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)   //sending message, here error probably
        {
            if (client.Connected)
            {
                STW.WriteLine(text_to_send);
            }
            else
            {
                Console.WriteLine("send failed !");
            }
            worker2.CancelAsync();
        }

Nie ma pętli w tym wątku powinno zatem wysłać i zwolnić wątek.
Pełny kod napisany na xamarn: http://pastebin.com/DJxHUdXn,

Błagam o pomoc, wierzę ze nie wiele trzeba by to wszystko działało : ))
Pozdrawiam

1

Spróbuj może coś takiego:


//w kodzie buttonClickConnect, ns to pole klasy typu NetworkStream
ns = client.GetStream();
//w backgroundWorder2 daj coś takiego:

byte[] msg = Encoding.ASCII.GetBytes("jakas wiadomosc");
ns.Write(msg,0,msg.Leght);

Ale coś mi ogólnie nie gra z tymi backgroundWorker'ami. Nie lepiej po prostu odpalić normalnego Taska z while który by cały czas odbierał wiadomości, a jakbyś chciał wysłać to po prostu wysyłasz 'normalnie' (synchronicznie) ?

1
wonman napisał(a):

Łączy się z serwerem ale nie może wysłać wiadomości. Raz wciskam send to nic sie nie dzieje, drugi raz klikam to wywala że thread jesy busy.

Powodem jest to że próbujesz uruchomić jeszcze raz już wcześniej uruchomiony "BackgroundWorker", żeby się przed tym uchronić możesz użyć if (worker1.IsBusy){...}.

A co do kodu to trochę ciężko się w tym połapać, wyrzuć na początek te wszystkie operacje po sieci do osobnej klasy żeby jakoś nad tym zapanować.

0
<quote="1282116">Spróbuj może coś takiego: ```csharp //w kodzie buttonClickConnect, ns to pole klasy typu NetworkStream ns = client.GetStream(); //w backgroundWorder2 daj coś takiego: byte[] msg = Encoding.ASCII.GetBytes("jakas wiadomosc"); ns.Write(msg,0,msg.Leght); ``` Zastosowałem w tych miejscach: ( public NetworkStream ns dalem na początku klasy ) ```csharp private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) //sending message, here error probably { if (client.Connected) { byte[] msg = Encoding.ASCII.GetBytes("jakas wiadomosc"); ns.Write(msg, 0, msg.Length);<[email protected]@@@@@@@@@@@@@@@@@@@@@@@ TUTAJ DAŁEM //STW.WriteLine(text_to_send); <------ to zakomentowałe } else ``` oraz: ```csharp private void buttonClickConnect(object sender, EventArgs e) { client = new TcpClient(); IPEndPoint IP_End = new IPEndPoint(IPAddress.Parse("192.168.1.1"), int.Parse("13000")); try { client.Connect(IP_End); if (client.Connected) { textviewConversation.Text += "Connected to server" + "\n"; STR = new StreamReader(client.GetStream()); STW = new StreamWriter(client.GetStream()); STW.AutoFlush = true; ns = client.GetStream(); <[email protected]@@@@@@@@@@@@@@@@@@@@@@@ TUTAJ DAŁEM worker1.RunWorkerAsync(); worker2.WorkerSupportsCancellation = true; ``` W kwestii logiki kodu hm... bazowałem na różnych źródłach tworząc tą aplikacje, nie mam dużego doświadczenia. Aplikacja działa na widnowsie to zostawiłem tak :) Po zastosowaniu Twoich zmian wciąż to samo - nie działa, jednak mogę nadmienić dokładniej: Klikam połącz, łączy się. Wpisuje i wysyłam #1 wiadomość, nic sie nie dzieje Wpisuje i wysyłam #2 wiadomość, i nagle przycina wypisuje że thread busy. Sam jednak sygnał dochodzi do servera który wariuje, dostaje jak by dziesiątki wiadomosci pustych. I tam też crashuje że thread multi. Ciekawe zjawisko. Mi się wydaje że ta wiadomość leci w cząstkach? temu tak dużo przychodzi na server powiadomień ale pustych wiadomości w stringu. kod SERWERA na PC ```csharp using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace serverDoWielu { public partial class MainWindow : Window { private List<tcpclient> listaClientow = new List<tcpclient>(); private List<streamreader> listaSTR = new List<streamreader>(); private List<streamwriter> listaSTW = new List<streamwriter>(); StreamReader STR; StreamWriter STW; private string received; private String text_to_send; public BackgroundWorker worker2; public BackgroundWorker worker3; int licznikClientow; public MainWindow() { InitializeComponent(); worker2 = new BackgroundWorker(); worker2.DoWork += backgroundWorker2_DoWork; worker3 = new BackgroundWorker(); worker3.DoWork += backgroundWorker3_DoWork; worker2.WorkerSupportsCancellation = true; } private void button_Click(object sender, RoutedEventArgs e) { worker3.RunWorkerAsync(); } private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) { string wiadomosc = (string)e.Argument; List<streamwriter> tmp = new List<streamwriter>(listaSTW); foreach (var item in tmp) { item.WriteLine(wiadomosc); } worker2.CancelAsync(); } private void backgroundWorker3_DoWork(object sender, DoWorkEventArgs e) { TcpListener listener = new TcpListener(IPAddress.Parse("192.168.1.1"), 13000); // TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000); licznikClientow = 0; while (true) { listener.Start(); listaClientow.Add(new TcpClient()); // add new listaClientow[licznikClientow] = listener.AcceptTcpClient(); //przypisujemy listaSTR.Add(new StreamReader( listaClientow[licznikClientow].GetStream() ) ); // to read listaSTW.Add(new StreamWriter( listaClientow[licznikClientow].GetStream() ) ); // to send listaSTW[licznikClientow].AutoFlush = true; //clear BackgroundWorker clientThread = new BackgroundWorker(); clientThread.WorkerSupportsCancellation = true; clientThread.DoWork += new DoWorkEventHandler(clientThread_DoWork); clientThread.RunWorkerAsync(licznikClientow); licznikClientow++; } } private void clientThread_DoWork(object sender, DoWorkEventArgs e) { int ktoryKlient = (int)e.Argument; while (listaClientow[ktoryKlient].Connected) { try { received = ""; received = listaSTR[ktoryKlient].ReadLine(); Console.WriteLine("@@@@@@ "+received); <[email protected]@@@@@@@@@@@@@@@ To wypluwa w dziesiątkach @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ text_to_send = received; if (text_to_send != "") { this.textBox.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { textBox.AppendText("->> " + text_to_send + "\n"); }); worker2.RunWorkerAsync(text_to_send); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } } private void button1_Click(object sender, RoutedEventArgs e) { foreach (var item in listaSTW) // sending to all people { item.WriteLine(textBox1.Text); } } } } ```
1

o dziwne bo minimalnie zmieniona (buttony, text inputy, labele) na c# wpf śmiga pięknie ta apka. A na xamarin niezbyt

Po 1. Zmień na serwie IPAddress.Parse(...) na IPAddress.Any,

Hmm trochę dziwne, trzeba się teraz zastanowić czy to w ogóle nie działa bo jest błąd w kodzie czy to nie działa z innego powodu.
Na mój gust to można by było po protu spróbować, czy w ogóle najprostsze wysyłanie/odbieranie wiadomości ma miejsce. Ja bym w ogóle zaczął to budować od podstawowego połączenia i wysłania jednej wiadomości do serwa i odbiór od serwa. Coś w stylu:

klient się połączył:
klient ---->[asdf]----> serwer
klient <---[asdf]---- serwer

Sprawdź czy w ogóle działa to tak jak napisałem.
Mniej więcej coś takiego:


//[serwer]//
TcpListener l = new TcpListener(IPAddress.Any,12345);
l.Start();
TcpClient c = l.AcceptTcpClient()

byte[] buff = new byte[32];
c.GetStream().Read(buff,0,32);
string msg= Encoding.ASCII.GetString(buff);
//wyświetl wiadomość.
string reply = "simple reply to client";
byte[] r = Encoding.ASCII.GetBytes(reply);
c.GetStream().Write(r,0,r.Lenght); // czy coś takiego nie pamietam dokładnie.

//[Klient]//
TcpClient c = new Tcp(...);
c.Connect(...)
string msg = "asdf";
byte[] buff = Encoding.ASCII.GetBytes(msg);
c.GetStream().Write(buff,0,buff.Lenght);
//
byte[] receive = new byte[32];
c.GetStream().Read(receive);

//encoding i wyświetl

Sprawdź czy takie podstawowe wysłanie/odebranie działa w ogóle

0

Android client:

public class MainActivity : Activity
    {
        public TextView napis;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);
            Button button = FindViewById<Button>(Resource.Id.MyButton);
            button.Click += Start;
            napis = FindViewById<Button>(Resource.Id.MyButton);
        }

        private void Start(object sender, EventArgs e)
        {
            TcpClient c = new TcpClient();
            IPEndPoint IP_End = new IPEndPoint(IPAddress.Any, 13000);
            c.Connect(IP_End);
            //c.Connect(...)
            string msg = "asdf";
            byte[] buff = Encoding.ASCII.GetBytes(msg);
            c.GetStream().Write(buff, 0, buff.Length);

            byte[] receive = new byte[32];
            var otrzymane = c.GetStream().Read(receive, 0, receive.Length); //@@@@@@@@ ??
            napis.Text = otrzymane.ToString();
        }
    } 

Mam pewne wątpliwości co do: c.GetStream().Read(receive, 0, receive.Length); to pod jakąś zmienną podczepić czy bez?
Serwer zrobie w cmd c#.

0

serwer cmd:
Dałem Parse zamiast Any. Chodzi o to że mam skonfigurowany VirtualBox, dodatkowo xamarin nie działa na 127.0.0.1, tylko na WIFI 192.168.x.x.
Powoduje to problemy pozostanę przy parse.

static void Main(string[] args)
        {

            TcpListener l = new TcpListener(IPAddress.Parse("192.168.1.1"), 12345);
            l.Start();
            TcpClient c = l.AcceptTcpClient();
            //IPAddress.Parse("192.168.1.1")

            byte[] buff = new byte[32];
            c.GetStream().Read(buff, 0, 32);
            string msg = Encoding.ASCII.GetString(buff);
            Console.WriteLine("wiadomosc: " + msg);
            string reply = "simple reply to client";
            byte[] r = Encoding.ASCII.GetBytes(reply);
            c.GetStream().Write(r, 0, r.Length);
            System.Console.ReadKey();
        } 

screen z odpalenia:
https://scr.hu/2pdc/286m6

Wiadomosc przeszła ! Sukces : )))) WIELKIE DZIĘKI !
Teraz już kwestia rozbudowy tej małej cegiełki do budowy pałacu.

Jeżeli masz jeszcze jakieś sugestie, coś sprawdzić cos wstawić itp to proszę napisz z chęcią to przetestuję. Przepraszam jeżeli Musiałeś dłużej czasem czekać.
Xamarin wciąż bym nazwał aplkacją w fazie BETA, duzo jej brakuje jeszcze, a programuje w niej przed wczoraj i się czasem gubię. Najważniejsze dla mnie było zbudować most między dwoma platformami.

C.d. TcpClient słyszałem że na Xamarinie są tez z tym problemy. Tutaj widze jednak pięknie śmiga. Zdradzę że mój główny cel to zabawa komórką moim laptopem. By sobie kliknięciem odpalić muzykę z laptopa, albo go wyłączyć shutdown-s. Czy tez tam regulować film wyświetlany z laptopa bedąc w kanapie z komórką. ( takie tam marzenie)

Dziękuję bardzo za pomoc ! Dałeś nadzieje : )) i się udało.

0

Coś tak czułem, że z tymi całymi backgroundworkerami są jakieś problemy ale to trzeba by było debbugować a i tak kod jest trochę przekombinowany. Wiadomość dotarła to bardzo dobrze. Klika uwag na przyszłość:

  1. Uważaj bo pakiety nie dochodzą w całości, na localhost będzie ci wszystko śmigało bardzo dobrze ale później próbuj się łączyć przez sieć i czy wszystko działa.
  2. Jeżeli wysyłasz wiadomość (dla przykładu): "ABCD" to:

byte[] buffer = new byte[8];
int received = networkstream.Read(buffer);

w buforze w tym momencie pierwsze cztery bajty to twoje litery, pozostałe są wyzerowane (pamiętaj o tym). To, ile bajtów zostało odebranych przechowywane jest teraz w received (w tym wypadku będzie 4, Read zwraca ilość przeczytanych bajtów).

Musiałbyś także stworzyć jakieś zakończenie wiadomości (coś na końcu co określa jej koniec, jakiś nieużywany ciąg czy coś) ponieważ wysyłając dwie lub więcej mogą się nałożyć w końcowym buforze, może być pierwsza cała i kilka bajtów następnej itp.

NetworkStream posiada także coś takiego jak IsAvailable (chyba tak,zapomniałem dokładnie - poszukaj jak sprawdzić czy są dane do czytania dla NetworkStream) i tylko wtedy, gdy są takie dane czytaj z networkstream.

0

Hmmm dziękuję za wskazówkę. Wiem że wysyłam cały buffor. Poczytam o tym jak to wygląda by ładnie wszystko chodziło. Co do localhota/ wifi. Operowanie na localhost nie wchodzi rachubę jeżeli chce operować na dwóch maszynach. Będę liczyć się z błędami. Jednak wciąż myślę że jak stoję 5m od urządzenia (bo będę raczej prowadzić prace w obrębie swojego pokoju) to nie będzie większych zakłóceń.

Pozdrawiam Serdecznie !

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