С# клиент/сервер Вопрос

Я невероятно смущен тем, что здесь происходит. Я ставил точки останова, и я просто не могу понять. По сути, у меня есть клиент и сервер. Я хочу, чтобы клиент отправил две отдельные строки данных. Поставив точки останова, я заметил, что мой клиент действительно заполняет обе строки соответствующими данными. Однако сервер никогда не увидит вторую строку. Почему это происходит и как это исправить? Любая помощь вообще будет принята с благодарностью! Ниже мой код:

Сервер:

    private static void HandleClientComm(object client)
    {
        /** creating a list which contains DatabaseFile objects **/
        List<DatabaseFile> theDatabase = new List<DatabaseFile>();

        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();

        StringBuilder response = new StringBuilder();
        byte[] message = new byte[4096];
        int bytesRead;

        do
        {
            bytesRead = 0;

            try
            {
                /*do
                {
                    bytesRead = clientStream.Read(message, 0, message.Length);
                    response.Append(Encoding.ASCII.GetString(message, 0, bytesRead));
                } while (clientStream.DataAvailable);*/

когда я изменяю этот закомментированный код на bytesRead = clientStream.Read(message, 0, 4096); я получаю ошибку IOException, которая читается следующим образом: Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом. Следовательно, я изменил его на цикл do while. Как мне обойти это исключение IOException и принять вторую строку?

                ASCIIEncoding encoder = new ASCIIEncoding();
                String file = encoder.GetString(message, 0, bytesRead);
                Menu.Insert(theDatabase, file); 
            }
            catch (Exception)
            {
                // A socket error has occured
                break;
            }

            if (bytesRead == 0)
            {
                // The client has disconnected from the server
                break;
            }
        } while (clientStream.DataAvailable);

        // Release connections
        clientStream.Close();
        tcpClient.Close();
    }

Клиент:

    static void Main(string[] args)
    {
        TcpClient client = new TcpClient();

        IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);

        client.Connect(serverEndPoint);

        NetworkStream clientStream = client.GetStream();
        NetworkStream clientStream2 = client.GetStream();

        ASCIIEncoding encoder = new ASCIIEncoding();
        ASCIIEncoding encoder2 = new ASCIIEncoding();
        String text = System.IO.File.ReadAllText("FirstNames.txt");
        String text2 = System.IO.File.ReadAllText("LastNames.txt");

        byte[] buffer = encoder.GetBytes(text);

        Console.ReadLine(); 
        clientStream.Write(buffer, 0, buffer.Length);
        clientStream.Flush();
        Console.ReadLine();

        byte[] buffer2 = encoder2.GetBytes(text2);

        clientStream2.Write(buffer2, 0, buffer2.Length);
        clientStream2.Flush();
        Console.ReadLine(); 
    }
}

person BigBug    schedule 23.09.2011    source источник


Ответы (3)


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

  1. Клиент: client.Connect(serverEndPoint)
    Сервер: HandleClientComm(newClient)

  2. Клиент: clientStream.Write(buffer, 0, buffer.Length)
    Сервер: bytesRead = clientStream.Read(message, 0, message.Length)
    Обратите внимание, что Read не гарантирует прочтение всего сообщения. Вполне нормально возвращать только ту часть, которая была получена на данный момент

  3. Клиент: Console.ReadLine()
    Сервер: while (clientStream.DataAvailable)
    В потоке нет данных — клиент их не отправлял. Скорее всего, это произошло бы даже без ReadLine — есть некоторое время, прежде чем клиент снова отправит данные

  4. Сервер: tcpClient.Close()

  5. Клиент: clientStream2.Write(buffer2, 0, buffer2.Length)
    Вы можете получить здесь исключение или нет — в зависимости от того, закрыл ли уже сервер соединение, в любом случае сервер больше не читает.

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

Клиент:

using (var client = new TcpClient("localhost", 8888))
using (var clientStream = client.GetStream())
{
    var buffer = Encoding.ASCII.GetBytes( File.ReadAllText("FirstNames.txt") );
    clientStream.Write(buffer, 0, buffer.Length);

    buffer = Encoding.ASCII.GetBytes( File.ReadAllText("LastNames.txt") );
    clientStream.Write(buffer, 0, buffer.Length);
}

Сервер:

using (var tcpClient = (TcpClient)client)
using (var clientStream = tcpClient.GetStream())
{
    // Store everything that the client sends.
    // This will work if the client closes the connection gracefully
    // after sending everything

    var receivedData = new MemoryStream();
    clientStream.CopyTo(receivedData);

    var rawBytes = receivedData.ToArray();
    var file = Encoding.ASCII.GetString(rawBytes, 0, rawBytes.Length);

    Menu.Insert(theDatabase, file);
}

Протокол прост, и может быть достаточно для вашего случая. Однако с ним есть проблемы, которые следует решать в производственном коде (например, что, если клиент отправляет слишком много данных, исчерпывает память сервера, что, если клиент прекращает отправку, не закрывая соединение и т. д.).

person Bojan Resnik    schedule 23.09.2011

потому что while (clientStream.DataAvailable) больше не соответствует действительности после вашего первого вызова клиента.

person Yuan    schedule 23.09.2011
comment
проблема в том, что когда я заменяю этот раздел кода чем-то вроде bytesREad = clientStream.REad(message,0,4096); мой клиент отправляет мне ошибку IOException следующего содержания: Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом. Следовательно, я изменил его на цикл do while. Как мне обойти это исключение IOException и принять вторую строку? - person BigBug; 23.09.2011
comment
@BlueButtons: вам нужен внутренний цикл, который просто вращается, говоря do...while (!clientStream.DataAvailable), и ожидает данных. Как только данные приходят, он считывает все доступные данные, проверяет сообщение и либо прекращает чтение данных, либо снова начинает ждать следующего фрагмента. - person mellamokb; 23.09.2011
comment
@mellamokb: я попробовал это предложение, и моя программа, похоже, ничего не принимает, когда я это делаю. Вы имеете в виду, что это написано в моем коде: do{ bytesRead = clientStream.read(....); response.Append(....) do{ }while(!clientStream.dataAvailable); } while(clientStream.DataAvailable); ? Это то, что я пробовал... - person BigBug; 23.09.2011
comment
В общем, ваш сервис должен быть включен все время, что вы могли бы сделать while(true) для вращения и прослушивания. конечно, вы можете пофантазировать с while(shouldContinue) - person Yuan; 23.09.2011
comment
@yuan, хорошо, я это исправил, но моя главная проблема заключается в том, что я могу читать две строки. когда я делаю это изменение, я получаю исключение IOException, которое я указывал ранее. есть идеи? - person BigBug; 23.09.2011
comment
Я не очень хорошо знаком с тем, что вы делаете, но вы вызываете NetworkStream clientStream = tcpClient.GetStream(); только один раз, что может вызвать исключение после первого вызова клиента. - person Yuan; 23.09.2011
comment
@Yuan: хм, кажется, я на самом деле звоню дважды. у меня есть NetworkStream clientStream = client.GetStream(); NetworkStream clientStream2 = client.GetStream(); поэтому у меня есть два отдельных потока. Хотя спасибо за ответ.. - person BigBug; 23.09.2011
comment
Hey Yuan: Спасибо за помощь, кажется, я решил проблему самостоятельно. Кстати, в коде есть два NetworkStreams. Они просто один под другим. Нужно прочитать много кода - в нем легко заблудиться, извините за это. Еще раз огромное спасибо!!! - person BigBug; 23.09.2011

Вы выходите из цикла client-recv на своем сервере только потому, что DatAvailable имеет значение false. Это означает, что если клиент должен был отправить кадр данных (который вы потребляете) и сделать паузу, тогда ваш сервер не увидит доступных данных в этот момент и отключится, даже если через долю секунды должен был прийти другой кадр данных от клиента. in. Почти всегда конец диалога зависит от содержания передаваемых данных. Вы, конечно, никогда не сможете полагаться на тот факт, что DataAvailable однажды окажется ложным.

В качестве продолжения, если вы предоставите дополнительную информацию об используемом протоколе, мы можем оказать дополнительную помощь. Например, если протокол состоит в том, что две строки отправляются с CR/LF в конце, тогда цикл сервера должен проверять буфер, чтобы узнать, когда следует отключиться.

person tcarvin    schedule 23.09.2011
comment
проблема в том, что когда я заменяю этот раздел кода чем-то вроде bytesREad = clientStream.REad(message,0,4096); мой клиент отправляет мне ошибку IOException следующего содержания: Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом. Следовательно, я изменил его на цикл do while. Как мне обойти это исключение IOException и принять вторую строку? - person BigBug; 23.09.2011