我正在开发一个需要TCP客户端和服务器的项目,服务器将消息回送给客户端。以下是作业:
服务器应用程序应:
- 在众所周知的IP地址和端口上侦听TCP连接
- 接受在该端口上启动的连接
- 接收来自客户端的消息并回复它们
- 继续执行此操作,直到客户端断开连接。
醇>客户申请应:
- 与众所周知的IP地址和端口
建立服务器连接- 以异步方式向服务器发送消息。消息的格式是 你的选择;但是,它必须包含足够的信息才能实现 在从服务器返回时被识别出来。
醇>
我已经完成了Server的编码,这就是我为客户提出的。
我的问题:
服务器在众所周知的IP和端口上侦听TCP连接是什么意思在我的实现中,我使用了接受端口服务器监听的ServerSocket
。我的解释是否正确?
在我当前的TCPClient实现中,客户端向Server发送消息,但println()似乎是一个阻塞调用,这使得它成为Synchronous。我该怎么做才能使我的客户端异步?
为简洁起见,我没有添加TCPServer的代码,请告诉我是否需要
UPDATE *的 * 根据反馈,我已经通过TCPClient类进行了修改。收到客户端请求后,我生成两个线程ReceiveMessage和SendMessage。这样做会让我有以下异常:
[Client] Message sent: Message from Client 97
[Client] Message sent: Message from Client 98
[Client] Message sent: Message from Client 99
[Client] Done Sending all the messages
java.net.SocketException: Socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.fill(BufferedReader.java:136)
at java.io.BufferedReader.readLine(BufferedReader.java:299)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at org.chanders.client.ReceiveMessage.run(ReceiveMessage.java:18)
at java.lang.Thread.run(Thread.java:680)
以下是新客户代码:
public class TCPClient {
Socket clientSocket = null;
OutputStream out = null;
BufferedReader in = null;
String message = "Hello from Client";
int messagecount = 100;
// server credentials
private static final String SERVER_ADDRESS = "localhost";
private static final int SERVER_PORT = 50001;
protected void execute() {
try {
clientSocket = new Socket(SERVER_ADDRESS, SERVER_PORT);
Thread send = new Thread(new SendMessage(clientSocket.getOutputStream()));
Thread receive = new Thread(new ReceiveMessage(clientSocket.getInputStream()));
send.start();
receive.start();
//For server to wait until send and receive threads finish
send.join();
receive.join();
} catch (UnknownHostException uhe) {
System.err.println("Couldnt find host: " + SERVER_ADDRESS);
uhe.printStackTrace();
System.exit(-1);
}catch(IOException ioe) {
System.err.println("Couldnt get I/O: " + SERVER_ADDRESS);
ioe.printStackTrace();
System.exit(-1);
}catch(InterruptedException ie) {
System.err.println("Thread.join failed: ");
ie.printStackTrace();
System.exit(-1);
}
finally {
//cleanup();
}
}
private void cleanup() {
try {
clientSocket.close();
}catch(Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
public static void main(String[] args) {
TCPClient client = new TCPClient();
client.execute();
}
public class SendMessage implements Runnable {
OutputStream out = null;
String message = "Message from Client";
int messageCount = 100;
public SendMessage(OutputStream out) {
this.out = out;
}
public void run() {
PrintWriter writer = new PrintWriter(out);
try {
for (int i = 0; i < messageCount; i++) {
String m = message + " " + i;
writer.println(m);
System.out.println("[Client] Message sent: " + m);
}
System.out.println("[Client] Done Sending all the messages");
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
} finally {
cleanup();
}
}
private void cleanup() {
try {
out.close();
}catch(Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
}
public class ReceiveMessage implements Runnable {
InputStream in = null;
String message;
public ReceiveMessage(InputStream in) {
this.in = in;
}
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
try {
while ((message = reader.readLine()) != null) {
System.out.println("[Client] Received message from Server: "
+ message);
}
System.out.println("[Client] Done Receiving messages from Server");
} catch (Exception e) {
e.printStackTrace();
} finally {
cleanup();
}
}
private void cleanup() {
try {
in.close();
}catch(Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
}
答案 0 :(得分:1)
在此上下文中,Asynchronous
可能并不意味着您不能使用println,而是客户端必须能够在发送新消息时接收消息。
客户端应创建套接字,然后创建两个线程,一个用于发送消息,另一个用于重新接收和打印它们。
<强>更新强>
要避免异常,请使用clientSocket.shutdownOutput()而不是关闭输出流。 您可以将发送代码移回主线程,并为 receive 代码保留一个单独的线程,或在加入发送后调用shutdownOutput()线。无论什么对你有用。
答案 1 :(得分:0)
为每个客户端使用单独的线程。当你写一些东西时,在服务器端,必须有一个接受字符串的方法。否则会阻塞。粘贴您的服务器代码。
答案 2 :(得分:0)
众所周知的端口是专门为特定协议指定的端口号,例如80表示HTTP,443表示HTTPS。您是否打算实施特定协议?如果你是我建议你使用该协议的端口号。维基百科有一个众所周知的端口号列表: http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
答案 3 :(得分:0)
如果这是专业作业(而不是一些家庭作业),那么我强烈推荐Netty Server,它基本上是一个NIO客户端服务器框架。它大大简化/简化了这种开发。
请务必检查他们的documentation,因为它提供了完全实现问题中所述服务器/客户端功能的示例。
如果这是一个家庭作业,那么this example应该提供所有必要的细节。另请查看Oracle resources。