我最近在C#中编写了一个快速而肮脏的概念验证代理服务器,作为使Java Web应用程序与驻留在另一台服务器上的传统VB6应用程序进行通信的努力的一部分.这简直太荒谬了:
代理服务器和客户端都使用相同的消息格式; 在代码中我使用一个ProxyMessage
类来表示来自客户端的请求和服务器生成的响应:
public class ProxyMessage { int Length; // message length (not including the length bytes themselves) string Body; // an XML string containing a request/response // writes this message instance in the proper network format to stream // (helper for response messages) WriteToStream(Stream stream) { ... } }
消息尽可能简单:正文的长度+消息正文.
我有一个单独的ProxyClient
类,表示与客户端的连接.它处理代理和单个客户端之间的所有交互.
我想知道的是它们是简化与异步套接字编程相关的样板代码的设计模式还是最佳实践?例如,您需要注意管理读取缓冲区,以免意外丢失字节,并且需要跟踪处理当前消息的距离.在我当前的代码中,我在我的回调函数中完成所有这些工作TcpClient.BeginRead
,并在一些实例变量的帮助下管理缓冲区的状态和当前的消息处理状态.
我传递给我的回调函数的代码BeginRead
如下,以及上下文的相关实例变量.代码似乎工作正常"原样",但我想知道它是否可以重构一点以使其更清晰(或者它可能已经是?).
private enum BufferStates { GetMessageLength, GetMessageBody } // The read buffer. Initially 4 bytes because we are initially // waiting to receive the message length (a 32-bit int) from the client // on first connecting. By constraining the buffer length to exactly 4 bytes, // we make the buffer management a bit simpler, because // we don't have to worry about cases where the buffer might contain // the message length plus a few bytes of the message body. // Additional bytes will simply be buffered by the OS until we request them. byte[] _buffer = new byte[4]; // A count of how many bytes read so far in a particular BufferState. int _totalBytesRead = 0; // The state of the our buffer processing. Initially, we want // to read in the message length, as it's the first thing // a client will send BufferStates _bufferState = BufferStates.GetMessageLength; // ...ADDITIONAL CODE OMITTED FOR BREVITY... // This is called every time we receive data from // the client. private void ReadCallback(IAsyncResult ar) { try { int bytesRead = _tcpClient.GetStream().EndRead(ar); if (bytesRead == 0) { // No more data/socket was closed. this.Dispose(); return; } // The state passed to BeginRead is used to hold a ProxyMessage // instance that we use to build to up the message // as it arrives. ProxyMessage message = (ProxyMessage)ar.AsyncState; if(message == null) message = new ProxyMessage(); switch (_bufferState) { case BufferStates.GetMessageLength: _totalBytesRead += bytesRead; // if we have the message length (a 32-bit int) // read it in from the buffer, grow the buffer // to fit the incoming message, and change // state so that the next read will start appending // bytes to the message body if (_totalBytesRead == 4) { int length = BitConverter.ToInt32(_buffer, 0); message.Length = length; _totalBytesRead = 0; _buffer = new byte[message.Length]; _bufferState = BufferStates.GetMessageBody; } break; case BufferStates.GetMessageBody: string bodySegment = Encoding.ASCII.GetString(_buffer, _totalBytesRead, bytesRead); _totalBytesRead += bytesRead; message.Body += bodySegment; if (_totalBytesRead >= message.Length) { // Got a complete message. // Notify anyone interested. // Pass a response ProxyMessage object to // with the event so that receivers of OnReceiveMessage // can send a response back to the client after processing // the request. ProxyMessage response = new ProxyMessage(); OnReceiveMessage(this, new ProxyMessageEventArgs(message, response)); // Send the response to the client response.WriteToStream(_tcpClient.GetStream()); // Re-initialize our state so that we're // ready to receive additional requests... message = new ProxyMessage(); _totalBytesRead = 0; _buffer = new byte[4]; //message length is 32-bit int (4 bytes) _bufferState = BufferStates.GetMessageLength; } break; } // Wait for more data... _tcpClient.GetStream().BeginRead(_buffer, 0, _buffer.Length, this.ReadCallback, message); } catch { // do nothing } }
到目前为止,我唯一真正的想法是将缓冲区相关的东西提取到一个单独的MessageBuffer
类中,只需让我的读回调在它们到达时向它添加新的字节.在MessageBuffer
随后会担心这样的事情目前BufferState
,当它接收完整的消息,该触发事件ProxyClient
可以然后进一步传播到主代理服务器代码,其中请求可以被处理.