Повторно используйте клиент Java-сокета еще раз - PullRequest
5 голосов
/ 18 января 2012

Мой вопрос очень прост.Я просто хочу снова использовать сокет объекта клиента.

Обычно люди создают новый сокет каждый раз, когда клиент пытается подключиться к серверу, например:

 Socket test = new Socket (host, port);
 OutputStream out = test.getOutputStream ();
 InputStream in = test.getInputStream ();
 out.writeObject (object)
 out.flush ();
 in.readObject();

После первого writeObjectхотите снова отправить дополнительную информацию на сервер, повторно используя out.writeObject (nextObject), однако сервер больше не доступен.Чтобы заставить его работать, я должен пройти все шаги выше.Почему?


Вот код, который я использую, чтобы проверить, что я имел в виду.Код после «Второй запрос не ...» не достигает сервера.Однако, если я удалю комментарии чуть выше этих комментариев, они пройдут.Почему я не могу повторно использовать один и тот же объект сокета клиента без необходимости использования нового Socket ().

Class Hello -> Data passed between the server and the client
Class TestClient -> Client
Class TestServer -> Server


package tests;

import java.io.Serializable;


public class Hello implements Serializable {

    String hola = "hello my friend";

    public String getHello () {

        return hola;

    }

    public void setHello (String str) {

        hola = str;

    }

}


package tests;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class TestClient {

    public static void main(String[] args) {


        try {

            ObjectOutputStream clientOutput;
            ObjectInputStream serverInput;
            Hello goingout = new Hello ();

            Socket client = new Socket ("localhost", 5555);
            clientOutput  = new ObjectOutputStream (client.getOutputStream());
            serverInput   = new ObjectInputStream (client.getInputStream());

            clientOutput.writeObject(goingout);
            clientOutput.flush();
            Hello comingin = (Hello) serverInput.readObject();
            System.out.println ("first time received: " + comingin.getHello());


            /*
             * Socket client = new Socket ("localhost", 5555);
               clientOutput  = new ObjectOutputStream (client.getOutputStream());
               serverInput   = new ObjectInputStream (client.getInputStream());
             */

            /*
             * Second request doesn't got through the server
             */
            comingin.setHello("again");
            clientOutput.writeObject(goingout);
            clientOutput.flush();
            comingin = (Hello) serverInput.readObject();
            System.out.println ("second time received: " + comingin.getHello());



        } catch (ClassNotFoundException ex) {
            System.out.println (ex.getMessage());
        } catch (UnknownHostException ex) {
            System.out.println (ex.getMessage());
        } catch (IOException ex) {
            System.out.println (ex.getMessage());
        }


    }

}

public class TestServer {

    public static void main(String[] args) {



        try {

              ServerSocket server = new ServerSocket(5555, 100);
              ObjectInputStream clientInput;
              ObjectOutputStream serverOutput;

              for (;;) {
                  System.out.println ("Listening for connections");
                  Socket fromClient = server.accept();
                  serverOutput = new ObjectOutputStream (fromClient.getOutputStream());
                  clientInput = new ObjectInputStream (fromClient.getInputStream());
                  Hello received = (Hello) clientInput.readObject();
                  serverOutput.writeObject(received);
                  serverOutput.flush();
              }


        } catch (ClassNotFoundException ex) {
            Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

Ответы [ 4 ]

2 голосов
/ 18 января 2012

Вам необходимо использовать многопоточный сервер.

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

Во-первых, JavaDocs - это одно из худших мест, чтобы понять как или почему вам следует использовать библиотеку, они действительно только говорят вам что делает библиотека. 1009 *

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

У Oracle есть страница о сокетах Java-сервера , которая объясняет несколько клиентов (с повторным использованием) с одним сервером.

У них также есть страница расширенных сокетов , которая более подробно описана.

1 голос
/ 18 января 2012

Проблема в том, что объявление Socket fromClient = server.accept(); на сервере находится внутри области действия for. Таким образом, в конце цикла for (после serverOutput.flush();) переменная fromClient уничтожается, и при следующем запуске цикла создается новая переменная. Вам нужно будет реструктурировать его следующим образом, если вы хотите, чтобы одно и то же соединение могло отправлять / получать несколько сообщений:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap

public class User extends Runnable
{
    public String username;
    public Socket socket;
    public TestServer testServerReference;
    ObjectInputStream clientInput;
    ObjectOutputStream serverOutput;

    public User(Socket s, TestServer tS)
    {
        socket = s;
        testServerReference = tS;
    }

    /**
     *  Creates the in/out Streams, returns the username
     */
    public String init()
    {
        serverOutput = new ObjectOutputStream (fromClient.getOutputStream());
        clientInput = new ObjectInputStream (fromClient.getInputStream());
        this.username = (String)clientInput.readObject();  //the first thing the client should send is a username

    }

    public void run()
    {
        try
        {
            //add send/receive logic here for connected client
        }
        catch (Exception ex)
        {
        }
        finally
        {
            testServerReference.removeUser(username);
        }
    }
}

public class TestServer {

    public static HashMap<String,User> userList = new HashMap<String,User>();
    public static void main(String[] args) 
    {

        try {

              ServerSocket server = new ServerSocket(5555, 100);

              while (true) //start accepting new connections
              {
                  System.out.println ("Listening for connections");
                  Socket fromClient = server.accept();
                  User newUser = new User(fromClient,this);
                  String newUsername = newUser.init();
                  userList.add(newUsername,newUser);
                  (new Thread(newUser)).start();
              }


        } catch (ClassNotFoundException ex) {
            Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void removeUser(String username)
    {
        userList.remove(username);
    }

}

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

0 голосов
/ 18 января 2012

Хорошо, люди. В основном спасибо aoi222 и Kely French, но всем остальным. Я нашел решение, которое:

1.- Очевидно, вам нужно создать поток, который обслуживает исключительно клиента. Первый раз клиент подключается к серверу к общему порту. Это создает поток, слушающий определенный порт, и сообщает клиенту, какой новый порт будет прослушивать новый поток. Затем клиент меняет свой порт и создает новый сокет, чтобы начать разговор с клиентом.

2.- Сервер, работающий как отдельный поток, должен продолжать слушать цикл, как указано в aoi222 навсегда. Принять только из цикла для эксклюзивного потока, который был назначен клиенту. Пожалуйста, смотрите код ниже и извините за имя переменных, так как они немного сбивают с толку:

      fromClient = server.accept();
      clientInput = new ObjectInputStream (fromClient.getInputStream());
      serverOutput = new ObjectOutputStream (fromClient.getOutputStream());
      Hello received;

      while ( (received = (Hello)clientInput.readObject()) != null ) {
          received.setHello("pepito grillo");
          serverOutput.writeObject(received);
      }

3.- Пожалуйста, обратите внимание, что сервер основного потока ДОЛЖЕН иметь server.accept в цикле для того, чтобы принять соединение новых клиентов

       for (;;) {
              newClient = mainServer.accept();
              new exclusiveThread ().start ()
       }

"exclusiveThread" - это поток, который реализует код в 2.

Отредактировано: Я забыл сказать, что клиент должен отправить серверу сообщение о завершении, чтобы определить, что разговор завершен, и остановить lopp. В противном случае это приведет к исключению java.io.EOFException.

Большое спасибо, ребята, за вашу помощь.

0 голосов
/ 18 января 2012

Я думаю, что ответ здесь: http://docs.oracle.com/javase/1.4.2/docs/api/java/net/ServerSocket.html#accept%28%29.

Это просто невозможно сделать, если у вас есть сервер, обслуживающий несколько клиентов.Server.accept создает новый сокет, что означает, что клиент теряет соединение с сокетом, который сервер создал в первый раз, так как это перезаписывает объект сокета.

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

Таким образом, это невозможно, поэтому во всех примерах в сети используются новыеSocket () на стороне клиента в любое время пытается отправить новую информацию на сервер, даже если клиент такой же.

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