PDA

Просмотр полной версии : C#, сокеты, l2phx


Kilatif
05.02.2012, 22:30
Пишу программку на C#, что то типа ООГ бота. Подобные программы я уже писал на Delphi и Qt, теперь решил попробовать на C#. Все отлично, но везде свой подход работы с сокетами. И я столкнулся с какой-то достаточно интересной проблемой...

Когда отправляю на сервер пакеты больше 256 байт, сервер его благополучно принимает, а вот следующие за ним уже нет. И происходит это, когда я отсылаю пакеты больше 256 байт и только тогда.
Конкретный пример:

Подключаюсь я к GS и сразу отправляю ProtocolVersion размером 267 байт (ему впринципе и 7 хватает, но клиент посылает еще какую-то дрянь вконце и поэтому посылаю ее и я).
После этого клиент мне присылает KeyInit.
Я инициализирую свой Xor и отправляю ему RequestAuthLogin иии... и все. В ответ тишина.

В логах анализатора трафика (Wireshark) этот пакет на сервер видно что отправляется.
Сразу возникает логичная догадка о том, что я неправильно сформировал пакет, но не тут то было.
Когда я посылаю ProtocolVersion размером < 256 байт, то тогда сервер мне вполне нормально присылает ответ на RequestAuthLogin.

Для дебага этой проблемы я решил подключить l2phx.
Подключил его с помощью LSP перехвата соединения и начал следить.

Когда посылаю ProtocolVersion 267 байт, то происходит полная херня:
в программе четко и ясно отсылаю 267 байт, а отсылается только 11, что четко видно как в логах l2phx, так и в логах Wireshark.
Следующие за таким пакетом от клиента вообще больше не отсылаются, сколько бы я не пытался, при этом соединение не разрывается, именно я пытаюсь отослать пакет, а он не отправляется на сервер. К тому же, методом тыка получил простую закономерность, что отсылается как раз таки N mod 256 байт.
Если отсылается < 256 байт пакет, то и с l2phx все работает нормально.

Происходит это конкретно в C# и нигде больше, поэтому посчитал какой-то особенностью работы с сокетами о которых я не знаю.
Чтение пакетов я осуществляю в новом потоке в цикле:

private void PacketsReader()
{
while (isClientConnected)
{
byte[] size = new byte[4];

lock (thisLock)
{
client.Receive(size, 2, SocketFlags.None);
}
packetSize = BitConverter.ToInt32(size, 0);
packetSize -= 2;

if (packetSize > 0)
{
packet = new byte[packetSize];
client.Receive(packet, packetSize, SocketFlags.None);

packet = crypter.Decrypt(packet, packetSize);
parser.Parse(packet, packetSize);
}
}
}

Как видно из кода, после дешифрации пакет отправляется на парсер, где определяется тип пакета и вызывается соответствующая ф-я для обработки.
Отправка пакета происходит вот так:

public void SendPacket(byte[] packet, int packetSize, bool isEncrypt = true)
{
byte[] temp = new byte[packetSize];

Array.Copy(packet, 0, temp, 0, packetSize);
if (isEncrypt) temp = crypter.Encrypt(temp, packetSize);

byte[] sendPacket;

packetSize += 2;
sendPacket = new byte[packetSize];

Array.Copy(BitConverter.GetBytes((byte)packetSize) , 0, sendPacket, 0, 2);
Array.Copy(temp, 0, sendPacket, 2, packetSize - 2);

lock (thisLock)
{
client.Send(sendPacket);
}
}


Кто знает почему происходит такая ерунда? :)
P.S. Если что-то тут не указал, спрашивайте - укажу, отвечу.

supernewbie
05.02.2012, 23:31
у тебя второй байт размера теряется © кэп

Добавлено через 1 минуту
с C# работал сто лет назад и то пять минут, но инфа 95% что тут неправильно
(byte)packetSize, и надо как-нить типо (short)packetSize

Kilatif
05.02.2012, 23:43
supernewbie, мда уж... как обычно, идиотские ошибки основанные на идиотских мелочах. И че эт меня переклинило что размер пакета в одном байте храниться? :D Я уж думал тут инопланетяне замешаны в этих ошибках :D Спс большое )

xkor
06.02.2012, 10:50
Kilatif, интересно твои боты на других языках фрагментацию игровых пакетов тоже не учитывали? при малых размерах пакетов client.Receive будет читать весь пакет, но вот пакеты большего размера из за потоковой сути TCP будут разбиваться на порции и в момент вызова client.Receive могут быть ещё не полностью приняты компом, и client.Receive при этом считает только то количество которое было уже принято, эта функция не ждёт пока в буфере окажется столько байт сколько ты указал считать, считывается столько сколько пришло.
Я в аналоге пакетхака который пишу на шарпе с TCP соединением работаю как с потоком и в асинхронном режиме:
public abstract class AbstractIOLayer
{
public Action onClose;
public Action<BinaryReader, int> onData;

protected ProxyConnection owner;
public AbstractIOLayer(ProxyConnection owner) { this.owner = owner; }

public abstract void SendData(byte[] buf, int offset, int len);
public abstract void Close();
public abstract bool Actived { get; }
public abstract void BeginRead();
}

public class StreamContainer : AbstractIOLayer
{
private Stream _stream;
private const int _bufSize = 30000;
private readonly byte[] _buf = new byte[_bufSize];
private BinaryReader _reader;
private bool _needRead; // true if need call BeginReceive in OnRecieve
private bool _opened = true;

public override bool Actived { get { return _opened; } }

public StreamContainer(Stream stream, ProxyConnection owner) : base(owner)
{
_reader = new BinaryReader(new MemoryStream(_buf));
_stream = stream;
}

public override void SendData(byte[] buf, int offset, int len)
{
if (_opened) _stream.Write(buf, offset, len);
}

public override void Close()
{
if (_opened)
{
_opened = false;
_stream.Close();
if (onClose != null) onClose();
}
}

public override void BeginRead()
{
_needRead = false; // if call in any layer don't call in OnRecieve
if (_opened) _stream.BeginRead(_buf, 0, _bufSize, OnRead, null);
}

private void OnRead(IAsyncResult r)
{
if (_opened) try
{
_needRead = true;
_reader.BaseStream.Position = 0;
var l = 0;
try { l = _stream.EndRead(r); }
//catch (Exception e) { CoreLog.AddExpectedError(e, owner.GetNameWithDir(this)); }
catch { Close(); return; }

if (l > 0 && onData != null)
{
onData(_reader, l);
if (_needRead) BeginRead();
}
else Close();
}
catch (Exception e) { CoreLog.AddError(e, owner.GetNameWithDir(this)); }
}
}

ЗЫ блин, когда же я пофикшу замену в подсветке синтаксиса символа "[" на "[ ;"...

mira
06.02.2012, 12:31
Как не странно в "определенных условиях". (Хз зависит от настроек винды соединения или еще чего) сокет не дробит пакеты вообще. И на конкретной машине недочет может быть невиден.

Kilatif
06.02.2012, 14:16
xkor, на других языках все было вполне нормально и никогда с этим проблем не возникало, хотя я задумывался над этим вопросом и у меня действительно никогда пакеты не дробились. Возможно это один из случаев, которые указал mira.