Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 21.03.10 01:01
Оценка:
Я только учусь не ругайте сильно.

Есть вот такой код:
      IPAddress ipAddress = Dns.GetHostAddresses("127.0.0.1")[0];
      TcpListener serverListener = new TcpListener(ipAddress, 8888);
      serverListener.Start();
      serverListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), serverListener);


        private static LinkedList<TcpClient> clientList = new LinkedList<TcpClient>();

    // Process the client connection.
    public static void DoAcceptTcpClientCallback(IAsyncResult ar) {
      // Get the listener that handles the client request.
      TcpListener listener = (TcpListener) ar.AsyncState;

      // End the operation and display the received data on  the console.
      TcpClient client = listener.EndAcceptTcpClient(ar);
      clientList.AddLast(client);

      // Process the connection here. (Add the client to a server table, read data, etc.)
      NetworkStream clientStream = client.GetStream();

      Console.WriteLine("Client connected completed. Total clients:" + clientList.Count);

      // Signal the calling thread to continue.
      //tcpClientConnected.Set();
    }


Вся проблема в том, что DoAcceptTcpClientCallback срабатывает только один раз =( А как перехватить момент подключение к серверу и момент отключения от сервера?

Спасибо.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Re: Socket + Отключение клиента
От: Jolly Roger  
Дата: 21.03.10 04:36
Оценка: 2 (1)
Здравствуйте, EyfelFenk, Вы писали:

EF>не ругайте сильно.


А несильно можно?

EF>Вся проблема в том, что DoAcceptTcpClientCallback срабатывает только один раз =( А как перехватить момент подключение к серверу и момент отключения от сервера?


Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.

Да вот Вам упрощённый пример эхо-сервера, как раз вчера набросал для теста.

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SrvTcpListener
{
    class ServerProgram
    {
        class AsyncData
        {
            public Socket s;
            public byte[] Buf;
        }

        static volatile bool _сlose;

        static AsyncCallback acptCallback = new AsyncCallback((ar) =>
            {
                var listener = (TcpListener)ar.AsyncState;
                if (_сlose) return;
                listener.BeginAcceptSocket(acptCallback, listener);

                var socket = listener.EndAcceptSocket(ar);
                AsyncData data = new AsyncData();
                data.s = socket;
                data.Buf = new byte[256];
                socket.BeginReceive(data.Buf, 0, data.Buf.Length,
                    SocketFlags.None, recvCallback, data);
            });

        static AsyncCallback recvCallback = new AsyncCallback((ar) =>
            {
                SocketError err;
                AsyncData data = (AsyncData)ar.AsyncState;
                int Len = data.s.EndReceive(ar, out err);
                if (Len > 0 && err == SocketError.Success)
                {
                    data.s.BeginSend(data.Buf, 0, Len, SocketFlags.None, (ar2) =>
                        {
                            SocketError err2;
                            int Len2 = data.s.EndSend(ar2, out err2);
                            if (Len2 > 0 && err2 == SocketError.Success)
                                data.s.BeginReceive(data.Buf, 0, data.Buf.Length,
                                    SocketFlags.None, recvCallback, data);
                            else
                                // Клиент потерян
                                data.s.Close();
                        }, null);
                }
                else
                    // Клиент потерян
                    data.s.Close();
            });
        static void Main(string[] args)
        {
            IPAddress addr = IPAddress.Parse("127.0.0.1");
            var listener = new TcpListener(addr, 7777);
            _сlose = false;
            listener.Start();
            listener.BeginAcceptSocket(acptCallback, listener);
            Console.WriteLine("Start");
            Console.ReadKey();
            _сlose = true;
            listener.Stop();
            Console.WriteLine("Stop");
            Thread.Sleep(1);
        }
    }
}
"Нормальные герои всегда идут в обход!"
Re[2]: Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 21.03.10 12:38
Оценка:
Здравствуйте, Jolly Roger, Вы писали:


JR>Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.


Спасибо. Но вот когда мой клиент пытается отправить и получить данные от сервера:
try {
        connectToServer();

        NetworkStream serverStream = clientSocket.GetStream();

        CommandLogin cmd = new CommandLogin();
        cmd.write(name, Encoding.UTF8);
        cmd.write(password, Encoding.UTF8);

        //Отправка команды
        byte[] outStream = cmd.generateAnswer();
        serverStream.Write(outStream, 0, outStream.Length);
        serverStream.Flush();
        Console.WriteLine("КЛИЕНТ: отправили " + outStream);

        //Получение ответа
        byte[] inStream = new byte[10025];
        serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
        Console.WriteLine("КЛИЕНТ: получили " + outStream);

        serverStream.Close();

        disconnectFromServer();

        /*IUser user = ServerObject.ExecuteLogin(name, password);
        if (user != null) {
          return user;
        } else {
          throw new BadUserLogin("Введенные данные не верны. Повторите попытку.");
        }*/

        

        return null;
      } catch (SocketException error) {
        throw new ServerResponseException("Неудалось подключиться к серверу:" + error.Message);
      }


Выпадает ошибка:
An unhandled exception of type 'System.ObjectDisposedException' occurred in System.dll
Additional information: Доступ к ликвидированному объекту невозможен.


в строке:
int Len2 = data.socket.EndSend(ar2, out err2);


Что не так?
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Re[2]: Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 21.03.10 14:08
Оценка:
Здравствуйте, Jolly Roger, Вы писали:


JR>Чтобы сработало больше одного раза, надо вызвать больше одного раза А чтобы "поймать" момент отключения клиента, сокет должен находиться в состоянии передачи или приёма.


И не совысем понятно, где окончание приема от клиента =(
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Re[3]: Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 21.03.10 16:42
Оценка:
Здравствуйте, EyfelFenk, Вы писали:

Вопрос снимаю, это я дурак не там скобочку поставил =)
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Re[2]: Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 21.03.10 17:19
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

То есть мне надо, чтобы сервер по завершению приема данных от данного клиента выполнил определенную команду, и после ее завершения отправил ответ клиенту.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Re[3]: Socket + Отключение клиента
От: Jolly Roger  
Дата: 22.03.10 04:49
Оценка:
Здравствуйте, EyfelFenk, Вы писали:

EF>То есть мне надо, чтобы сервер по завершению приема данных от данного клиента выполнил определенную команду, и после ее завершения отправил ответ клиенту.


Ну так и в чём проблема-то, я не понимаю

        static AsyncCallback recvCallback = new AsyncCallback((ar) =>
            {
                SocketError err;
                AsyncData data = (AsyncData)ar.AsyncState;
                int Len = data.s.EndReceive(ar, out err);
                if (Len > 0 && err == SocketError.Success)
                {
                    // Вот здесь Вы получили данные от клиента, Len - число байт,
                    // здесь, убедившись, что запрос получен полностью, и обрабатывайте, записывайте
                    // ответ в буфер и отправляйте следующим вызовом
                    data.s.BeginSend(data.Buf, 0, Len, SocketFlags.None, (ar2) =>
                        {
                            SocketError err2;
                            int Len2 = data.s.EndSend(ar2, out err2);
                            if (Len2 > 0 && err2 == SocketError.Success)
                                data.s.BeginReceive(data.Buf, 0, data.Buf.Length,
                                    SocketFlags.None, recvCallback, data);
                            else
                                // Клиент потерян
                                // Сюда(либо ниже) попадёте, если клиент отключился 
                                // либо обнаружились проблемы со связью
                                // Если у Вас Err == Success, а Len == 0, то это означает, что клиент отключился.
                                //  Если  Err != Success, то имели место какие-то проблемы, 
                                // подавляющее  большинство из которых - фатальные, 
                                // и требуют немедленного закрытия сокета.
                                data.s.Close();
                        }, null);
                }
                else
                    // Клиент потерян
                    data.s.Close();
            });

Предвидя вопрос, пара слов о размере пакета. Сервер должен либо знать этот размер, либо уметь определить его конец, чтобы быть уверенным в получении его целиком. Это определяется протоколом. Например, некоторые протоколы использует в качестве метки конца строку "/r/n/r/n". Или можно передатьразмер от клиента первыми байтами пакета. Какой способ выбрать, решать придётся Вам, но что-то должно быть обязательно.
"Нормальные герои всегда идут в обход!"
Re[4]: Socket + Отключение клиента
От: EyfelFenk Россия  
Дата: 22.03.10 11:45
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Предвидя вопрос, пара слов о размере пакета. Сервер должен либо знать этот размер, либо уметь определить его конец, чтобы быть уверенным в получении его целиком. Это определяется протоколом. Например, некоторые протоколы использует в качестве метки конца строку "/r/n/r/n". Или можно передатьразмер от клиента первыми байтами пакета. Какой способ выбрать, решать придётся Вам, но что-то должно быть обязательно.


Спасибо большое за разъяснение детальное. А то после Java с колбаками туплю еще =(
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
Еще один вопрос
От: EyfelFenk Россия  
Дата: 23.03.10 18:27
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

В этом коде, я начиюнаю слушать опять, команды:
    static AsyncCallback acceptSocketCallback = new AsyncCallback((ar) => {
      Array values = (Array)ar.AsyncState;
      var listener = (TcpListener)values.GetValue(0);
      if (_сlose) {
        return;
      }
      listener.BeginAcceptSocket(acceptSocketCallback, values);

Вопрос в том, как определить новые это коннект от клиента или нет, просто если я два раза один клиентом посылаю команду, то он каждый раз заного создает все это конструкцию, а мне надо сохранять в список текущий подключний, чтобы при необходимости можно было отправить данные ему. Как это сделать? =(
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.