0

I'm still learning, and have spent a decent amount of time trying different things and searching around, but I still can't find an answer to my problem. If someone would be willing to show some code or tell me how, it'd be extremely helpful. If there isn't a way, work-arounds could be helpful as well. Anything at all.

Basically, I need to connect multiple clients to a server, listen for the receiving of a serialized class on both ends, and be able to send the same class (serialized, in response and at-will, on both ends) to the other.

For the clients' application: I need to be able to listen to the host on a specific port (and receive the serialized class, then deserialize it, continuously), and also send one out both at-will and in response.

For the server's application: I need to be able to listen to any incoming connections (any IP) on that same port, then isolate the connection as a single connection on another thread, listening for the serialized class on it (continusously) and also be able to send one out (again, at-will and in response).

Why one port for it all?: My ISP won't allow me to open more than 1 port.

Here's some of my current code that is working:

// 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 );

(I know it's not much, but by now I've messed up everything else to the point there's probably no use in posting what I've last tried for the client/server. Also, if I need to provide anything else, let me know, I'm new to the whole posting thing, my apologies in advance.)

EDIT: I made another project, a little bit more clean, without any serialization yet, and this still does not work. What am I doing wrong here? (I have looked it over a few times now, and I know I'm missing something, I just don't know where and what.)

Here is the full source:

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 );
        }
    }
}

There is something wrong with the reading process, the rest seems to check-out. It runs just past "CanRead && DataAvaialable", then it doesn't display the content or even write to the console if I put anything after that point, but no exception is caught either apparently because it won't show the "catch" output.

The problem in the main programs: it says that TcpClient.GetStream() is null.

Note: "cpuList" is a ListBox for Dictionary keys (as a string), "btnServer" is to start the TcpListener, "btnClient" is to start the TcpClient, and "txtSend" is where I enter text to send to the selected TcpClient in the ListBox.

  • I'm also still learning about c#, but I have some experience using socket in other progamming languages like Perl and Python. If you want to have multiple client using same port to connect to the server, you can create a multi socket in the same port, that means you need to use multi threads to accept multiclients. And for the later question, remember that a socket is connection between two parts only, so you can't send a message to all clients, but you can save all sockets connected (between you and client) in array, and foreach this array to send to your message to every client individually. – Mobrine Hayde Apr 21 '19 at 14:57
  • You can find so usefull answers simillar to you question here https://stackoverflow.com/questions/14974404/socket-programming-multiple-client-one-server – Mobrine Hayde Apr 21 '19 at 15:03
  • This is more or less what I was already looking at, and I haven't messed around with sockets too much so I got a bit lost with other posts. While that one is a lot more clean and I can actually read it (also, I appreciate it), I had to make an edit on the issue I'm coming across. It did help quite a bit. If I could get some input on this issue that I added in, that'd be great. I know I'm missing something, I just don't know what and where. All help has been and is highly appreciated, thank you now and in advance. – Amélie Lacroix Apr 24 '19 at 06:03

0 Answers0