2014-02-18 4 views
0

Я просматривал, искал ... и ничто не искрится на мой взгляд!Яйцо сокета: Сброс соединения

Я запускаю службу типа чата между сервером и Android-приложением. Клиент подключается, сервер регистрирует сокет, и каждые 10 минут сервер отправляет на все подключенные устройства сообщение.

Моя проблема в том, что в случайном порядке у меня есть исключение сброса соединения. Я не могу отследить, когда возникает проблема.

Мой стороне сервера код:

final public class ChatRoomService { 
    private final static String AUTHENTICATE = "AUTHENTICATE"; 
    private final static String BROADCAST = "BROADCAST"; 
    private final static String DISCONNECT = "DISCONNECT"; 
    private final static String OK = "OK"; 
    private final static String NOK = "NK"; 

    private final static Logger LOGGER = Logger.getLogger(ChatRoomService.class); 

    private ServerSocket listener = null; 

    @Inject 
    private EntityManager entityManager; 
    public EntityManager getEntityManager() { 
     return entityManager; 
    } 

    @Inject 
    private PlayerManager playerManager; 
    PlayerManager getPlayerManager() { 
     return playerManager; 
    } 

    private static HashSet<ChatRoomConnection> connections = new HashSet<ChatRoomConnection>(); 
    public void addConnection(ChatRoomConnection c) { 
     synchronized(connections) { 
      connections.add(c); 
     } 
    } 
    public void removeConnection(ChatRoomConnection c) { 
     synchronized(connections) { 
      connections.remove(c); 
     } 
    } 

    public void startListeningToChatRoomConnection() throws IOException { 
     listener = new ServerSocket(9010); 

     try { 
      LOGGER.infof("startListening - Start listening on port %s", 9010); 
      while (true) { 
       ChatRoomConnection connection = new ChatRoomConnection(listener.accept(), this); 
       addConnection(connection); 
       connection.start(); 
      } 
     } catch (IOException e) { 
      if (!listener.isClosed()) 
       LOGGER.errorf("listenToChatRoomConnection - Connection lost during connection: %s", e.getMessage()); 
     } finally { 
      if (listener != null && !listener.isClosed()) { 
       LOGGER.infof("listenToChatRoomConnection - Stop listening"); 
       listener.close(); 
      } 
     } 
    } 

    public void stopListeningToChatRoomConnection() throws IOException { 
     if (!listener.isClosed()) { 
      LOGGER.infof("stopListeningToChatRoomConnection - Stop listening"); 

      listener.close(); 
      listener = null; 

      // Closing all sockets 
      for (ChatRoomConnection connection : connections) { 
        connection.close(); 
      } 
      // Clear up the connections list 
      synchronized (connections) { 
       connections.clear(); 
      } 
     } 
    } 

    public void broadcastToChatRoomClients(Object message) { 
     synchronized (connections) { 
      // Log 
      LOGGER.debugf("Broadcast ChatRoom: %s - %s", 
        connections.size(), 
        message.toString()); 

      for (ChatRoomConnection connection : connections) { 
       LOGGER.debugf("Broadcast ChatRoom to %s", connection.userName); 
       connection.publish(message); 
      } 
     } 
    } 

    private ChatRoomService() { 
    } 

    private static class ChatRoomConnection extends Thread { 
     private Socket socket; 
     private BufferedReader readerFromClient; 
     private PrintWriter writerToClient; 
     public String userName; 

     private ChatRoomService chatCService; 

     ChatRoomConnection(Socket socket, ChatRoomService chatRoomService) { 
      super("ChatRoomConnection"); 

      this.socket = socket; 
      this.chatRoomService = chatRoomService; 
     } 

     public void run() { 
      try { 
       readerFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
       writerToClient = new PrintWriter(socket.getOutputStream(), true); 

       // 1- Authenticate the Device/ Player 
       writerToClient.println(ChatRoomService.AUTHENTICATE); 
       writerToClient.flush(); 
       Gson gson = new Gson(); 
       Request request = gson.fromJson(readerFromClient.readLine(), Request.class); 
       if (chatRoomService.getPlayerManager().isPlayerSignedIn(request.getPlayerId(), request.getSignedInOn())) { 
        Player player = (Player) chatRoomService.getEntityManager().find(Player.class, request.getPlayerId()); 
        userName = player.getUsername(); 
        LOGGER.infof("listenToChatRoomConnection - Connection established with %s", userName); 
        writerToClient.println(ChatRoomService.OK); 
        writerToClient.flush(); 
        while (true) 
         if ((readerFromClient.readLine() == null) || 
          (readerFromClient.readLine().startsWith(ChatRoomService.DISCONNECT))) 
         break; 
       } else { 
        writerToClient.println(ChatRoomService.NOK); 
        writerToClient.flush(); 
       } 
      } catch (Exception e) { 
       LOGGER.errorf("listenToChatRoomConnection - Error with %s: %s", userName, e.getMessage()); 
       e.printStackTrace(); 
      } finally { 
       try { 
        if (!socket.isClosed()) { 
         LOGGER.infof("listenToChatRoomConnection - Connection closed by the client for %s", userName); 
         socket.close(); 
        } 
       } catch (IOException e) { 
        LOGGER.errorf("listenToChatRoomConnection - Can not close socket: %s", e.getMessage()); 
        e.printStackTrace(); 
       } finally { 
        chatRoomService.removeConnection(this); 
       } 
      } 
     } 

     public void publish(Object message) { 
      if (!socket.isClosed()) { 
       writerToClient.println(ChatRoomService.BROADCAST); 
       Gson gson = new Gson(); 
       writerToClient.println(gson.toJson(message)); 
      } 
     } 

     public void close() { 
      writerToClient.println(ChatRoomService.DISCONNECT); 
      try { 
       LOGGER.infof("listenToChatRoomConnection - Connection closed by the server for %s", userName); 
       socket.close(); 
      } catch (IOException e) { 
       LOGGER.errorf("Error when trying to close a socket: %s", e.getMessage()); 
       e.printStackTrace(); 
      } 
     } 
    }; 
} 

Код устройства:

public class ServerBroadcastManager { 
    private static final String TAG = ServerBroadcastManager.class.getName(); 

    // Type of messages from the server 
    static public String AUTHENTICATE = "AUTHENTICATE"; 
    static public String DISCONNECT = "DISCONNECT"; 
    static public String BROADCAST = "BROADCAST"; 
    static public String OK = "OK"; 
    static public String NOK = "NK"; 

    private int networkPort; 
    private ServerBroadcastListener broadcastListener; 
    private Socket networkSocket; 

    BufferedReader in; 
    PrintWriter out; 

    public ServerBroadcastManager(Context context, ServerBroadcastListener listener, int port) { 
    this.networkPort = port; 
    this.broadcastListener = listener; 
    } 

    public void startListening(final Context context) { 
    Runnable run = new Runnable() { 
     @Override 
     public void run() { 
      // Make connection and initialize streams 
      try { 
       networkSocket = new Socket(); 
       networkSocket.connect(new InetSocketAddress(mydomain, networkPort), 30*1000); 
       in = new BufferedReader(new InputStreamReader(
         networkSocket.getInputStream())); 
       out = new PrintWriter(networkSocket.getOutputStream(), true); 

       // Process all messages from server, according to the protocol. 
       while (true) { 
        String line = in.readLine(); 
        if (line.startsWith(ServerBroadcastManager.AUTHENTICATE)) { 
         Request request = formatAuthenticateRequest(context); 
         Gson requestGson = new Gson();   
         out.println(requestGson.toJson(request)); 
         out.flush(); 
         // Waiting for confirmation back 
         line = in.readLine(); 
         if (line.startsWith(ServerBroadcastManager.OK)) { 
         } else if (line.startsWith(ServerBroadcastManager.NOK)) { 
         } 
        } else if (line.startsWith(ServerBroadcastManager.BROADCAST)) { 
         Gson gson = new Gson(); 
         @SuppressWarnings("unchecked") 
         LinkedHashMap<String,String> broadcast = gson.fromJson(in.readLine(), LinkedHashMap.class); 
         broadcastListener.processBroadcast(broadcast); 
        } else if (line.startsWith(ServerBroadcastManager.DISCONNECT)) { 
         break; 
        } 
       } 
      } catch (UnknownHostException e) { 
        Log.i(TAG, "Can not resolve hostname"); 
      } catch (SocketTimeoutException e) { 
        Log.i(TAG, "Connection Timed-out"); 

       broadcastListener.connectionFailed(); 
      } catch (IOException e) { 
        Log.i(TAG, "Connection raised on exception: " + e.getMessage()); 

       if (!networkSocket.isClosed()) { 
        broadcastListener.connectionLost(); 
       } 
      } 
     }   
    }; 

    Thread thread = new Thread(run); 
    thread.start(); 
} 

public void stopListening() { 
    try { 
     if (networkSocket != null) 
      networkSocket.close(); 
    } catch (IOException e) { 
      Log.i(TAG, "Exception in stopListening: " + e.getMessage()); 
    } 
} 

private Request formatAuthenticateRequest(Context context) { 
    Request request = new Request(); 
    SharedPreferences settings = context.getApplicationContext().getSharedPreferences(Constants.USER_DETAILS, 0); 

    request.setPlayerId(BigInteger.valueOf((settings.getLong(Constants.USER_DETAILS_PLAYERID, 0)))); 
    request.setSignedInOn(settings.getLong(Constants.USER_DETAILS_SIGNEDINON, 0)); 

    return request; 
    } 
} 

Мой последний курорт может быть, чтобы переместить мой сервер в другое место, и посмотреть, если это не может быть связано с моим широкополосным маршрутизатором. Я заметил, что некоторые из моих HTTP-вызовов не доходят до сервера, хотя пересылка портов на месте.

Спасибо. Дэвид.

ответ

1

Я не могу найти, где в исходном коде сервер отправляет сообщение каждые 10 минут всем подключенным клиентам, но у меня возникли исключения перезагрузки соединения при использовании длительных соединений WebSocket. Я решил эту проблему, убедившись, что некоторые данные (сообщение ping-pong) отправляются от клиента каждую минуту.

В то время я проследил проблему с моим домашним маршрутизатором, который через 5 минут просто закрыл все простоя, но брандмауэры могут проявлять такое же поведение. Ни сервер, ни клиент не замечают закрытое соединение, пока данные не будут переданы. Это особенно неприятно для клиента, если клиент ожидает данные с сервера - эти данные просто не прибудут. Поэтому клиент должен проверить, действительно ли соединение по-прежнему действует (и снова подключаться, когда это необходимо).

С момента появления сообщения ping-pong от клиента каждую минуту я не видел исключений для сброса соединения.

+0

Спасибо! Хорошая вещь о PING (которую я не хотел реализовывать) заключается в том, что теперь клиент вызывает IOException, поэтому я могу захватить и потенциально повторно подключиться). Знаете ли вы, что хороший образец дизайна для повторного подключения? Я думаю делать в заявлении catch и перезапускать нового обработчика, но это кажется странным. –

+0

@l_ingenu Поместите код устройства Runnable в отдельный класс/Object и поместите код, устанавливающий соединение в отдельный метод подключения этого запускаемого объекта. Вызовите метод connect в начале метода run (как вы это делали сейчас) и вызовите его снова, когда требуется повторное подключение. Единственное, что связано с повторным подключением, это то, что вам придется очистить некоторые переменные (например, закрыть сокет и сбросить состояние соединения), прежде чем снова вызвать метод подключения. – vanOekel

Смежные вопросы