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;
}
}
}
Vui lòng không đặt tiền tố cho tiêu đề bằng 'C#', đó là những thẻ dành cho :) – kprobst
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