2012-03-14 27 views
9

Bản tóm tắt ngắn gọn về tình huống:Có thể phát hiện xem luồng đã được khách hàng đóng chưa?

Tôi có dịch vụ nhận thông tin và gửi trả lời qua Ổ cắm. Các kết nối không được bảo mật. Tôi muốn thiết lập một dịch vụ khác có thể cung cấp TLS cho các kết nối này - dịch vụ mới này sẽ cung cấp một cổng đơn và phân phối các kết nối dựa trên chứng chỉ ứng dụng khách đã cung cấp. Tôi không muốn sử dụng stunnel cho một vài lý do, một là nó sẽ yêu cầu một cổng chuyển tiếp cho mỗi cổng nhận.

Giải pháp Tôi hiện đang cố gắng để thực hiện:

Về cơ bản, tôi đang cố gắng để vợ chồng một SslStream (đến) với một NetworkStream (đi - có thể là một Socket, nhưng tôi đặt nó vào một NetworkStream để phù hợp với các đầu vào) và có các hoạt động đọc/ghi được liên kết cho cả hai. Liên kết này sẽ cung cấp luồng giữa khách hàng (qua SSL/TLS) và dịch vụ (qua kết nối không an toàn).

Dưới đây là lớp tôi đã đưa ra để liên kết các Luồng:

public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Finish reading data. 
     int length = state.InStream.EndRead(result); 

     // Write data. 
     state.OutStream.Write(state.Buffer, 0, length); 

     // Wait for new data. 
     state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

Vấn đề:

Khi khách hàng được thực hiện gửi thông tin và định đoạt đối với các SslStream, máy chủ không có bất kỳ loại dấu hiệu nào về việc điều này có xảy ra hay không. Lớp StreamConnector này vui vẻ tiếp tục chạy vào vĩnh cửu mà không ném bất kỳ loại lỗi nào, và tôi không thể tìm thấy bất kỳ chỉ báo nào mà nó sẽ dừng lại. (Có, tất nhiên, thực tế là tôi nhận được 0 chiều dài mỗi lần trong ReadCallback, nhưng tôi cần để có thể cung cấp các kết nối dài, vì vậy đây không phải là một cách tốt để đánh giá.)

vấn đề là ReadCallback được gọi ngay cả khi không có sẵn dữ liệu. Không chắc chắn nếu điều đó sẽ khác nếu tôi đang sử dụng một Socket trực tiếp thay vì một dòng, nhưng có vẻ như không hiệu quả để tiếp tục chạy mã đó hơn và hơn nữa.

Câu hỏi của tôi:

1) Có cách nào để biết một suối đã bị đóng cửa từ phía khách hàng?

2) Có cách nào tốt hơn để làm những gì tôi đang cố gắng làm không?

2a) Có cách nào hiệu quả hơn để chạy vòng lặp đọc/ghi không đồng bộ không?

EDIT: Cảm ơn, Robert. Hóa ra vòng lặp tiếp tục được gọi vì tôi không đóng Luồng (do không biết làm thế nào để biết khi nào Luồng cần phải đóng). Tôi bao gồm giải pháp mã đầy đủ trong trường hợp người khác gặp sự cố này:

/// <summary> 
/// Connects the read/write operations of two provided streams 
/// so long as both of the streams remain open. 
/// Disposes of both streams when either of them disconnect. 
/// </summary> 
public class StreamConnector 
{ 
    public StreamConnector(Stream s1, Stream s2) 
    { 
     StreamConnectorState state1 = new StreamConnectorState(s1, s2); 
     StreamConnectorState state2 = new StreamConnectorState(s2, s1); 
     s1.BeginRead(state1.Buffer, 0, state1.Buffer.Length, new AsyncCallback(ReadCallback), state1); 
     s2.BeginRead(state2.Buffer, 0, state2.Buffer.Length, new AsyncCallback(ReadCallback), state2); 
    } 

    private void ReadCallback(IAsyncResult result) 
    { 
     // Get state object. 
     StreamConnectorState state = (StreamConnectorState)result.AsyncState; 

     // Check to make sure Streams are still connected before processing. 
     if (state.InStream.IsConnected() && state.OutStream.IsConnected()) 
     { 
      // Finish reading data. 
      int length = state.InStream.EndRead(result); 

      // Write data. 
      state.OutStream.Write(state.Buffer, 0, length); 

      // Wait for new data. 
      state.InStream.BeginRead(state.Buffer, 0, state.Buffer.Length, new AsyncCallback(ReadCallback), state); 
     } 
     else 
     { 
      // Dispose of both streams if either of them is no longer connected. 
      state.InStream.Dispose(); 
      state.OutStream.Dispose(); 
     } 
    } 
} 

public class StreamConnectorState 
{ 
    private const int BYTE_ARRAY_SIZE = 4096; 

    public byte[] Buffer { get; set; } 
    public Stream InStream { get; set; } 
    public Stream OutStream { get; set; } 

    public StreamConnectorState(Stream inStream, Stream outStream) 
    { 
     Buffer = new byte[BYTE_ARRAY_SIZE]; 
     InStream = inStream; 
     OutStream = outStream; 
    } 
} 

public static class StreamExtensions 
{ 
    private static readonly byte[] POLLING_BYTE_ARRAY = new byte[0]; 

    public static bool IsConnected(this Stream stream) 
    { 
     try 
     { 
      // Twice because the first time will return without issue but 
      // cause the Stream to become closed (if the Stream is actually 
      // closed.) 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      stream.Write(POLLING_BYTE_ARRAY, 0, POLLING_BYTE_ARRAY.Length); 
      return true; 
     } 
     catch (ObjectDisposedException) 
     { 
      // Since we're disposing of both Streams at the same time, one 
      // of the streams will be checked after it is disposed. 
      return false; 
     } 
     catch (IOException) 
     { 
      // This will be thrown on the second stream.Write when the Stream 
      // is closed on the client side. 
      return false; 
     } 
    } 
} 
+0

Vui lòng không đặt tiền tố cho tiêu đề bằng 'C#', đó là những thẻ dành cho :) – kprobst

+0

Xin lỗi. :) Thấy đề cập đến ngôn ngữ trong một số chức danh khác trong khi tôi đang nghiên cứu; nghĩ rằng nó sẽ hữu ích. Sẽ dính vào các thẻ từ đây. – zimdanen

Trả lời

3

Bạn phải cố gắng đọc hoặc ghi vào ổ cắm - hoặc bất kỳ thứ gì dựa trên nó - để phát hiện ngắt kết nối.

Cố gắng viết sẽ ném một ngoại lệ/trả về lỗi (tùy thuộc vào kiểu ngôn ngữ của bạn) hoặc có thể chỉ viết 0 byte. Cố gắng đọc hoặc là sẽ ném một ngoại lệ/trả lại một lỗi (một lần nữa tùy thuộc vào mô hình ngôn ngữ của bạn) hoặc trả lại null.Cần lưu ý rằng nếu bạn đang sử dụng một mô hình máy chủ dựa trên lựa chọn, ổ cắm bị ngắt kết nối hiển thị - tức là trả về lựa chọn - như có thể đọc được khi ngắt kết nối, sau đó bạn cố gắng đọc nó và nhận lỗi hoặc null.

+0

Vì vậy, tôi cần phải cố gắng ghi vào từng luồng (điều này có thể dẫn đến dữ liệu xấu trên luồng nếu nó không bị đóng)? – zimdanen

+0

Đoán, nếu tôi viết 0 byte, thì đó không phải là vấn đề. – zimdanen

+2

Tôi có thể thêm điều đó theo [** Nito **] (http://nitoprograms.blogspot.co.il/2009/05/detection-of-half-open-dropped.html) cách duy nhất để phát hiện ra sự sụt giảm kết nối là ghi vào luồng. –

0

Tôi không thể không nghĩ rằng khách hàng của bạn nên nói với máy chủ khi nó được thực hiện với một số loại tin nhắn. Nó luôn luôn là tốt nhất để được chuẩn bị cho một dây bị cắt hoặc mất điện hoặc một plug được kéo, nhưng nói chung bạn muốn chấm dứt một kết nối với một số loại end-of-message marker. Lưu các ngoại lệ cho các vấn đề thực sự, không phải cho các cuộc trò chuyện bình thường.

+0

Khi xử lý SslStream trên máy khách (sở hữu NetworkStream, sở hữu Socket), không có gì được gửi đến máy chủ để cho nó biết rằng nó bị ngắt kết nối. Ít nhất, không có gì đóng socket/stream trên máy chủ. – zimdanen

+0

@zimdanen: Tôi thấy đó là vấn đề thiết kế (lớn) với SslStream. Không cần phải làm gì ngoài những gì bạn đã làm nếu SslStream vượt quá quyền hạn của bạn. Chúng ta phải làm cho phần mềm của chúng ta làm việc với thế giới vì nó thay vì nó phải như vậy. (Mặc dù vậy, tôi không thể chống lại việc đó.) – RalphChapin

Các vấn đề liên quan