Асинхронный TCP-сервер - Советы по созданию сообщений
У меня есть асинхронный сервер сокетов TCP в C #, который я сделал с помощью оболочек TcpListener / TcpClient для Socket. Я довольно плохо знаком с сетью в целом, поэтому я не знал, как TCP обрабатывает отправленные данные и как он не сохраняет границы сообщений. После небольшого исследования я думаю, что я нашел твердое решение, но мне интересно, есть ли у кого-нибудь еще советы для меня.
В настоящее время данные, которые я отправляю, являются байтом [] сериализованного объекта класса с использованием protobuf (библиотека обмена данными Google)https://code.google.com/p/protobuf/
После того, как я сериализовал свои данные и перед их отправкой, я решил добавить 4-байтовый Int32 в начале байтового массива. Моя идея состоит в том, что когда пакет отправляется на сервер, он анализирует Int32 и затем ждет, пока не получит это число байтов, прежде чем что-либо делать с данными, в противном случае просто снова вызову BeginRead.
Вот код, через который он проходил до того, как я записал данные, и он, кажется, работает нормально, но я открыт для любых предложений по производительности:
public byte[] PackByteArrayForSending(byte[] data)
{
// [0-3] Packet Length
// [3-*] original data
// Get Int32 of the packet length
byte[] packetLength = BitConverter.GetBytes(data.Length);
// Allocate a new byte[] destination array the size of the original data length plus the packet length array size
byte[] dest = new byte[packetLength.Length + data.Length];
// Copy the packetLength array to the dest array
Buffer.BlockCopy(packetLength, 0, dest, 0, packetLength.Length);
// Copy the data array to the dest array
Buffer.BlockCopy(data, 0, dest, packetLength.Length, data.Length);
return dest;
}
Я немного застрял на стороне сервера. Я читал переменную packetLength, используя Buffer.BlockCopy для копирования первых 4 байтов, а затем BitConverter.ToInt32 для считывания длины, которую я должен получить. Я не уверен, должен ли я постоянно читать входящие данные в специфичный для клиента объект Stream или просто использовать цикл while. Вот пример кода, который у меня есть на стороне сервера:
NetworkStream networkStream = client.NetworkStream;
int bytesRead = networkStream.EndRead(ar);
if (bytesRead == 0)
{
Console.WriteLine("Got 0 bytes from {0}, marking as OFFLINE.", client.User.Username);
RemoveClient(client);
}
Console.WriteLine("Received {0} bytes.", bytesRead);
// Allocate a new byte array the size of the data that needs to be deseralized.
byte[] data = new byte[bytesRead];
// Copy the byte array into the toDeserialize buffer
Buffer.BlockCopy(
client.Buffer,
0,
data,
0,
bytesRead);
// Read the first Int32 tp get the packet length and then use the byte[4] to get the packetLength
byte[] packetLengthBytes = new byte[4];
Buffer.BlockCopy(
data,
0,
packetLengthBytes,
0,
packetLengthBytes.Length);
int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);
// Now what do you recommend?
// If not all data is received, call
// networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client);
// and preserve the initial data in the client object
Спасибо за ваше время и советы, я с нетерпением жду, чтобы узнать больше об этом предмете.