Search code examples
c#tcptcpclientsystem.net.sockets

How do I re-use TCP Client?


I've had troubles with System.Net.WebRequest and System.Net.HttpRequest in terms of compatibility with multi-threading and socket consumption. I've attempted to go a level lower and roll my own dirt simple Http classes.

Since the problem before was each thread was creating too many sockets too quickly, I am attempting to use a single socket (1 per thread) over multiple iterations ( a for loop).

Code:

My Test Class (has hardcoded ip and port until I can get it working):

public sealed class Foo : IDisposable {

        private string m_ip = "localhost";
        private int m_port = 52395;
        private TcpClient m_tcpClient;


        public Foo() {
            m_tcpClient = new TcpClient( m_ip, m_port );
        }

        public void Execute() {
            using( var stream = m_tcpClient.GetStream() )
            using( var writer = new StreamWriter( stream ) )
            using( var reader = new StreamReader( stream ) ) {
                writer.AutoFlush = true;
                // Send request headers
                writer.WriteLine( "GET /File HTTP/1.1" );
                writer.WriteLine( "Host: " + m_ip + ":" + m_port.ToString() );
                writer.WriteLine( "Connection: Keep-Alive" );
                writer.WriteLine();
                writer.WriteLine();

                // Read the response from server
                string response = reader.ReadToEnd();
                Console.WriteLine( response );
            }
        }    

        void IDisposable.Dispose() {
            m_tcpClient.Client.Dispose();
        }
    }

Static void Main:

using( Foo foo = new Foo() ) {
    for( int i = 0; i < 10; i++ ) {
        foo.Execute();
    }
}

Error

The error I am receiving is The operation is not allowed on non-connected sockets. after the first iteration of the for loop successfully completes.

I understand the cause of the error, (After the response is read the TcpClient.Client closes), but I don't know how to explicitly tell the socket to remain open.

Edit Further examination of the HTTP response I get back from the server it has Connection: Close in it. I assumed since this was raw TCP it wouldn't parse the HTTP. Could this be the root of the problem? (If so is there a way to ignore it)


Solution

  • Change the order in your main method, so you will create a new object each iteration

    for( int i = 0; i < 10; i++ ) 
    {
        using( Foo foo = new Foo() ) 
        {
            foo.Execute();
        }
    }
    

    If you want to keep your socket open, you need to refactor your application a little bit so it will not call Dispose after one iteration, for example

    public sealed class Foo : IDisposable {    
        private string m_ip = "localhost";
        private int m_port = 52395;
        private TcpClient m_tcpClient;
    
        private Stream stream;
        private StreamWriter writer;
        private StreamReader reader;
    
        public void Execute() {         
            // Send request headers
                ...    
            // Read the response from server                
        }   
    
        void Open(){
            m_tcpClient = new TcpClient( m_ip, m_port );
            stream = m_tcpClient.GetStream();
            writer = new StreamWriter( stream );
            reader = new StreamReader( stream );
        }   
    
        void Close() {
            m_tcpClient.Client.Dispose();
            reader.Dispose();
            writer.Dispose();
            stream.Dispose();
        }
    
        //Plus Dispose implementation
    }
    

    And here is the usage

    using( Foo foo = new Foo() ) {
        foo.Open();
        for( int i = 0; i < 10; i++ ) {
            foo.Execute();
        }
        foo.Close();
    }