制作一个Java客户端服务器应用程序

8
我正在尝试以客户端/服务器方式制作Java应用程序。 客户端是一个SWT GUI,显示来自服务器的数据。服务器连接到数据库。
抱歉,这是一个经典问题,但我不知道如何开始。
在我工作的项目中,他们使用Proxy.newProxyInstance()实现了很多与Glassfish服务器透明调用相关的操作。
我不想使用Glassfish服务器。我只想要一个简单的纯Java应用程序。但代理的概念似乎很酷。
您有这样的想法或示例吗?我该如何编写服务器部分来处理客户端的请求?
提前感谢
Fluminis

我本来想建议尝试JMS生产者/消费者方法,但你想要简单的东西。 - Buhake Sindi
4个回答

40
我将为您解释TCP:
基本概念是您必须在一台机器上运行一个“服务器”。该服务器接受等待连接的客户端。每个连接都通过端口进行(希望您知道...)。
始终使用1024以上的端口,因为低于1025的端口大多数情况下都保留用于标准协议(如HTTP(80),FTP(21),Telnet等)。

然而,在Java中创建服务器是这样完成的:

ServerSocket server = new ServerSocket(8888); // 8888 is the port the server will listen on.

如果你想进行研究,"Socket" 是你可能要找的词。
要将客户端连接到服务器,您需要编写以下内容:
Socket connectionToTheServer = new Socket("localhost", 8888); // First param: server-address, Second: the port

但是现在还没有建立连接。服务器必须接受等待的客户端(正如我在上面提到的):

Socket connectionToTheClient = server.accept();

完成了!你的连接已经建立!通信就像文件IO一样。唯一需要记住的是,你必须决定何时要刷新缓冲区并通过套接字发送数据。
使用PrintStream进行文本写入非常方便:

OutputStream out = yourSocketHere.getOutputStream();
PrintStream ps = new PrintStream(out, true); // Second param: auto-flush on write = true
ps.println("Hello, Other side of the connection!");
// Now, you don't have to flush it, because of the auto-flush flag we turned on.

使用BufferedReader进行文本读取是一个很好(最佳*)的选择:

InputStream in = yourSocketHere.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = br.readLine();
System.out.println(line); // Prints "Hello, Other side of the connection!", in this example (if this would be the other side of the connection.

希望您能从以下信息开始进行网络交互!
附注:当然,所有的网络代码都必须通过try-catch捕获IOExceptions。
编辑:我忘记写为什么它并不总是最佳选择。BufferedReader使用缓冲区,并尽可能多地读取到缓冲区中。但有时您不希望BufferedReader窃取换行符后面的字节并将其放入自己的缓冲区中。
简短示例:
InputStream in = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// The other side says hello:
String text = br.readLine();
// For whatever reason, you want to read one single byte from the stream,
// That single byte, just after the newline:
byte b = (byte) in.read();

但是 BufferedReader 已经将你想读取的字节存储在其缓冲区中。因此,调用 in.read() 将返回读者缓冲区中最后一个字节之后的字节。
因此,在这种情况下,最好的解决方案是使用 DataInputStream,并以自己的方式管理它,以知道字符串的长度,并仅读取该数量的字节并将其转换为字符串。或者:您可以使用 DataInputStream.readLine()。
这种方法不使用缓冲区,逐字节读取并检查换行符。因此,此方法不会从底层 InputStream 中窃取字节。
编辑:您可以开发自己的协议,在其中可以使用 Java 反射请求方法调用。例如:
String className = ...;
String methodName = ...;
Class[] methodParamTypes = ...;
Object[] methodParams = ...;
Class cl = Class.forName(className);
Method me = cl.getDelcaredMethod(methodName, methodParamTypes);
Object returnValue = me.invoke(this, methodParams);

一旦您拥有了对象,您可以使用序列化将其发送到连接的另一侧:ObjectOutputStreamsObjectInputStreams。使用这两个类,您可以通过流写入和读取对象。

Object obj = ...; // Your object you want to write through the stream. (Needs to implement java.io.Serializable)
ObjectOutputStream oos = new ObjectOuptputStream(socket.getOutputStream());
oos.writeObject(oos);
oos.reset(); // (***)
// Don't close it! Otherwise the connection will be closed as well.

在连接的另一端:

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object object = ois.readObject(); // Read the object
// Don't close it!
// Then cast it to whatever you want.

(***): 关于reset()的更多信息以及何时使用它,请查看我的问题


谢谢你的回答。我已经明白了你的例子如何检索文本。但是,使用socket调用服务器上的方法并返回复杂对象是否可能? - fluminis
请看ObjectOutputStream和ObjectInputStream。 - Martijn Courteaux
1
此外,您可能会考虑使用Java RMI(远程方法调用)-有很好的文档资料可供参考。 - JoséNunoFerreira
注意:Unix要求您以root身份使用<1024端口。Windows有一个更复杂的方案,但通常允许它。 - Thorbjørn Ravn Andersen
@ThorbjørnRavnAndersen:这是我在答案中提到的第一件事之一。 - Martijn Courteaux
我正在阐述为什么会这样。 - Thorbjørn Ravn Andersen

5

Glassfish可能比你需要的功能更强大,但我不会放弃利用现有库。

一些套接字选项:

如果您决定更多地走向Web服务而不是套接字,则Jetty是适合小型HTTP的选择。


3
如果您想编写一个简单的客户端服务器程序,可以按照tutorial中给出的建议进行操作。
如果您想做一些更复杂的事情,就像您说的那样,使用纯Java,那么您可能需要看看RMI
然而,现在Web服务非常流行,您可能会发现从长远来看,这更简单。

1
1. 确定您想要提供的服务(loadThing,editThing,makeThingBelch)。 2. 决定通信/消息协议(http似乎很简单)。 3. 实现服务,并频繁进行单元测试以确保它们正常工作。 4. 现在开始编写客户端,记住您可能需要添加新服务或更改现有服务。
另外一个需要考虑的问题是,为什么您想要使用“简单的Java”编写它,而不是使用现有的应用程序服务器?一些EJB3方法可以抽象和管理通信、安全、事务完整性等等。

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