TCP - подключение нескольких клиентов к серверу - PullRequest
0 голосов
/ 21 апреля 2019

Я все еще учусь и потратил приличное количество времени, пробуя разные вещи и ища, но я все еще не могу найти ответ на свою проблему.Если кто-то захочет показать какой-нибудь код или сказать мне как, это будет чрезвычайно полезно.Если нет способа, обходные пути также могут быть полезны.Все что угодно.

По сути, мне нужно подключить несколько клиентов к серверу, прослушивать получение сериализованного класса на обоих концах и иметь возможность отправлять один и тот же класс (сериализованный, в ответ и набудет (с обоих концов) к другому.

Для клиентского приложения: мне нужно иметь возможность прослушивать хост на определенном порту (и получать сериализованный класс, затем непрерывно десериализовать его),а также отправлять одно как по желанию, так и в ответ.

Для серверного приложения: мне нужно иметь возможность прослушивать любые входящие соединения (любой IP) на этом же порту, а затем изолировать соединение какодиночное соединение в другом потоке, прослушивание сериализованного класса в нем (непрерывно), а также возможность отправки одного (снова, по желанию и в ответ).

Почему один порт для всего этого ?: МойИнтернет-провайдер не позволит мне открыть более 1 порта.

Вот мой текущий работающий код:

// the serializable class (currently)
[ System.Serializable ] public class Packet {
    public System.String          str   { get; set; }
    public System.Drawing.Image   img   { get; set; }
}

// to deserialize the class
Packet pkt = ( Packet ) ( new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter() ).Deserialize( network_stream );

// to serialize and send the class
( new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter() ).Serialize( network_stream, pkt );

(я знаю, что это не так много, но сейчас яве мнерасскажите обо всем остальном до такой степени, что, вероятно, бесполезно публиковать то, что я в последний раз пробовал для клиента / сервера.Кроме того, если мне нужно будет предоставить что-то еще, дайте мне знать, я новичок во всем этом, заранее извиняюсь.)

РЕДАКТИРОВАТЬ: я сделал другой проект, немного более чистый, безНикакой сериализации пока нет, и это все еще не работает.Что я здесь не так делаю?(Я просматривал это несколько раз, и я знаю, что что-то упустил, я просто не знаю, где и что.)

Вот полный источник:

namespace TCP {
    public partial class Main_Form : System.Windows.Forms.Form {
        private System.Net.IPAddress Sea = System.Net.IPAddress.Any; // Listening address
        private System.Net.IPAddress Crew = System.Net.IPAddress.Parse( "127.0.0.1" ); // Host address
        private System.UInt16 Port = 888;
        private Ship Ship = null; // TcpListener
        private Crewmate Pirate = null; // TcpClient
        public Main_Form() {
            this.InitializeComponent();
        }
        private void btnServer_Click( System.Object sender, System.EventArgs e ) {
            if( this.Ship == null ) { // only create 1 instance for testing purposes
                this.Ship = new Ship { addr = this.Sea, port = this.Port, form = this }; // Connect TcpListener to 0.0.0.0:888 (Listening address & Port), and bind (form = this) for external use
            }
        }
        private void btnClient_Click( System.Object sender, System.EventArgs e ) {
            if( this.Pirate == null ) { // only create 1 instance for testing purposes
                this.Pirate = new Crewmate { addr = this.Crew, port = this.Port }; // Connect TcpClient to 127.0.0.1:888 (Host address & Port)
            }
        }
        private void txtSend_KeyDown( System.Object sender, System.Windows.Forms.KeyEventArgs e ) {
            if( e.KeyCode == System.Windows.Forms.Keys.Enter ) {
                e.Handled = true;
                var TextMessage = this.txtSend.Text;
                this.txtSend.Clear();
                this.Ship.Post( TextMessage );
            }
        }
        public System.String GetCurrentCrewmate() { // for external use
            if( this.cpuList.SelectedItems.Count != 1 ) {
                return( System.String.Empty );
            }
            return( this.cpuList.SelectedItems[ 0 ].ToString() );
        }
        public void EnlistCrewmate( System.String key ) { // also for external use
            this.cpuList.Items.Add( key );
            this.cpuList.SelectedIndex = this.cpuList.Items.Count - 1;
        }
    }
    public static class Thread {
        public static void Start( System.Action func ) {
            System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback( delegate( System.Object state ) { func(); } ) );
        }
    }
    public static class Debug {
        public static void Write( System.String line ) {
            System.Console.WriteLine( line );
        }
        public static void WriteBlock( System.String prefix, System.String suffix, System.String block ) {
            Debug.Write( prefix );
            Debug.Write( block );
            Debug.Write( suffix );
        }
    }
    public class Crewmate {
        public System.String serial;
        public System.Net.IPAddress addr;
        public System.UInt16 port;
        private System.Net.Sockets.TcpClient Pirate;
        public Crewmate() {
            this.Pirate = new System.Net.Sockets.TcpClient(); // create TcpClient on init
            Debug.Write( "[Crewmate] Pirate connecting to Ship..." );
            Thread.Start( this.Listen ); // start listening
        }
        private void Listen() {
            while( true ) { // loop to stay connected as much as possible
                while( !this.Pirate.Client.Connected ) { // while not connected, try to connect to host
                    try {
                        this.Pirate.Client.Connect( addr, port );
                        Debug.Write( "[Crewmate] Pirate connected to Ship!" );
                    }
                    catch {
                        Debug.Write( "[Crewmate] Pirate is having some connection issues..." );
                    }
                }
                while( this.Pirate.Client.Connected ) { // while connected to host, try to read data
                    var NetworkStream = this.Pirate.GetStream();
                    var Reader = new System.IO.StreamReader( NetworkStream );
                    while( true ) { // loop reading data (on Can Read & Data Abailable)
                        if( NetworkStream.CanRead && NetworkStream.DataAvailable ) {
                            Debug.Write( "[Crewmate] Data is available to read, proceeding..." );
                            try {
                                Debug.Write( "[Crewmate] Ship->Crew: \"" + Reader.ReadToEnd() + "\"" );
                            }
                            catch( System.Exception ex ) {
                                Debug.Write( "[Crewmate] Couldn't read data for some reason... Hmm..." );
                                Debug.Write( "[Message]" + ex.Message + "[/Message]" );
                                Debug.WriteBlock( "[StackTrace]", "[/StackTrace]", ex.StackTrace );
                            }
                        }
                    }
                }
                Debug.Write( "[Crewmate] Pirate lost connection to Ship." ); // when connection lost to host
            }
        }
        public void Post( System.String String ) {
            Debug.Write( "[Crewmate] Attempting to send message to Ship..." );
            // Send to Ship {
            var Writer = new System.IO.StreamWriter( this.Pirate.GetStream() );
            Writer.Write( String );
            Writer.Flush();
            // }
            Debug.Write( "[Crewmate] Message sent to Ship!" );
        }
    }
    public class Ship {
        public System.String serial;
        public System.Net.IPAddress addr;
        public System.UInt16 port;
        private System.Collections.Generic.Dictionary< System.String, System.Net.Sockets.TcpClient > Crew =
            new System.Collections.Generic.Dictionary< System.String, System.Net.Sockets.TcpClient >();
        public Main_Form form;
        public Ship() {
            Debug.Write( "[Ship] Ship starting..." );
            Thread.Start( this.Listen ); // start listening
        }
        private void Listen() {
            while( true ) { // loop to stay connected as much as possible
                try {
                    var Listener = new System.Net.Sockets.TcpListener( addr, port ); // try to create TcpListener
                    Listener.Start(); // try to start listening
                    Debug.Write( "[Ship] Ship is now running!" );
                    while( true ) { // loop while listening
                        var Pirate = Listener.AcceptTcpClient(); // accept TcpClient
                        Debug.Write( "[Ship] Pirate accepted to Crew." );
                        Thread.Start( delegate { this.HandleCrewmate( Pirate ); } ); // thread TcpClient via HandleCrewmate for further individual handling
                        Debug.Write( "[Ship] Pirate being sent onboard..." );
                    }
                }
                catch {
                    Debug.Write( "[Ship] Ship is currently refusing to listen on " + this.addr.ToString() + ":" + this.port ); // when TcpListener refuses to bind to addr:port
                }
            }
        }
        public System.Net.Sockets.TcpClient GetCrewmate( System.String key ) { // get TcpClient from Dictionary by key
            System.Net.Sockets.TcpClient result;
            var exists = this.Crew.TryGetValue( key, out result );
            if( exists ) {
                return( result );
            }
            return( null );
        }
        public System.Boolean IsCrewmateListed( System.String key ) { // test if TcpClient exixts by key
            return( this.GetCrewmate( key ) != null );
        }
        private void PutCrewmate( System.String key, System.Net.Sockets.TcpClient value ) { // for later use, to update existing clients if they've only lost connection, this is where class (de/)serialization will come into play
            if( this.IsCrewmateListed( key ) ) {
                this.Crew[ key ] = value;
            }
            else {
                this.Crew.Add( key, value );
            }
        }
        private void LostCrewmate( System.String key ) { // when we lost connection to a TcpClient, remove them from the list (this will also be handled later on)
            if( this.IsCrewmateListed( key ) ) {
                this.Crew.Remove( key );
            }
        }
        private void HandleCrewmate( System.Net.Sockets.TcpClient Pirate ) { // for handling TcpClients individually
            Thread.Start( delegate {
                // Enlist Pirate for the Ship to see (add TcpClient to ListBox with Dictionary Key as a name)... {
                var key = "Pirate #1";
                this.PutCrewmate( key, Pirate );
                this.EnlistCrewmate( key );
                // }
                var NetworkStream = Pirate.GetStream();
                var Reader = new System.IO.StreamReader( NetworkStream );
                while( Pirate.Connected ) { // while connected to client
                    if( NetworkStream.CanRead && NetworkStream.DataAvailable ) { // try to read data (on Can Read & Data Abailable)
                        Debug.Write( "[Ship] Data is available to read, proceeding..." );
                        try {
                            Debug.Write( "[Ship] Crew->Ship: \"" + Reader.ReadToEnd() + "\"" );
                        }
                        catch( System.Exception ex ) {
                            Debug.Write( "[Ship] Couldn't read data for some reason... Hmm..." );
                            Debug.Write( "[Message]" + ex.Message + "[/Message]" );
                            Debug.WriteBlock( "[StackTrace]", "[/StackTrace]", ex.StackTrace );
                        }
                    }
                }
                Debug.Write( "[Ship] Lost connection to Pirate..." ); // when connection lost to client (this is also where I will handle the LostCrewmate action eventually)
            } );
        }
        public void Post( System.String String ) {
            var bytes = System.Text.Encoding.ASCII.GetBytes( String );
            Debug.Write( "[Ship] Attempting to send message to selected Pirate..." );
            // Send to selected Pirate only (send data to TcpClient by selected Dictionary key in ListBox)... {
            var key = this.form.GetCurrentCrewmate();
            if( key == System.String.Empty ) return;
            var Pirate = this.GetCrewmate( key );
            if( !Pirate.Connected ) return;
            Debug.Write( "[Ship] Sending message to Pirate..." );
            var Writer = new System.IO.StreamWriter( Pirate.GetStream() );
            Writer.Write( String );
            Writer.Flush();
            // }
            Debug.Write( "[Ship] Message sent to Pirate!" );
        }
        // because invoking is required, otherwise it picks up as an unsafe thread call...
        private delegate void EnlistCrewmateEventHandler( System.String key );
        private void EnlistCrewmate( System.String key ) {
            this.form.Invoke( new EnlistCrewmateEventHandler( this.form.EnlistCrewmate ), key );
        }
    }
}

Что-то не так с процессом чтения, остальное похоже на проверку.Он запускается сразу после «CanRead && DataAvaialable», затем он не отображает содержимое и даже не записывает в консоль, если я что-то добавлю после этой точки, но ни одно исключение не перехватывается, очевидно, потому что он не будет отображать вывод «catch».

Проблема в основных программах: он говорит, что TcpClient.GetStream () имеет значение null.

Примечание: "cpuList" - это ListBox для словарных ключей (в виде строки), "btnServer«должен запустить TcpListener,« btnClient »- запустить TcpClient, а« txtSend »- это то, где я вводю текст для отправки выбранному TcpClient в ListBox.

...