一个客户端在发送消息后应该如何正确地等待服务器的响应呢?

huangapple go评论73阅读模式
英文:

How should a client properly wait for a reponse from the server after a message has been sent?

问题

以下是翻译好的内容:

我正在尝试创建一个能够正确回显消息的客户端和服务器。

这意味着客户端应该向服务器发送一条消息,服务器应该接收并将相同的消息发送回客户端,然后客户端将打印出接收到的消息。

我在让客户端在服务器完成发送响应之前等待的过程中遇到了问题,客户端随后会接收它并将其打印出来。

唯一它有点工作的方式是当我注释掉 EchoClient2.java 中的接收消息代码。

EchoClient2.java

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class EchoClient2 {
    // ... (略去其余部分)
    
    private void sendMessage(String message) throws IOException {
        // 发送消息
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);

        System.out.println("客户端将发送文本:" + message);

        out.write(messageBytes);
        out.flush();
        
        // 接收消息 - 不给服务器发送响应的机会?
        byte[] data = new byte[8];
        ByteArrayOutputStream getData = new ByteArrayOutputStream();
        int numBytes;
        while ((numBytes = in.read(data)) != -1) { 
            getData.write(data, 0, numBytes);
        }
        
        byte[] message2Bytes = getData.toByteArray();
        String text = new String(message2Bytes, StandardCharsets.UTF_8);
        
        System.out.println("服务器发送给客户端的文本:" + text);
    }
    
    // ... (略去其余部分)
}

EchoServer2.java

import java.net.*;
import java.nio.charset.StandardCharsets;
import java.io.*;

public class EchoServer2 {
    // ... (略去其余部分)
    
    private void actionMessage() throws IOException {
        // 接收消息
        byte[] data = new byte[8];
        ByteArrayOutputStream getData = new ByteArrayOutputStream();
        int numBytes;
        while ((numBytes = in.read(data)) != -1) { 
            getData.write(data, 0, numBytes);
        }

        byte[] messageBytes = getData.toByteArray();
        String text = new String(messageBytes, StandardCharsets.UTF_8);
        
        System.out.println("服务器收到:" + text + ",将发送回客户端");
        
        // 发送回相同的消息
        out.write(messageBytes);
        out.flush();
    }
    
    // ... (略去其余部分)
}

是否有人知道为什么这个不正常工作?

英文:

I am trying to make a client and server properly echo messages.

This means the client should send a message to the server, the server should receive and send the same message back to the client, and then the client prints out the received message.

I am having trouble making the client properly wait for the server to finish sending the response before the client then receives it and prints it out.

The only way it sort of works is when I comment out the EchoClient2.java RECEIVE MESSAGE code.

EchoClient2.java

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class EchoClient2 {
	private Socket clientSocket;
	private DataOutputStream out;
	private DataInputStream in;

	private void start(String ip, int port) {
		try {
			clientSocket = new Socket(ip, port);
			out = new DataOutputStream(clientSocket.getOutputStream());
			in = new DataInputStream(clientSocket.getInputStream());
		} catch (IOException e) {
			System.out.println("Error when initializing connection");
		}
	}

	private void sendMessage(String message) throws IOException {
		//SENDING MESSAGE
		byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);

		System.out.println("Client will send the text: " + message);

		out.write(messageBytes);
		out.flush();
	
		//RECEIEVE MESSAGE - Doesn't give server a chance to respond?
		byte[] data = new byte[8];
		ByteArrayOutputStream getData = new ByteArrayOutputStream();
		int numBytes;
		while ((numBytes = in.read(data)) != -1) { 
			getData.write(data, 0, numBytes);
		}
		
		byte[] message2Bytes = getData.toByteArray();
		String text = new String(message2Bytes, StandardCharsets.UTF_8);
		
		System.out.println("Server sent the client text: " + text);
	}

	private void stop() {
		try {
			in.close();
			out.close();
			clientSocket.close();
		} catch (IOException e) {
			System.out.println("error when closing");
		}
	}

	public static void main(String[] args) throws IOException {
		EchoClient2 client = new EchoClient2();
		client.start("127.0.0.1", 4444);
		client.sendMessage("exit");
		client.stop();
	}
}

EchoServer2.java

import java.net.*;
import java.nio.charset.StandardCharsets;
import java.io.*;

public class EchoServer2 {
	private ServerSocket serverSocket;
	private Socket clientSocket;
	private DataOutputStream out;
	private DataInputStream in;

	public void start(int port) {
		try {
			serverSocket = new ServerSocket(port);
			clientSocket = serverSocket.accept();
			out = new DataOutputStream(clientSocket.getOutputStream());
			in = new DataInputStream(clientSocket.getInputStream());   
		} catch (IOException e) {
			System.out.println("Error when establishing/accepting server connection");
		}
	}

	private void actionMessage() throws IOException {
		//RECEIEVE MESSAGE
		byte[] data = new byte[8];
		ByteArrayOutputStream getData = new ByteArrayOutputStream();
		int numBytes;
		while ((numBytes = in.read(data)) != -1) { 
			getData.write(data, 0, numBytes);
		}

		byte[] messageBytes = getData.toByteArray();
		String text = new String(messageBytes, StandardCharsets.UTF_8);
		
		System.out.println("Server got " + text + " and will send this back to client");

		//SENDING BACK SAME MESSAGE
		out.write(messageBytes);
		out.flush();
	}

	public void stop() {
		try {
			in.close();
			out.close();
			clientSocket.close();
			serverSocket.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}

	public static void main(String[] args) throws IOException {
		EchoServer2 server = new EchoServer2();

		System.out.println("Now waiting for incoming connection...");

		server.start(4444);

		server.actionMessage();
		
		server.stop();
	}
}

Would anyone know why this is not working properly?

答案1

得分: 1

服务器正在以8字节的块读取客户端的输入,直到客户端断开连接(但实际上客户端尚未断开连接),然后才会写回响应。服务器需要改为在每个块到达时立即写回,然后在客户端断开连接后断开连接。这意味着将 out.write() 移动到 while 读取循环内部。

private void actionMessage() throws IOException {
	byte[] data = new byte[8];
	int numBytes;
	while ((numBytes = in.read(data)) != -1) {
		out.write(data, 0, numBytes);
		out.flush();
	}
} 

这对于简单的回显服务器来说是可以的,但对于更实际的消息服务器来说就不太适用。您需要一个更明确的协议,在不必在它们之间失去连接的情况下来回交换消息(除非您想实现无状态的协议,如HTTP,它允许在请求之间断开连接)。例如:

客户端:

private void sendMessage(String message) throws IOException {
	System.out.println("客户端将发送文本:" + message);

	// 发送消息
	byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
	out.writeInt(messageBytes.length);
	out.write(messageBytes);
	out.flush();

	// 接收消息
	int len = in.readInt();
	byte[] data = new byte[len];
	in.readFully(data);
	String text = new String(data, StandardCharsets.UTF_8);
	System.out.println("服务器发送给客户端的文本:" + text);
}

public static void main(String[] args) throws IOException {
	EchoClient2 client = new EchoClient2();
	client.start("127.0.0.1", 4444);
	try {
		client.sendMessage("你好");
		client.sendMessage("退出");
	} catch (IOException e) {
		...
	}
	client.stop();
}

服务器:

private void actionMessage() throws IOException {
	// 接收消息
	int len = in.readInt();
	byte[] data = new byte[len];
	in.readFully(data);
	String text = new String(data, StandardCharsets.UTF_8);
	System.out.println("服务器收到 " + text + " 并将其发送回客户端");

	// 发送消息
	data = text.getBytes(StandardCharsets.UTF_8);
	out.writeInt(data.length);
	out.write(data);
	out.flush();
}

public static void main(String[] args) throws IOException {
	EchoServer2 server = new EchoServer2();
	System.out.println("现在等待传入连接...");
	server.start(4444);
	while (true) {
		try {
			server.actionMessage();
		} catch (IOException e) {
			...
			break;
		}
	}
	server.stop();
}
英文:

The server is reading the client’s input in 8-byte chunks until the client disconnects (which it is not doing), THEN it is writing a response back. The server needs to instead write back each chunk as it arrives, then disconnect after the client has disconnected. That means moving out.write() inside the while reading loop.

private void actionMessage() throws IOException {
	byte[] data = new byte[8];
	int numBytes;
	while ((numBytes = in.read(data)) != -1) {
		out.write(data, 0, numBytes);
		out.flush();
	}
} 

This is fine for a simple echo server, but it is not very useful for a more realistic messaging server. You need a more defined protocol to exchange messages back and forth without having to lose the connection in between them (unless you want to implement a stateless protocol, like HTTP, which allows disconnects between requests). For example:

Client:

private void sendMessage(String message) throws IOException {
	System.out.println("Client will send the text: " + message);

	//SEND MESSAGE
	byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
	out.writeInt(messageBytes.length);
	out.write(messageBytes);
	out.flush();

	//RECEIVE MESSAGE
	int len = in.readInt();
	byte[] data = new byte[len];
	in.readFully(data);
	String text = new String(data, StandardCharsets.UTF_8);
	System.out.println("Server sent the client text: " + text);
}

public static void main(String[] args) throws IOException {
	EchoClient2 client = new EchoClient2();
	client.start("127.0.0.1", 4444);
	try {
		client.sendMessage("hello");
		client.sendMessage("exit");
	} catch (IOException e) {
		...
	}
	client.stop();
}

Server:

private void actionMessage() throws IOException {
	//RECEIVE MESSAGE
	int len = in.readInt();
	byte[] data = new byte[len];
	in.readFully(data);
	String text = new String(data, StandardCharsets.UTF_8);
	System.out.println("Server got " + text + " and will send this back to client");

	//SEND MESSAGE
	data = text.getBytes(StandardCharsets.UTF_8);
	out.writeInt(data.length);
	out.write(data);
	out.flush();
}

public static void main(String[] args) throws IOException {
	EchoServer2 server = new EchoServer2();
	System.out.println("Now waiting for incoming connection...");
	server.start(4444);
	while (true) {
		try {
			server.actionMessage();
		} catch (IOException e) {
			...
			break;
		}
	}
	server.stop();
}

huangapple
  • 本文由 发表于 2020年10月7日 11:56:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/64236965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定