Socket łączący się sam ze sobą

0

Witam i z góry dziękuję za jakąkolwiek radę w następującej kwestii: Mam następującą klasę (kod poniżej), problem polega na tym, że jak używam jej do łączenia się z samą sobą przez TcpListener'a i odpalając 2 naraz to nieraz przechodzą wszystkie wiadomości, nieraz co piąta lub co dziesiąta. Sytuacja raczej nie ma miejsca gdy łącze się z serwerem zewnętrznym tzn. wysyłanie i odbieranie jest ok. Proszę o radę.

 
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace WindowsFormsApplication3
{

    class NetworkConnector
    {
        public event NormalDelegate DataReceived;
        public event NormalDelegate ConnectionClosed;
        public event NormalDelegate ConnectionStarted;
        private NetworkStream networkStream;
        Thread sendThread;
        private string ip;
        private int port;
        private string data;
        private bool abortListener = false;
        TcpClient tcpClient = null;

        public void CloseConnection(object sender, EventArgs e)
        {
            if(ConnectionClosed !=null)
                ConnectionClosed("NC: event stop", new EventArgs());
            abortListener = true;
        }

        public void Send(object sender, EventArgs e)
        {
            this.data = sender as string;
            sendThread = new Thread(SendThread);
            sendThread.Start();
        }

        public void SendXmlError(object sender, EventArgs e)
        {
            this.data = "<error msg='" + sender as string + "' />";
            sendThread = new Thread(SendThread);
            sendThread.Start();
        }
        

        private void SendThread()
        {
            try
            {
                Byte[] message = System.Text.Encoding.ASCII.GetBytes(data);
                networkStream.Write(message, 0, message.Length);
            }
            catch (Exception x)
            {
                if (ConnectionClosed != null)
                    ConnectionClosed(x.Message, new EventArgs());
            }
        }
        

        public NetworkConnector(string ip, int port)
        {
            this.ip = ip;
            this.port = port;
            Thread thread = new Thread(Connect);
            thread.Start();
        }

        public NetworkConnector(TcpClient tcpClient)
        {
            this.tcpClient = tcpClient;
            Thread thread = new Thread(Connect);
            thread.Start();
        }

        private bool SocketConnected()
        {
            if (tcpClient.Client.Poll(0, SelectMode.SelectRead))
            {
                byte[] buff = new byte[1];
                if (tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
                {
                    return false;
                }
            }
            return true;
        }

        private void Connect()
        {
            try
            {
                if (tcpClient == null)
                    tcpClient = new TcpClient(ip, System.Convert.ToInt32(port));
                EventArgs eventsArgs = new EventArgs();
                networkStream = tcpClient.GetStream();
                byte[] clientReadBuffer = new byte[1024];
                int readIndex;
                string receivedMessage;
                EventArgs eventArgs = new EventArgs();
                if (ConnectionStarted != null)
                    ConnectionStarted("Connection started", eventArgs);
                while (!abortListener && SocketConnected())
                {
                    receivedMessage = "";
                    if (networkStream.DataAvailable)
                    {
                        do
                        {
                            readIndex = networkStream.Read(clientReadBuffer, 0, clientReadBuffer.Length);
                            receivedMessage += Encoding.ASCII.GetString(clientReadBuffer, 0, readIndex);
                        } while (networkStream.DataAvailable);
                        if (DataReceived != null)
                            DataReceived(receivedMessage, eventArgs);
                    }
                    Thread.Sleep(500);
                }
            }
            catch (Exception e)
            {
                if(ConnectionClosed != null)
                    ConnectionClosed("NC: " + e.Message, new EventArgs());
            }
            if (sendThread != null)
                sendThread.Join();
            if (tcpClient != null)
                tcpClient.Client.Disconnect(false);
            if (ConnectionClosed != null)
                ConnectionClosed("socket stopped, manually: " + abortListener, new EventArgs());
        }
    }
}

0

po co łączyć socketa z samym sobą?

0

Nie z samym sobą tylko z drugim takim samym jak on, a po to żeby spełniał funkcję nadawczo - odbiorczą w programie np p2p (dwa takie same obiekty rozmawiające ze sobą) więc powinien w celach testowo-rozwojowych być w stanie bezbłędnie się sam ze sobą komunikować (program nie socket) a tak nie jest ;).

0

Może pokaż jak używasz tego TcpListenera, inna sprawa, po co bawisz się Socketami jak masz technologie jak WCF.
Ale jak już chcesz to kolejna sprawa, dlaczego wciskasz tam Thread.Sleep'a? Jak będziesz operował na Sockecie i metodzie Receive, sama ona będzie blokować wątek.
Czemu metoda wysyłająca pojedyncza wiadomość jest odpalana na osobnym wątku? Dziwnie to jakoś tak wygląda.

Przetestowałem i łączyłem się na tej samej maszynie uzywajac TcpListenera i wydawało się być ok.

0

Tak zrobilem (send na osobnym watku) zeby sie program nie wieszal w przypadku gdy polaczenie jest zerwane a zostanie nacisniety przycisk na formularzu, a Thread.Sleep jest tam bo

if (networkStream.DataAvailable)

omija receive zeby nie bylo zwisu przy zerwanym polaczeniu, jakbym nie dal sleep'a to petla by wykonywala sie 1000 razy na sekude (albo tak mi sie tylko blednie wydaje ;) ekspertem nie jestem)

0

Zrobiłeś to jakoś odwrotnie.
Robisz sobie kolejke wiadomości. (tam dodajesz jakieś wiadomości do wysłania (synchronizacja), jakiś lock)
Odpalasz wątek wysyłający, w nim metodę wysyłającą jak jest coś w kolejce (np BlockingQueue czy jak to cudo się zwało, ewentualnie możesz sterować to WaitHandle i ResetEvents, ale to juz zabawa na dosyć "niskim" poziomie)
Czyli wygląda to tak że jak coś trafi do kolejki, to wątek to wysyła i potem zasypia.

robisz drugi wątek - (pobierający) używając metody Receive na sockecie, jak metoda się wykona możesz odpalać jakieś zdarzenie o nadejściu nowych danych.
Tylko musisz mieć na uwadzę że zdarzenie takowe nadchodzi z innego wątku niż ten wątek UI i jeśli zechcesz bezpośrednio to wywołanie wykorzystać do uaktualnienia GUI, to potrzeba będzie zrobić Invoke. (szukaj delegate, invoke)

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