Java套接字编程

我正在使用java套接字构建一个简单的客户端/服务器应用程序并尝试使用ObjectOutputStream等。

我一直在关注这个教程http://java.sun.com/developer/technicalArticles/ALT/sockets ,当它谈到通过套接字传输对象时开始。

请参阅我的客户端代码http://pastebin.com/m37e4c577然而,这似乎不起作用,我无法弄清楚什么是不起作用。 底部注释掉的代码直接从教程中复制出来 – 当我只使用它而不是创建客户端对象时,这个工作正常。

谁能看到我做错了什么?

问题是您创建流的顺序:

在文章的服务器中(我假设你正在使用它),当打开一个新连接时,服务器首先打开一个输入流,然后打开一个输出流:

public Connect(Socket clientSocket) { client = clientSocket; try { ois = new ObjectInputStream(client.getInputStream()); oos = new ObjectOutputStream(client.getOutputStream()); } catch(Exception e1) { // ... } this.start(); } 

注释的示例代码使用相反的顺序,首先建立输出流,然后输入流:

 // open a socket connection socket = new Socket("localhost", 2000); // open I/O streams for objects oos = new ObjectOutputStream(socket.getOutputStream()); ois = new ObjectInputStream(socket.getInputStream()); 

但是你的代码反过来做了:

 server = new Socket(host, port); in = new ObjectInputStream(server.getInputStream()); out = new ObjectOutputStream(server.getOutputStream()); 

建立输出流/输入流对将停止,直到他们交换了握手信息,因此您必须匹配创建顺序。 您只需在示例代码中交换第34行和第35行即可。

你不是在任何地方写这个对象。

再次看到该链接,您需要写的地方:

  oos.writeObject( new Date() ); 

在您的代码中,您只有

 ois.readObject(); 

这就是为什么

只是提醒。

使用ObjectOutputStream时请记住它保留了引用缓存。 如果您编写对象,更改对象内容,然后再次发送相同的对象,您将获得重复的数据。 例如:

 List list = new ArrayList(); list.add("value1"); out.writeObject(list); list.clear(); list.add("value2"); out.writeObject(list); 

将在客户端生成两个带有字符串“value1”的列表。

为避免这种情况,必须调用reset方法以在多次写入相同的对象引用时重置流缓存:

 List list = new ArrayList(); list.add("value1"); out.writeObject(list); out.reset(); list.clear(); list.add("value2"); out.writeObject(list); 

也许你会先学习最基本的东西。

这是我刚刚编写的一个示例。

它启动一个只注册一个客户端的服务器 ,它发送一个对象并死掉。

当用户(您)按Enter键时,将创建一个新客户端 ,它将连接到先前创建的服务器并读取服务器将发送的对象。

这里没有处理任何例外。 只是为了使事情更简单,但这不是应该处理exception的方式。

当您理解了这里的所有概念时,将更容易理解本教程中的那些概念。

 import java.io.*; import java.net.*; import java.util.*; public class SimpleServer implements Runnable { // Creates the server, send a "date" and die. public void run() { try { ServerSocket server = new ServerSocket( 8090 ); Date creationDate = new Date(); System.out.println("(Server) Server is ready " + "and running at " + creationDate ); Socket uniqueClient = server.accept(); // We will write using this "chained" object. ObjectOutputStream out = new ObjectOutputStream( uniqueClient.getOutputStream()); out.writeObject( creationDate ); // close all, at this point forget about the exceptions. // this is lesson #1 out.close(); uniqueClient.close(); server.close(); System.out.println("(Server) The server is down"); }catch( IOException ioe ) {} } public static void main ( String [] args ) throws IOException , ClassNotFoundException { Thread serverThread = new Thread( new SimpleServer() ); serverThread.start(); // start the server thread ... doh.. System.out.println("(Client) Press enter when you want "+ " to connect to the server..."); Scanner scanner = new Scanner( System.in ); scanner.nextLine(); Socket client = new Socket("localhost", 8090 ); // Read from this object. ObjectInputStream in = new ObjectInputStream( client.getInputStream() ); Date date = ( Date ) in.readObject(); System.out.println("(Client) Current time is: " + new Date() ); System.out.println("(Client) Object read from server : " + date ); in.close(); client.close(); } } 

我希望这有帮助。

如果您尝试使用调试器,它会告诉您问题出在哪里。(也许不是为什么)

您遇到的问题是ObjectOutputStream写入标头,ObjectInputStream读取该标头。 您首先创建了ObjectInputStream,这意味着它正在尝试读取永远不会被写入的标头。

解决方案:始终首先创建ObjectOutputStream,然后在创建ObjectInputStream之前flush()它。

最好打开outputStream因为输出流不会阻塞。 然后,您有等待Stream的输入流。 在所有流之后,您写入流并刷新它 – outputStream.flush()以发送数据字节。 您还需要另一端的方法来读取输入,无论是简单的inputStream.read() ,它将每个字节读取为char的整数,还是使用BufferedReaderScanner 。 我已经使用了几乎所有可能的方法,但最有效的发送方法是outputStream.write(String) ,它将一串char作为byte s写入流中,并读取inputStream.read()读取一个char 。 我希望这有帮助。