You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:54:56