TCP-将多个客户端连接到服务器

时间:2019-04-21 14:23:05

标签: c# sockets tcp tcpclient tcplistener

我仍在学习,并且花了很多时间尝试不同的事物并四处搜寻,但是我仍然找不到解决我问题的答案。如果有人愿意显示一些代码或告诉我怎么做,那将非常有帮助。如果没有办法,解决方法也可能会有所帮助。什么都没有。

基本上,我需要将多个客户端连接到服务器,侦听两端都接收到序列化的类,并能够将相同的类(在两端响应并随意序列化)发送给另一个。

对于客户的应用程序:我需要能够侦听特定端口上的主机(并接收序列化的类,然后连续对其进行反序列化),并且还可以随意发送一个并作为响应。

对于服务器的应用程序:我需要能够侦听同一端口上的任何传入连接(任何IP),然后将该连接隔离为另一个线程上的单个连接,并继续侦听其上的序列化类。并能够再次发送(随意,随意和回复)。

为什么要全部使用一个端口?:我的ISP不允许我打开多个端口。

以下是我目前正在使用的一些代码:

// 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()为空。

注意:“ cpuList”是字典键(作为字符串)的列表框,“ btnServer”用于启动TcpListener,“ btnClient”用于启动TcpClient,“ txtSend”是我输入要发送到的文本的位置列表框中选择的TcpClient。

0 个答案:

没有答案