再次重用j​​ava套接字客户端

时间:2012-01-17 20:56:35

标签: java sockets

我的问题非常简单。我只想再次使用客户端对象套接字。

通常人们在客户端尝试连接服务器时都会创建一个新套接字:

 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)向服务器发送更多信息,但是不再访问服务器。为了使它工作,我必须完成上述所有步骤。为什么呢?


这是我用来测试我的意思的代码。 “Second Request not not ...”之后的代码无法到达服务器。但是,如果我删除那些评论之上的评论,它将会通过。为什么我不能重新使用相同的客户端套接字对象而不必使用新的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 个答案:

答案 0 :(得分:2)

您需要使用线程服务器。

服务器可以访问多个客户端。每个服务器套接字必须由其自己的线程处理。服务器侦听已知套接字,并且每个客户端使用单独的套接字连接(因此多个客户端可以在同一主机上运行)。

首先,JavaDocs是了解如何或为什么你应该使用库的最糟糕的地方之一,他们真的只告诉你库是什么

只有当您使用single threaded server

时,您的帖子才会出现这种情况

Oracle在Java server sockets上有一个页面,用于解释具有单个服务器的多个客户端(具有重用)。

他们还有一个更详细的advanced sockets page

答案 1 :(得分:1)

问题是服务器上的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);
    }

}

我只是很快将这些代码放在一起并且还没有测试编译它,所以它可能仍然包含一些错误,但总的想法就在那里。

答案 2 :(得分:0)

我认为答案就在这里:http://docs.oracle.com/javase/1.4.2/docs/api/java/net/ServerSocket.html#accept%28%29

当您有一台服务器参加多个客户端时,根本无法完成。 Server.accept创建一个新套接字,这意味着客户端失去与服务器在第一个实例创建的套接字的连接,因为这会覆盖套接字对象。

您无法从循环中获取接受,因为这意味着服务器只会侦听一个客户端。您可以使用两个实例,但此解决方案仅适用于仅侦听一个客户端的多个请求的服务器。

总之,它无法完成,因此网络上的所有示例在客户端尝试向服务器发送新信息时都使用新的Socket(),即使客户端是相同的。

答案 3 :(得分:0)

好的人。感谢aoi222和Kely French主要,但感谢其他任何人。我找到了解决方案:

1.-显然,您需要创建一个专门为客户提供服务的线程。客户端第一次连接到服务器到一般端口。这创建了一个侦听特定端口的线程,让客户端知道新端口,新线程将要侦听的端口。然后客户端更改其端口并创建一个新套接字以启动与客户端的对话。

2.-作为单独线程运行的服务器必须继续监听aoi222指示的循环。对于已分配客户端的独占线程,accept只是在循环之外。请参阅下面的代码并抱歉变量的名称,因为它们有点令人困惑:

      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 ()
       }

&#34; exclusiveThread&#34;是一个实现2中代码的线程。

<强>编辑: 我忘了说客户端必须向服务器发送结束消息,以确定对话已完成并停止lopp。否则会导致java.io.EOFException。

非常感谢你们的帮助。