Java客户端与服务器通信异常:初始数据接收失败求助
问题描述
我明确知道自己存在一处明显错误,但始终无法定位。我正在实现一个客户端(Alice)向服务器(Bob)发送自身identity与nonce的功能,服务器读取后应返回自身nonce,以及经加密的服务器identity;客户端接收后,再向服务器发送自身加密后的identity供其解密。目前遇到的问题是:服务器无法接收客户端发送的初始输入(加密操作前的数据),已确认客户端与服务器连接正常,但服务器无法读取输入。
原始代码
服务器Bob代码
import javax.crypto.*; import javax.crypto.spec.*; import java.io.*; import java.net.*; import java.security.*; import java.util.Arrays; public class Bob { public static void main(String[] args) throws Exception { String Bobidentity = "Bob"; // Start listening for incoming connections int portNumber = 1500; ServerSocket serverSocket = new ServerSocket(portNumber); System.out.println("Bob waiting for Alice..."); // Accept Alice's connection Socket socket = serverSocket.accept(); System.out.println("Alice connected."); // Get input/output streams ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); // Receive identity and nonce from Alice String aliceIdentity = (String) in.readObject(); int aliceNonce = in.readInt(); System.out.println("Received Alice Identity (1) " + aliceIdentity); System.out.println("Received Alice Nonce (1) " + aliceNonce); int bobNonce = 3; // Send nonce to Alice out.writeInt(bobNonce); // Generate shared secret key using both nonces byte[] sharedSecretBytes = (Integer.toString(bobNonce) + Integer.toString(aliceNonce)).getBytes(); SecretKey secretKey = new SecretKeySpec(sharedSecretBytes, "DES"); // Encrypt and send message to Alice containing Bob's identity and nonce NA byte[] messageToAlice = (Bobidentity + "," + aliceNonce).getBytes(); Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedMessage = cipher.doFinal(messageToAlice); out.writeObject(encryptedMessage); // Receive encrypted message from Alice containing Alice's identity and nonce NB byte[] encryptedResponse = (byte[]) in.readObject(); System.out.println("Encrypted message from Alice (3) " + Arrays.toString(encryptedResponse)); byte[] decryptedResponse = cipher.doFinal(encryptedResponse); String[] parts = new String(decryptedResponse).split(","); String aliceIdentityReceived = parts[0]; int receivedNonce = Integer.parseInt(parts[1]); System.out.println("Decrypted Identity from Alice (3) " + aliceIdentityReceived); System.out.println("Decrypted Nonce from Alice (3) " + receivedNonce); // Close the connection socket.close(); serverSocket.close(); } }
客户端Alice代码
import javax.crypto.*; import javax.crypto.spec.*; import java.io.*; import java.net.*; import java.security.*; import java.util.Arrays; public class Alice { public static void main(String[] args) throws Exception { // Connect to Bob String hostName = "localhost"; int portNumber = 1500; //Socket socket = new Socket("localhost", 12345); try ( Socket socket = new Socket(hostName, portNumber); ) { ObjectOutputStream outt = new ObjectOutputStream(socket.getOutputStream()); int nonce = 5; String identity = "Alice"; // Send identity and nonce to Bob outt.writeObject(identity); outt.writeInt(nonce); // Receive nonce and encrypted message from Bob ObjectInputStream inn = new ObjectInputStream(socket.getInputStream()); int receivedNonce = inn.readInt(); byte[] encryptedMessage = (byte[]) inn.readObject(); System.out.println("Encrypted message from Bob (2) " + Arrays.toString(encryptedMessage)); System.out.println("Bobs Nonce (2) " + receivedNonce); // Generate shared secret key using both nonces byte[] sharedSecretBytes = (Integer.toString(nonce) + Integer.toString(receivedNonce)).getBytes(); SecretKey secretKey = new SecretKeySpec(sharedSecretBytes, "DES"); // Decrypt and extract Bob's identity and nonce NA Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedMessage = cipher.doFinal(encryptedMessage); String[] parts = new String(decryptedMessage).split(","); String bobIdentity = parts[0]; int bobNonce = Integer.parseInt(parts[1]); System.out.println("Decrypted Bobs identity (2) " + bobIdentity); System.out.println("Decrypted nonce (2) " + bobNonce); // Send encrypted message to Bob containing Alice's identity and nonce NB byte[] messageToBob = (identity + "," + receivedNonce).getBytes(); byte[] encryptedResponse = cipher.doFinal(messageToBob); outt.writeObject(encryptedResponse); // Close the connection } catch (UnknownHostException e) { System.err.println("Don't know about host " + hostName); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for the connection to " + hostName); System.exit(1); } } }
问题定位与修复
核心问题:输出流未刷新,数据滞留在缓冲区
客户端Alice发送identity和nonce后,ObjectOutputStream的缓冲区没有被强制刷新,导致数据没有实际发送到服务器,服务器的in.readObject()一直处于阻塞等待状态。
此外,双方生成共享密钥的顺序不一致,会导致后续解密失败,这也是需要修正的潜在问题。
修复步骤
1. 客户端添加流刷新操作
在客户端Alice发送完初始的identity和nonce后,调用flush()强制缓冲区数据发送:
// Send identity and nonce to Bob outt.writeObject(identity); outt.writeInt(nonce); outt.flush(); // 新增:确保数据立即发送到服务器
2. 统一密钥生成顺序
服务器Bob原本的密钥生成是Bob的nonce + Alice的nonce,而客户端是Alice的nonce + Bob的nonce,两者顺序相反,导致密钥不一致。修改服务器的密钥生成代码,统一为Alice的nonce在前,Bob的nonce在后:
// 服务器Bob修改后的密钥生成代码 byte[] sharedSecretBytes = (Integer.toString(aliceNonce) + Integer.toString(bobNonce)).getBytes();
3. 修正Cipher模式切换问题
客户端在使用Cipher进行解密后,需要重新初始化为加密模式才能进行加密操作,否则会抛出异常:
// 客户端Alice发送加密消息前,重新初始化Cipher为加密模式 cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedResponse = cipher.doFinal(messageToBob);
完整修复后的代码
修复版客户端Alice
import javax.crypto.*; import javax.crypto.spec.*; import java.io.*; import java.net.*; import java.security.*; import java.util.Arrays; public class Alice { public static void main(String[] args) throws Exception { String hostName = "localhost"; int portNumber = 1500; try (Socket socket = new Socket(hostName, portNumber)) { ObjectOutputStream outt = new ObjectOutputStream(socket.getOutputStream()); int nonce = 5; String identity = "Alice"; // Send identity and nonce to Bob outt.writeObject(identity); outt.writeInt(nonce); outt.flush(); // 刷新缓冲区,确保数据发送 // Receive nonce and encrypted message from Bob ObjectInputStream inn = new ObjectInputStream(socket.getInputStream()); int receivedNonce = inn.readInt(); byte[] encryptedMessage = (byte[]) inn.readObject(); System.out.println("Encrypted message from Bob (2) " + Arrays.toString(encryptedMessage)); System.out.println("Bob's Nonce (2) " + receivedNonce); // Generate shared secret key using both nonces byte[] sharedSecretBytes = (Integer.toString(nonce) + Integer.toString(receivedNonce)).getBytes(); SecretKey secretKey = new SecretKeySpec(sharedSecretBytes, "DES"); // Decrypt and extract Bob's identity and nonce NA Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedMessage = cipher.doFinal(encryptedMessage); String[] parts = new String(decryptedMessage).split(","); String bobIdentity = parts[0]; int bobNonce = Integer.parseInt(parts[1]); System.out.println("Decrypted Bob's identity (2) " + bobIdentity); System.out.println("Decrypted nonce (2) " + bobNonce); // Send encrypted message to Bob containing Alice's identity and nonce NB byte[] messageToBob = (identity + "," + receivedNonce).getBytes(); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 重新初始化为加密模式 byte[] encryptedResponse = cipher.doFinal(messageToBob); outt.writeObject(encryptedResponse); outt.flush(); } catch (UnknownHostException e) { System.err.println("无法识别主机 " + hostName); System.exit(1); } catch (IOException e) { System.err.println("无法建立与 " + hostName + " 的连接I/O"); System.exit(1); } catch (Exception e) { e.printStackTrace(); } } }
修复版服务器Bob
import javax.crypto.*; import javax.crypto.spec.*; import java.io.*; import java.net.*; import java.security.*; import java.util.Arrays; public class Bob { public static void main(String[] args) throws Exception { String Bobidentity = "Bob"; int portNumber = 1500; ServerSocket serverSocket = new ServerSocket(portNumber); System.out.println("Bob等待Alice连接..."); Socket socket = serverSocket.accept(); System.out.println("Alice已连接。"); ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); // Receive identity and nonce from Alice String aliceIdentity = (String) in.readObject(); int aliceNonce = in.readInt(); System.out.println("收到Alice的身份 (1) " + aliceIdentity); System.out.println("收到Alice的Nonce (1) " + aliceNonce); int bobNonce = 3; // Send nonce to Alice out.writeInt(bobNonce); out.flush(); // Generate shared secret key using both nonces - 统一顺序 byte[] sharedSecretBytes = (Integer.toString(aliceNonce) + Integer.toString(bobNonce)).getBytes(); SecretKey secretKey = new SecretKeySpec(sharedSecretBytes, "DES"); // Encrypt and send message to Alice containing Bob's identity and nonce NA byte[] messageToAlice = (Bobidentity + "," + aliceNonce).getBytes(); Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedMessage = cipher.doFinal(messageToAlice); out.writeObject(encryptedMessage); out.flush(); // Receive encrypted message from Alice containing Alice's identity and nonce NB byte[] encryptedResponse = (byte[]) in.readObject(); System.out.println("收到Alice的加密消息 (3) " + Arrays.toString(encryptedResponse)); // 初始化解密模式 cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedResponse = cipher.doFinal(encryptedResponse); String[] parts = new String(decryptedResponse).split(","); String aliceIdentityReceived = parts[0]; int receivedNonce = Integer.parseInt(parts[1]); System.out.println("解密后的Alice身份 (3) " + aliceIdentityReceived); System.out.println("解密后的Nonce (3) " + receivedNonce); // Close the connection socket.close(); serverSocket.close(); } }
内容的提问来源于stack exchange,提问作者Fabiano Morra
相关产品推荐
相关产品推荐

