2012-07-23 243 views
3

对于System.Net.WebRequest和System.Net.HttpRequest而言,在兼容多线程和套接字使用方面遇到了麻烦。我试图走低一级,滚动我自己的污垢简单的Http类。如何重新使用TCP客户端?

由于之前的问题是每个线程太快创建太多的套接字,我试图在多次迭代(for循环)上使用一个套接字(每个线程1个)。

代码:

我的测试类(已硬编码IP和端口,直到我能得到它的工作):

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

静态无效的主要:

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

错误

在for循环的第一次迭代成功完成后,我收到的错误为The operation is not allowed on non-connected sockets.

我了解错误的原因,(在响应被读取后TcpClient.Client关闭),但我不知道如何明确地告诉套接字保持打开状态。

编辑 HTTP响应的进一步检查我从它在它Connection: Close回服务器。我假设,因为这是原始的TCP它不会解析HTTP。这可能是问题的根源吗? (如果是的话,有没有办法忽略它)

回答

2

改变你主要方法的顺序,所以您将创建一个新的对象每次迭代

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

如果你要保持你的套接字打开,你需要重构你的应用程序一点点,所以它不会一个迭代后打电话Dispose,例如

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 
} 

这里是使用方法

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

我读的问题,他不_n想要创建多个实例。 – 2012-07-23 17:40:48

+0

虽然这将在同步程序中起作用,但当我尝试在多线程环境中使用它时,它不会帮助我使用套接字消耗。 (由操作系统清理的插槽与线程迭代不匹配,因此单个线程将创建多个插槽,从而快速占用资源) – James 2012-07-23 17:41:45

+0

如果您要这么做,Dispose'应该可能调用Close。喜欢这个。尽管如此,我没有看到一个令人信服的理由,不会将'close'重命名为'Dispose'。对称与'打开',也许...但是'关闭'实际上只是处理,所以... – cHao 2012-07-23 17:49:40

0

我认为您的KeepAlive实现工作不正确。当您关闭流时,您正在关闭底层套接字。将您的流创建移动到构造函数。

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

然后保持两者的生命,直到整个对象被关闭:

void IDisposable.Dispose() 
    { 
     m_tcpStream.Close(); 
     m_tcpClient.Client.Dispose(); 
    } 
+0

这导致错误'流不可写'System.IO异常,并且该根目录是TcpClient仍然关闭 – James 2012-07-23 17:57:09

+0

我想知道是否它期望一系列特定的事件以使连接可​​读写?编辑:是否有你必须实现iDisposable的原因?为什么不尝试删除它,看看它是否更具可预测性? – 2012-07-23 19:25:55

0

当调用第一Executeusing关闭(处置)m_tcpClient。你不需要那些using s

using(var stream = m_tcpClient.GetStream())