Home > Blockchain >  TcpClient Singleton don't sending/receiving data after changing activity in Xamarin.Android App
TcpClient Singleton don't sending/receiving data after changing activity in Xamarin.Android App

Time:12-22

I'm developing Xamarin.Android app with multiple activities. I'm having trouble maintaining a tcp connection in a singleton.

public sealed class TcpService
    {
        public TcpClient _tcpClient;
        public NetworkStream _networkStream;
        public string _recievedMessege;
        public Xamarin.Essentials.NetworkAccess networkAccess;
        public bool _isAdmin;
        private TcpService()
        {
            networkAccess = Connectivity.NetworkAccess;
            _tcpClient = new TcpClient();
            _tcpClient.Connect(IPAddress.Parse("10.0.2.2"), 100);
            _networkStream = _tcpClient.GetStream();
            Thread tcpReaderThread = new Thread(ReceiveTcpCommand);
            tcpReaderThread.Start();
        }
        private static TcpService _instance;
        private static readonly object InstanceLock = new object();
        public static TcpService GetInstance()
        {
            if (_instance == null)
            {
                lock (InstanceLock)
                {
                    if (_instance == null)
                        _instance = new TcpService();
                }
            }
            return _instance;
        }

        public void ReceiveTcpCommand()
        {
            byte[] buffer = new byte[0];
            byte[] tempBuffer = new byte[2];
            int packageLength = 0;
            while (true)
            {
                if (_tcpClient.Connected)
                {
                    try
                    {
                        if (packageLength > 0)
                        {
                            _networkStream.Read(buffer, 0, packageLength);
                            packageLength = 0;
                            string recieved = Encoding.ASCII.GetString(buffer);
                            buffer = new byte[1];
                            _recievedMessege = recieved;
                        }
                        else
                        {
                            _networkStream.Read(tempBuffer, 0, tempBuffer.Length);
                            packageLength = BitConverter.ToInt16(tempBuffer);
                            tempBuffer = new byte[2];
                            buffer = new byte[packageLength];
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        tempBuffer = new byte[2];
                        buffer = new byte[1];
                    }
                }
            }
        }

        public void SendTcpCommand(string command)
        {
            if (_tcpClient.Connected && !string.IsNullOrEmpty(command))
            {
                try
                {
                    byte[] tempBytes = Encoding.ASCII.GetBytes(command);
                    byte[] length = BitConverter.GetBytes((Int16)tempBytes.Length);
                    byte[] sendBytes = new byte[length.Length   tempBytes.Length];
                    sendBytes[0] = length[0];
                    sendBytes[1] = length[1];
                    for (int i = 2, j = 0; i < sendBytes.Length; i  )
                    {
                        sendBytes[i] = tempBytes[j  ];
                    }
                    _networkStream.Write(sendBytes, 0, sendBytes.Length);
                    _networkStream.Flush();
                }
                catch (Exception ex)
                {

                }
            };
        }



        public void KillTcpConnection()
        {
            SendTcpCommand(string.Format("Disconnect\n"));
            _networkStream.Close();
            _tcpClient.Close();
        }
    }

In the first activity in OnCreate, I create a singleton instance responsible for creating a connection to the server.

TcpService _tcpService;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.LoginLayout);
            // Create your application here

            //...

            _tcpService = TcpService.GetInstance();
            if (_tcpService.networkAccess == NetworkAccess.Internet
                || _tcpService.networkAccess == NetworkAccess.ConstrainedInternet
                || _tcpService.networkAccess == NetworkAccess.Local)
            {
                Thread tcpCallbackThread = new Thread(TcpCallback);
                tcpCallbackThread.Start();
             }
        }

I decided that the best way to handle incoming data would be to place it in a variable inside a singleton, leaving handling for each activity in the form of a thread using the TcpCallback method.

 public void TcpCallback()
        {
            string tempAdminBool=string.Empty;
            while (true)
            {
                if (_tcpService._recievedMessege != null)
                {
                    if (RegexMatcher.IsRegexMatch(_tcpService._recievedMessege, ClientRegexPattern.LoginAccepted))
                    {
                        if(RegexMatcher.RegexMatch(_tcpService._recievedMessege, ClientRegexPattern.LoginAccepted, 1).Equals(1))
                            _tcpService._isAdmin= true;
                        else
                            _tcpService._isAdmin= false;

                        Intent intent = new Intent(this, typeof(MenuActivity));
                        StartActivity(intent);
                    }
                    else if (_tcpService._recievedMessege.Contains("TryToLogin:Denied"))
                    {

                    }
                    else if (RegexMatcher.IsRegexMatch(_tcpService._recievedMessege, TCPServerCommand.InvalidCommand))
                    {
                        Button button = FindViewById<Button>(Resource.Id.button1);
                        button.Text = button.Text.Equals("dupa") ? "0" : "dupa"; 
//dupa is polish word for testing purposes
                    }
                    _tcpService._recievedMessege = null;
                }
            }
        }

When the application is in the first activity, there is no problem with communication, but when I go to the second activity, the client can neither send nor receive data. Although the application correctly uploads data to the stream, they are not sent and I do not receive any error information.

As for the server, it is the simplest multi-threaded program. The server uses TcpListener and when a new connection is detected, it creates a new thread to handle the communication with the client.

public TCPServerController()
        {
            Console.WriteLine("Server is ready...");
            _serverSocket = new TcpListener(_liseningPort);
            _tcpClient = default(TcpClient);
            _serverSocket.Start();
        }

        public void SetupServer()
        {
            int clientCounter = 0;
            while (true)
            {
                if (clientCounter == int.MaxValue)
                    clientCounter = 0;
                clientCounter  ;
                _tcpClient = _serverSocket.AcceptTcpClient();
                Console.WriteLine(string.Format("Client No. {0} Connected", clientCounter));
                new HandleClient(_tcpClient, clientCounter);
            }
        }
    }

    public class HandleClient
    {
        TcpClient _client;
        int? _authorizedUserId;
        bool _isUserAdmin = false;
        int _serverClientNumber;
        NetworkStream _networkStream;

        public HandleClient(TcpClient client, int clientNumber)
        {
            _client = client;
            _serverClientNumber = clientNumber;
            Thread clientThread = new Thread(ClientThread);
            clientThread.Start();
        }

        private void ClientThread()
        {
            byte[] bytesFrom = new byte[1024];
            bool connected = true;
            if (_client.Connected)
                _networkStream = _client.GetStream();
            while (connected)
            {
                try
                {
                    if (_client.Connected)
                    {

                        _networkStream.Read(bytesFrom, 0, bytesFrom.Length);
                        string message = Encoding.ASCII.GetString(bytesFrom);
                        _networkStream.Flush();
                        if (!string.IsNullOrEmpty(message))
                            SendResponse(DecodeCommand(message));
                    }
                    else
                    {
                        _authorizedUserId = null;
                        _isUserAdmin = false;
                        connected = false;
                    }
                }
                catch (IOException ex)
                {
                    Console.WriteLine(string.Format("Client No. {0}: The connection was aborted locally!", _serverClientNumber));
                    connected = false;
                    _networkStream.Close();
                    _client.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
                finally
                {
                    bytesFrom = Array.Empty<byte>();
                }
            }
        }

Has anyone an idea what could be wrong?

CodePudding user response:

OK, I find the solution: In server-side ClientThread method in finally I should use Array.Clear(bytesFrom, 0, bytesFrom.Length); instead of bytesFrom = Array.Empty<byte>();.

  • Related