Проблемы с получением асинхронного сервера сокетов C # - PullRequest
3 голосов
/ 09 мая 2011

Я применил свое Серверное приложение относительно этого поста здесь: http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c8781#Client1

Подводя итог: я использую асинхронные сокеты аля BeginAccept (..), BeginReceive (..). Мой сервер способен обрабатывать несколько клиентов, и все работает нормально, пока клиент не выполнит две или более синхронных операции отправки, не ожидая некоторое время. Клиент не получает никакой ошибки и поэтому не уведомляется, что сервер не получает второе сообщение! Если клиент ждет ок. Через 100 мс после первой операции отправки все работает нормально. Я думал, что когда я использую TCP, я могу гарантировать, что сервер получит сообщение. (За исключением того, что есть исключение)! Не могли бы вы предоставить мне решение, чтобы исправить это.

Вот методы WaitForData (..) и OnDataReceive (..), которые я реализовал на сервере

public void WaitForData(MyClient client)
{
    try
    {
        if (pfnCallBack == null)
        {
            pfnCallBack = new AsyncCallback(OnDataReceived);
        }

        iarResult = client.Socket.BeginReceive(client.DataBuffer,
                                                0, client.DataBuffer.Length,
                                                SocketFlags.None,
                                                pfnCallBack,
                                                client);
    }
    catch (SocketException se)
    {
        MessageBox.Show("SocketException@WaitForData" + se.Message);
    }
}
public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        MyClient user= (MyClient)asyn.AsyncState;
        int iRx = user.Socket.EndReceive(asyn);

        byte[] receivedData = user.DataBuffer;

        MemoryStream memStream = new MemoryStream();
        BinaryFormatter binForm = new BinaryFormatter();
        memStream.Write(receivedData, 0, receivedData.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        MyMessage msg = (MyMessage)binForm.Deserialize(memStream);

        switch (msg.Command)
        {
            case (MyMessage.MyCommand.ConnId):
                this.connId = (int) msg.MyObject;
                tsslConnStatus.Text += " | ID: " + connId.ToString();
            break;

            case (MyMessage.MyCommand.Text):
                MessageBox.Show(msg.MyObject.ToString());
                break;
        }
        WaitForData(server);
    }
    catch (ObjectDisposedException ode)
    {
        MessageBox.Show("ObjectDisposedException@OnReceiveData" + ode.Message);
    }
    catch (SocketException se)
    {
        MessageBox.Show("SocketException@OnReceiveData" + se.Message);
    }
}

КЛИЕНТ вызывает синхронный метод отправки дважды или более! сервер INSTANCEOF MyClient

if (server.Socket.Connected)
{
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    bf.Serialize(ms, message);
    MyMessage = new MyMessage(something);
    server.Socket.Send(ms.ToArray());
}

Итак, я думаю, что этих фрагментов кода должно быть достаточно, чтобы вы поняли идею, которую я пытался использовать! Если вам нужна дополнительная информация или фрагменты кода, просто скажите мне, что я опубликую это!

Thanx!

1 Ответ

12 голосов
/ 09 мая 2011

TCP основан на потоке, а не на сообщениях. Один Read может содержать любую из следующих альтернатив:

  • Небольшая часть сообщения
  • Половина сообщения
  • Точно одно сообщение
  • Полтора сообщения
  • Два сообщения

Таким образом, вам нужно использовать какой-то метод, чтобы увидеть, пришло ли полное сообщение. Наиболее распространенные методы:

  • Добавить нижний колонтитул (например, пустую строку), который указывает конец сообщения
  • Добавить заголовок фиксированной длины, содержащий длину сообщения

Обновление

Простой пример с длиной как заголовок.

Сторона сервера:

var buffer = binaryFormmater.Serialize(myobj);
var length = buffer.Length;
networkStream.Send(length);
networkStream.Send(buffer, 0, buffer.Length);

Клиентская сторона:

var header = new buffer[4];
// TODO: You need to make sure that 4 bytes have been read.
networkStream.Read(header, 0, 4);
var length = BitConverter.ToInt32(buffer);

var readbuffer= new byte[65535];
var bytesLeft = length;
var messageStream = new MemoryStream();
while (bytesLeft > 0)
{
     var read = networkStream.Read(readbuffer, 0, bytesLeft);
     messageStream.Write(readbuffer, 0, read);
     bytesLeft -= read,
}

messageStream.Seek(0, SeekOrigin.Begin);
MyMessage msg = (MyMessage)binForm.Deserialize(messageStream);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...