Dang. Я не решаюсь даже ответить на это, учитывая сановников, которые уже взвешены, но здесь идет. Будьте нежны, о Великие!
Не имея возможности читать блог Марка (он заблокирован здесь из-за корпоративной интернет-политики), я собираюсь предложить «другой путь».
Уловка, на мой взгляд, заключается в , чтобы отделить получение данных от обработки этих данных .
Я использую класс StateObject, определенный следующим образом. Он отличается от реализации MSDN StateObject тем, что он не включает объект StringBuilder, константа BUFFER_SIZE является закрытой и для удобства включает конструктор.
public class StateObject
{
private const int BUFFER_SIZE = 65535;
public byte[] Buffer = new byte[BUFFER_SIZE];
public readonly Socket WorkSocket = null;
public StateObject(Socket workSocket)
{
WorkSocket = workSocket;
}
}
У меня также есть класс Packet, который является просто оберткой вокруг буфера и отметки времени.
public class Packet
{
public readonly byte[] Buffer;
public readonly DateTime Timestamp;
public Packet(DateTime timestamp, byte[] buffer, int size)
{
Timestamp = timestamp;
Buffer = new byte[size];
System.Buffer.BlockCopy(buffer, 0, Buffer, 0, size);
}
}
Моя функция ReceiveCallback () выглядит следующим образом.
public static ManualResetEvent PacketReceived = new ManualResetEvent(false);
public static List<Packet> PacketList = new List<Packet>();
public static object SyncRoot = new object();
public static void ReceiveCallback(IAsyncResult ar)
{
try {
StateObject so = (StateObject)ar.AsyncState;
int read = so.WorkSocket.EndReceive(ar);
if (read > 0) {
Packet packet = new Packet(DateTime.Now, so.Buffer, read);
lock (SyncRoot) {
PacketList.Add(packet);
}
PacketReceived.Set();
}
so.WorkSocket.BeginReceive(so.Buffer, 0, so.Buffer.Length, 0, ReceiveCallback, so);
} catch (ObjectDisposedException) {
// Handle the socket being closed with an async receive pending
} catch (Exception e) {
// Handle all other exceptions
}
}
Обратите внимание, что эта реализация абсолютно не обрабатывает полученные данные и не имеет никаких ожиданий относительно того, сколько байтов предполагается получить. Он просто получает все данные, находящиеся в сокете (до 65535 байт), и сохраняет эти данные в списке пакетов, а затем немедленно ставит в очередь еще один асинхронный прием.
Поскольку обработка больше не происходит в потоке, который обрабатывает каждый асинхронный прием, данные, очевидно, будут обрабатываться другим потоком , поэтому операция Add () синхронизируется через оператор блокировки. Кроме того, поток обработки (будь то основной поток или какой-либо другой выделенный поток) должен знать , когда есть данные для обработки. Для этого я обычно использую ManualResetEvent, что я и показал выше.
Вот как работает обработка.
static void Main(string[] args)
{
Thread t = new Thread(
delegate() {
List<Packet> packets;
while (true) {
PacketReceived.WaitOne();
PacketReceived.Reset();
lock (SyncRoot) {
packets = PacketList;
PacketList = new List<Packet>();
}
foreach (Packet packet in packets) {
// Process the packet
}
}
}
);
t.IsBackground = true;
t.Name = "Data Processing Thread";
t.Start();
}
Это базовая инфраструктура, которую я использую для всей моей сокетной связи. Это обеспечивает хорошее разделение между получением данных и обработкой этих данных.
Что касается другого возникшего у вас вопроса, важно помнить, что при таком подходе каждый экземпляр пакета не обязательно представляет полное сообщение в контексте вашего приложения. Экземпляр пакета может содержать частичное сообщение, одно сообщение или несколько сообщений, а ваши сообщения могут охватывать несколько экземпляров пакета. Я рассказал, как узнать, когда вы получили полное сообщение в соответствующем вопросе, который вы разместили здесь .