Проблема сокета новичка .NET - PullRequest
3 голосов
/ 21 февраля 2010

У меня есть клиентская / серверная сетевая программа на C #, которую я написал с помощью Классы TCPListener и TCPClient. Сервер читает все с клиента (небольшое количество XML) просто отлично, пока я не попытаюсь отправить большой файл (100k) возвращается клиенту.

Я использую функции потока для как клиент, так и сервер с неблокирующими функциями сокетов. Когда я делаю socket.SendFile ("filename") обратно клиенту, файл получает отрезал - я установил размер приемного буфера на клиенте в прошлом 100k, но он все равно отключается около 25k и связь между клиентом и сервером ненадежные послесловия.

Мой основной вопрос что происходит, если данные как-то остаются в канале? т.е. это будет читать по следующему сокету. Читать ... Требует ли каждый вызов отправки точно один и только один Читать? Может быть, я не даю клиенту достаточно времени, чтобы прочитал файл но их оба на одной машине и я пробовал Спать несколько секунд в разных местах без успеха.

Ответы [ 5 ]

2 голосов
/ 21 февраля 2010

1 Для отправки вызова может потребоваться более одного вызова Read, чтобы получить, а 1 Read call может прочитать данные, отправленные несколькими Send call.

TCP просто предоставляет поток, так что вам решать, как разделить передаваемые вами данные или сообщения.

В этом случае вам, вероятно, просто нужно выполнить цикл, читая до тех пор, пока поток не закроется.

2 голосов
/ 21 февраля 2010

Вполне возможно, что вы не можете прочитать все сообщение с помощью одного вызова Read (возможно, все данные еще не поступили). В сетевом программировании вы часто помещаете вызов Read в цикл while и просто читаете (), пока не получите все ожидаемое сообщение.

1 голос
/ 21 февраля 2010

Что я обычно делаю, это создаю структуру заголовка, которая отправляется

Header Size (int, 4 bytes)
File Name Offset (int, 4 bytes)
File Name Size (int , 4 bytes)
File Data Offset (int, 4 bytes)
File Data Size (int , 4 bytes)
[ message data here]

и затем этот заголовок читается с использованием BinaryReader или копированием байтов в структуру с использованием Marshal. Таким образом, вы всегда будете знать, какие данные поступают и сколько раз вам нужно вызвать Read ().

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

[StructLayout LayoutKind.Sequential]
struct MessageHeader
{
    public int HeaderSize;
    public int FileNameOffset;
    public int FileNameSize;
    public int FileDataOffset;
    public int FileDataSize;
}

Тогда Marshal.PtrToStructure позволит вам создать экземпляр этой структуры прямо из байта [], который вы читаете из сокета

1 голос
/ 21 февраля 2010

Вы, вероятно, хотите что-то вроде этого:

socket.Blocking = false;


const int ChunkSize = 1492;
const int ReceiveTimeout = 10000;
const int SendTimeout = 10000;

public void Send(Byte[] data)
{
    var sizeBytes = BitConverter.GetBytes(data.Length);
    SendInternal(sizeBytes);
    SendInternal(data);
}

public Byte[] Receive()
{
    var sizeBytes = ReceiveInternal(4);
    var size = BitConverter.ToInt32(sizeBytes, 0);
    var data = ReceiveInternal(size);
    return data;
}

private void SendInternal(Byte[] data)
{
    var error = SocketError.Success;
    var lastUpdate = Environment.TickCount;
    var size = data.Length;
    var count = 0;
    var sent = 0;

    while (sent < size)
    {
        count = Math.Min(ChunkSize, size - sent);
        count = socket.Send(data, sent, count, SocketFlags.None, out error);

        if (count > 0)
        {
            sent += count;
            lastUpdate = Environment.TickCount;
        }

        if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock)
            throw new SocketException((Int32)error);
        if (Environment.TickCount - lastUpdate > SendTimeout)
            throw new TimeoutException("Send operation timed out.");
        if (count == 0 && !socket.Poll(100, SelectMode.SelectWrite))
            throw new SocketException((Int32)SocketError.Shutdown);
    }
}

private Byte[] ReceiveInternal(Int32 size)
{
    var error = SocketError.Success;
    var lastUpdate = Environment.TickCount;
    var buffer = new Byte[ChunkSize];
    var count = 0;
    var received = 0;

    using (var ms = new MemoryStream(size))
    {
        while (received < size)
        {
            count = Math.Min(ChunkSize, size - received);
            count = socket.Receive(buffer, 0, count, SocketFlags.None, out error);

            if (count > 0)
            {
                ms.Write(buffer, 0, count);
                received += count;
                lastUpdate = Environment.TickCount;
            }

            if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock)
                throw new SocketException((Int32)error);
            if (Environment.TickCount - lastUpdate > ReceiveTimeout)
                throw new TimeoutException("Receive operation timed out.");
            if (count == 0 && socket.Poll(100, SelectMode.SelectRead) && socket.Available == 0)
                throw new SocketException((Int32)SocketError.Shutdown);
        }

        return ms.ToArray();
    }
}
0 голосов
/ 21 февраля 2010

Попробуйте отправить чанк со стороны сервера в чанках. Как и сказал другой, размещение кода очень поможет нам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...