#c# #c #boost-asio #flatbuffers
#c# #c #boost-asio #плоские буферы
Вопрос:
Я хочу создать такое же сообщение и отправить его с помощью C #, как я делаю это с помощью C , где все работает. Обратите внимание, что у меня есть клиент C #, с которым у меня проблемы, клиент C , где все работает нормально, и сервер C , который должен считывать сообщения как от клиентов C #, так и от C .
Вот как я отправляю сообщение с C :
void ConnectAuthserverCommand::SendLogin(tcp::socket amp;s, const flatbuffers::FlatBufferBuilder amp;builder) const {
ClientOpcode opc = CLIENT_LOGIN_REQUEST;
flatbuffers::FlatBufferBuilder builder2;
auto email = builder2.CreateString("test@abv.bg");
auto password = builder2.CreateString("test");
auto loginRequest = Vibranium::CreateLoginRequest(builder2, email, password);
builder2.FinishSizePrefixed(loginRequest);
size_t size2 = builder2.GetSize();
uint8_t *buf2 = builder2.GetBufferPointer();
uint8_t *actualBuffer2 = new uint8_t[size2 2];
actualBuffer2[1] = (opc >> 8);
actualBuffer2[0] = (opcamp;0xFF);
memcpy(actualBuffer2 2, buf2, size2);
boost::asio::write(s, boost::asio::buffer(actualBuffer2,size));
}
ClientOpcode
заключается в следующем:
enum ClientOpcode : uint16_t{
CLIENT_AUTH_CONNECTION = 0x001,
CLIENT_LOGIN_REQUEST = 0x002,
CLIENT_NUM_MSG_TYPES = 0x003,
};
Я делаю следующее: я получаю ClientOpcode
сообщение, которое я хочу поместить перед FlatBuffers
сообщением. Итак, я создаю массив uint8_t
, который я расширяю ровно на 2 байта (потому что размер uint16_t
равен 2 байтам). Затем на сервере я прочитал первые 2 байта, чтобы получить заголовок, и вот как я это делаю:
void Vibranium::Client::read_header() {
auto self(shared_from_this());
_packet.header_buffer.resize(_packet.header_size);
boost::asio::async_read(socket,
boost::asio::buffer(_packet.header_buffer.data(), _packet.header_size),
[this, self](boost::system::error_code ec,std::size_t bytes_transferred)
{
if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
{
Disconnect();
}
else
{
assert(_packet.header_buffer.size() >= sizeof(_packet.headerCode));
std::memcpy(amp;_packet.headerCode, amp;_packet.header_buffer[0], sizeof (_packet.headerCode));
if(_packet.headerCode)
read_size();
else
Logger::Log("UNKNOWN HEADER CODE", Logger::FatalError);
}
});
}
Пока все хорошо, однако я не могу отправить правильно отформатированное то же сообщение с клиента C #. Обратите внимание, что я отправляю точно такие же данные, взгляните:
Client authClient = GameObject.Find("Client").GetComponent<AuthClient>().client; // This is how I get Client class instance.
ClientOpcode clientOpcode = ClientOpcode.CLIENT_LOGIN_REQUEST;
var builder = new FlatBuffers.FlatBufferBuilder(1);
var email = builder.CreateString("test@abv.bg");
var password = builder.CreateString("test");
var loginRequest = LoginRequest.CreateLoginRequest(builder, email, password);
builder.FinishSizePrefixed(loginRequest.Value);
authClient.Send(builder, clientOpcode);
И вот как я на самом деле добавляю заголовок и отправляю данные на C#:
public static Byte[] PrependClientOpcode(FlatBufferBuilder byteBuffer, ClientOpcode code)
{
var originalArray = byteBuffer.SizedByteArray();
byte[] buffer = new byte[originalArray.Length 2];
buffer[1] = (byte)((ushort)code / 0x0100);
buffer[0] = (byte)code;
Array.Copy(originalArray, 0, buffer, 2, originalArray.Length);
return buffer;
}
public void Send(FlatBufferBuilder builder, ClientOpcode opcode)
{
byte[] buffer = builder.SizedByteArray();
var bufferToSend = PrependClientOpcode(builder, opcode);
if (bufferToSend.Length > MaxMessageSize)
{
Logger.LogError("Client.Send: message too big: " bufferToSend.Length ". Limit: " MaxMessageSize);
return;
}
if (Connected)
{
// respect max message size to avoid allocation attacks.
if (bufferToSend.Length <= MaxMessageSize)
{
// add to send queue and return immediately.
// calling Send here would be blocking (sometimes for long times
// if other side lags or wire was disconnected)
sendQueue.Enqueue(bufferToSend);
sendPending.Set(); // interrupt SendThread WaitOne()
}
}
else
{
Logger.LogWarning("Client.Send: not connected!");
}
}
ClientOpcode
перечисление на C # выглядит следующим образом:
public enum ClientOpcode : ushort
{
CLIENT_AUTH_CONNECTION = 0x001,
CLIENT_LOGIN_REQUEST = 0x002,
CLIENT_NUM_MSG_TYPES = 0x003,
}
Я думаю, что могу использовать ushort
в качестве замены uint16_t
в C #. Вот почему ClientOpcode является ushort .
Когда я отправляю сообщение, я получаю сообщение об ошибке на клиенте UNKNOWN HEADER CODE
. Если вы посмотрите на код сервера C для чтения заголовка, вы увидите, что это сообщение отображается, когда сервер не может прочитать код заголовка. Так или иначе, я не могу правильно разместить ClientOpcode
заголовок перед TCP-сообщением, отправленным с клиента C #.
Чтобы выяснить, в чем различия, я установил WireShark на хосте для отслеживания обоих сообщений. Вот они:
Это сообщение от корректно работающего клиента C :
И это дамп клиента C #:
Как вы можете видеть на втором изображении дампа TCP, длина больше. Сообщение C имеет длину 58
, равную длине сообщения C # 62
. Почему?
Клиент C отправляет данные:
0200340000000c00000008000c00040008000800000014000000040000000400000074657374000000000b00000074657374406162762e626700
Когда клиент C # отправляет:
0000003a0200340000000c00000008000c00040008000800000014000000040000000400000074657374000000000b00000074657374406162762e626700
Клиент C # добавляет к нему сообщение впереди 0000003a
. Если я удалю эти сообщения, они должны быть одинаковыми, и все будет работать.
Почему мой клиент C # добавляет эти дополнительные данные впереди и как я могу это исправить?
Комментарии:
1. Есть ли конкретная причина, по которой вы не можете использовать объединение различных кодов операций в схеме плоского буфера? Кажется более чистым, чем префикс типа кода операции вручную.
2. @Botje я хотел бы сначала добиться этого вручную. Что касается объединений FlatBuffer, я не совсем уверен, для чего они служат?
3. Согласно документации flatbuffers, вы должны иметь возможность определить
Union Op { AuthConnection, LoginRequest }
и посмотреть, какой вариант содержит сообщение.4. Вы не показали, как вы на самом деле отправляете это сообщение. Подозрительно, что 3a равно 58, что соответствует длине сообщения…
5. Проверьте длину
bufferToSend
… вероятно, оно не содержит этих дополнительных четырех байтов. Поэтому изучите код, который извлекается изsendQueue
очереди, который вы не показали.