4. 实现QNX与Android的Socket通信

任务说明

在QNX侧创建TCP Server,Android侧创建TCP Client,通过局域网建立连接并实现双向字符串消息传递。QNX侧监听指定端口,Android侧主动连接后,双方均可发送消息,对端实时接收并打印。

环境与依赖

  • QNX侧:QNX SDP 7.0+,支持标准POSIX Socket API,无需额外库
  • Android侧:Android Studio,minSdk 21+,需在Manifest声明INTERNET权限
  • 网络:QNX设备与Android设备处于同一局域网,QNX侧IP需预先获知(示例用192.168.1.100)

QNX侧实现(TCP Server)

// qnx_tcp_server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8888
#define BUFSIZE 256

int main() {
    int server_fd, client_fd;
    struct sockaddr_in addr;
    char buf[BUFSIZE];

    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) { perror("socket"); return 1; }

    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind"); return 1;
    }
    if (listen(server_fd, 1) < 0) { perror("listen"); return 1; }
    printf("QNX Server listening on port %d...\n", PORT);

    client_fd = accept(server_fd, NULL, NULL);
    if (client_fd < 0) { perror("accept"); return 1; }
    printf("Android client connected.\n");

    // 双向通信循环:先收后发
    while (1) {
        memset(buf, 0, BUFSIZE);
        int n = recv(client_fd, buf, BUFSIZE - 1, 0);
        if (n <= 0) { printf("Connection closed.\n"); break; }
        printf("Received from Android: %s\n", buf);

        printf("QNX reply: ");
        fgets(buf, BUFSIZE, stdin);
        buf[strcspn(buf, "\n")] = 0;
        if (send(client_fd, buf, strlen(buf), 0) < 0) { perror("send"); break; }
    }

    close(client_fd);
    close(server_fd);
    return 0;
}

编译:qcc -o qnx_server qnx_tcp_server.c,运行:./qnx_server。代码先阻塞等待Android连接,连接建立后进入收发循环。

Android侧实现(TCP Client)

// MainActivity.java 关键部分
public class MainActivity extends AppCompatActivity {
    private Socket socket;
    private PrintWriter out;
    private BufferedReader in;
    private TextView tvStatus, tvReceived;
    private EditText etMessage;
    private static final String SERVER_IP = "192.168.1.100";
    private static final int SERVER_PORT = 8888;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvStatus = findViewById(R.id.tvStatus);
        tvReceived = findViewById(R.id.tvReceived);
        etMessage = findViewById(R.id.etMessage);

        new Thread(this::connectServer).start();

        findViewById(R.id.btnSend).setOnClickListener(v -> {
            String msg = etMessage.getText().toString();
            if (!msg.isEmpty() && out != null) {
                new Thread(() -> out.println(msg)).start();
                etMessage.setText("");
            }
        });
    }

    private void connectServer() {
        try {
            socket = new Socket(SERVER_IP, SERVER_PORT);
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            runOnUiThread(() -> tvStatus.setText("Connected to QNX"));

            String line;
            while ((line = in.readLine()) != null) {
                String finalLine = line;
                runOnUiThread(() -> tvReceived.append("QNX: " + finalLine + "\n"));
            }
        } catch (IOException e) {
            runOnUiThread(() -> tvStatus.setText("Error: " + e.getMessage()));
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try { if (socket != null) socket.close(); } catch (IOException ignored) {}
    }
}

布局文件需包含tvStatus、tvReceived、etMessage、btnSend四个控件。Manifest中添加<uses-permission android:name="android.permission.INTERNET"/>。

验证方法

  1. 在QNX终端运行./qnx_server,看到监听日志
  2. 启动Android App,观察状态栏显示“Connected to QNX”
  3. 在Android输入框发送“Hello QNX”,QNX终端应打印Received from Android: Hello QNX
  4. 在QNX终端输入“Hello Android”并回车,Android的tvReceived区域应显示QNX: Hello Android
  5. 双向各发送一条消息且对端正确显示,即判定通信成功

常见问题

  • 连接失败:确认QNX IP正确、防火墙未拦截8888端口、两端在同一网段
  • Android收不到消息:QNX侧send后需加\n换行符(示例中fgets已去除换行,需手动拼接),否则Android的readLine()会阻塞
  • QNX侧recv返回0:Android端Socket被关闭,属正常断开