VirtualBox主机与虚拟机TCP通信连接失败问题求助
Windows主机Java TCP服务器与Ubuntu虚拟机C客户端连接失败排查方案
问题背景
在Windows 10主机上编写Java TCP服务器,Ubuntu 22.04虚拟机上编写C客户端,服务器负责接收表达式并计算返回结果,客户端发送表达式并接收结果。已配置VirtualBox Host-Only网络:主机IPv4为192.168.56.1,虚拟机IPv4为192.168.56.101,子网掩码均为255.255.255.0。
运行后服务器仅打印初始化信息并停留在等待状态,客户端无输出、未建立连接,Wireshark捕获到TCP包但连接未完成。
服务器Java代码
package server; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class TcpServer { private static final int SERVER_PORT = 8080; private static ServerSocket serverSocket; public static void main(String[] args) { // Initialize server socket try { // 明确绑定到Host-Only网卡地址,避免多网卡环境下的绑定问题 serverSocket = new ServerSocket(SERVER_PORT, 5, InetAddress.getByName("192.168.56.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); // 端口绑定失败后直接退出,避免无效等待 } System.out.println("created server socket"); System.out.println("IPv4 address: " + serverSocket.getInetAddress()); System.out.println("port number: " + serverSocket.getLocalPort()); try { System.out.println("waiting"); Socket clientSocket = serverSocket.accept(); System.out.println("connected"); process(clientSocket); } catch (IOException e) { e.printStackTrace(); } finally { try { if (serverSocket != null) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } private static void process(Socket clientSocket) { try ( InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); ) { String expression = bufferedReader.readLine(); System.out.println("expression: " + expression); String result = calculate(expression); System.out.println("answer: " + result); bufferedWriter.write(result); bufferedWriter.flush(); System.out.println("sended result"); } catch (IOException e) { e.printStackTrace(); } } private static String calculate(String expression) { ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); try { // 统一转换为字符串,避免非String类型结果导致的类型转换异常 return String.valueOf(scriptEngine.eval(expression)); } catch (ScriptException e) { e.printStackTrace(); return "Calculation error"; // 返回明确的错误信息 } } }
客户端C代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> void error_handling(char *message); int main(int argc, char* argv[]) { int sock; struct sockaddr_in serv_addr; char result[1024] = {'\0'}; int str_len; if (argc != 3) { printf("Usage : %s <IP> <port> \n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { error_handling("socket() error"); } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(argv[1]); serv_addr.sin_port = htons(atoi(argv[2])); if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) { error_handling("connect() error!"); } printf("connected\n"); // 添加换行,确保输出立即刷新显示 char expr[1024]; printf("enter expression.\n"); fgets(expr, 1024, stdin); // 去除fgets读取的换行符,避免服务器readLine读到空内容 expr[strcspn(expr, "\n")] = '\0'; // 发送实际表达式长度,而非固定1024字节 if (send(sock, expr, strlen(expr), 0) == -1) { error_handling("send() error"); } str_len = recv(sock, result, 1023, 0); // 预留1字节给字符串结束符 if (str_len == -1) { error_handling("recv() error!"); } result[str_len] = '\0'; // 手动添加字符串结束符,避免输出乱码 printf("Message from server: %s \n", result); close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
排查与解决步骤
1. 检查Windows防火墙规则
- 打开Windows Defender防火墙,添加入站规则,允许
192.168.56.0/24网段访问8080端口的TCP连接; - 可临时关闭防火墙测试,确认是否是防火墙拦截导致连接失败。
2. 验证网络连通性
- 在Ubuntu虚拟机中执行
ping 192.168.56.1,确认Host-Only网络链路正常; - 使用
telnet 192.168.56.1 8080测试端口可达性,若telnet无法连接,说明端口未开放或网络存在问题。
3. 确认客户端启动参数
- 运行客户端时必须传入主机的Host-Only地址和端口,命令为:
./client 192.168.56.1 8080 - 禁止使用
localhost或虚拟机自身地址作为连接目标。
4. 分析Wireshark捕获的TCP包
- 若客户端发送SYN包后无SYN-ACK回复:说明服务器未接收到请求,大概率是防火墙拦截或服务器端口绑定错误;
- 若有SYN-ACK回复但客户端未发送ACK:检查Ubuntu虚拟机的防火墙规则,确保出站TCP连接被允许。
5. 修正代码潜在问题
- 服务器:明确绑定Host-Only网卡地址,添加端口绑定失败的退出逻辑,修正计算结果的类型转换;
- 客户端:发送实际表达式长度,去除输入的换行符,添加字符串结束符,确保输出实时显示。
内容的提问来源于stack exchange,提问作者KimOneChan
相关产品推荐
相关产品推荐

