Java套接字编程

4
我正在使用Java sockets构建一个简单的客户端/服务器应用程序,并尝试使用ObjectOutputStream等进行实验。
我一直在按照这个网址上的教程http://java.sun.com/developer/technicalArticles/ALT/sockets,从中间开始讲述如何在套接字上传输对象。
请查看我的客户端代码http://pastebin.com/m37e4c577,但是它似乎无法正常工作,我无法弄清楚哪里出了问题。底部注释掉的代码是直接从教程中复制的-当我只使用它而不是创建客户端对象时,它可以正常工作。
有人能看出我做错了什么吗?

你需要将它的注释取消掉。开玩笑,那么编译错误是什么? - Filip Ekberg
没有错误——只是我的未注释的代码无法正常工作,我不知道为什么?已注释的代码可以正常工作——我只是将其分离并放入了一个实际的对象……这让我发疯。 - Malachi
6个回答

7
问题在于你创建流的顺序:
在文章提到的服务器中(我假设你正在使用它),当新连接打开时,服务器首先打开输入流,然后是输出流:
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行来实现这一点。


5
你没有将对象写入任何地方。 再次查看链接,在某个地方你必须写入: ```html

这里需要写入对象

```
 oos.writeObject( new Date() );

在你的代码中,你只有

ois.readObject();

这就是为什么。

所贴代码仅包括客户端,因此无需编写对象。 - Kothar

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

2
如果您尝试使用调试器,它会告诉您问题出在哪里(也许不是为什么)。
您的问题在于ObjectOutputStream写入一个头文件,而ObjectInputStream读取该头文件。您先创建了ObjectInputStream,这意味着它正在尝试读取一个永远不会被写入的头文件。
解决方案:始终先创建ObjectOutputStream并在创建ObjectInputStream之前刷新()它。

2

也许您希望先学习最基本的内容。

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

它启动了一个 服务器,只为一个客户端提供服务,并发送一个对象后退出。

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

这里没有处理任何异常。只是为了简化事情,但这不是处理异常的正确方式。

当您理解了这里的所有概念后,理解教程中的内容将会更容易。

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

    }
}

我希望这对您有所帮助。

2

温馨提示。

当您使用ObjectOutputStream时,请注意它会保留一个引用缓存。如果您写入一个对象,更改对象内容,然后再次发送相同的对象,您将获得重复的数据。例如:

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

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

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接