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

Java UDP通道服务器向客户端回传数据时遇绑定异常问题

问题描述

首次使用UDP通道实现服务器向客户端回传数据时,遇到以下错误:

  • java.net.BindException: Cannot assign requested address: no further information
    改用write方法发送数据后,又出现新错误:
  • java.net.PortUnreachableException

客户端代码(注:代码中类名为Server,实际为客户端逻辑)

import java.net.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

import javax.swing.JFileChooser;

public class Server {
    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            System.out.printf("\nEscriba el puerto:");
            int pto = Integer.parseInt(br.readLine());
            System.out.printf("\nMTU:");
            int mtu = Integer.parseInt(br.readLine());
            // Crea canal
            DatagramChannel canal = DatagramChannel.open();
            canal.configureBlocking(false);
            // Obtén socket asociado al canal
            DatagramSocket socket = canal.socket();
            // Crea dirección de socket
            SocketAddress dir = new InetSocketAddress(pto);
            // Asocia la dirección del socket al socket
            socket.bind(dir);
            // Crea selector
            Selector selector = Selector.open();
            // Registra selector al canal para escuchar la operación OP_READ
            canal.register(selector, SelectionKey.OP_READ);
            // Crea buffer
            //ByteBuffer buffer = ByteBuffer.allocateDirect(mtu);
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
            int opc=-1;
            while (true) {
                // bloquea el programa hasta que un canal este listo para ejecutar una operacion
                selector.select(5000);// espera 5 segundos por la conexión
                // Obtén las llaves asociadas al selector
                // C:\Users\datre\OneDrive\Documentos\Eclipse\Redes2\archivos
                Set sk = selector.selectedKeys();
                Iterator it = sk.iterator();
                while (it.hasNext()) {
                    SelectionKey key = (SelectionKey) it.next();
                    it.remove();
                    //key.interestOps(SelectionKey.OP_READ);
                    if (key.isWritable()) {
                        System.out.print("It's writable server\n");
                        if(opc == 1) {//Listar archivos
                            //ENVIAR OPC
                            ByteBuffer tamBuffer = ByteBuffer.allocate(4);
                            tamBuffer.asIntBuffer().put(opc);
                            tamBuffer.rewind();
                            SocketAddress client = canal.receive(buffer);
                            canal.send(tamBuffer,dir);
                            //LISTA DE ARCHIVOS
                            System.out.print("Listar archivos\n");
                        } else if(opc == 2) {//Leer archivo
                            //ENV OPC
                            //ENVIAR ARCHIVO
                            System.out.print("Leer archivos\n");
                        }
                        key.interestOps(SelectionKey.OP_READ);
                        //ready (canal,dir);
                        //System.exit(0);
                        //canal.close();
                    } // if
                    else if (key.isReadable()) {
                        System.out.println("Is readable server: getting option");
                        ByteBuffer b = ByteBuffer.allocate(4);
                        b.clear();
                        SocketAddress client = canal.receive(b);
                        b.flip();
                        opc = b.getInt();
                        System.out.println("Opc : " + opc + "\n");
                        key.interestOps(SelectionKey.OP_WRITE);
                    } // if
                } // while2
            } // while
        } catch (IOException e) {
            System.err.println(e);
        } // catch
    }// main
    
    private static void ready(DatagramChannel channel, SocketAddress serverAddress) {
        System.out.println("Sending filename\n");
        try {
            String fileName;

            File f = new File("C:\\Users\\datre\\OneDrive\\Documentos\\IA\\Estacionamiento1.pdf");
            fileName = f.getName();

            ByteBuffer buf = ByteBuffer.allocate(1024);
            buf.clear();
            buf.put(fileName.getBytes());
            buf.flip();

            channel.send(buf,serverAddress); // Sending the packet with the file name
            //channel.write(buf);
            //channel.close();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }
    
    
}//class

服务器代码(注:代码中类名为Client,实际为服务器逻辑)

package main;


import java.net.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileSystemView;

public class Client {
    private final static int LIMITE = 100;

    public static void main(String[] args) {
        boolean bandera = false;

        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            System.out.printf("Escriba la dirección del servidor:");
            String host = br.readLine();
            System.out.printf("\nEscriba el puerto:");
            int pto = Integer.parseInt(br.readLine());
            SocketAddress remoto = new InetSocketAddress(host, pto);
            // Crea canal
            DatagramChannel canal = DatagramChannel.open();
            canal.configureBlocking(false);
            // Conecta el canal al socket
            canal.connect(remoto);
            // Crea selector
            Selector selector = Selector.open();
            // Registra el selector para esperar la operacion de OP_WRITE
            canal.register(selector, SelectionKey.OP_WRITE);
            // Crea un buffer de 4 bytes
            //ByteBuffer buffer = ByteBuffer.allocateDirect(4);
            //Server route
            String serverRoute = "C:\\Users\\datre\\OneDrive\\Documentos\\Eclipse\\TrustworthyUDPChannel"; 
            int o=1;
            while (true) {
                // bloquea el programa hasta que un canal este listo para ejecutar una operacion
                selector.select(5000); // espera 5 segundos por la conexión
                // Obten el conjunto de keys del selector
                Set sk = selector.selectedKeys();
                Iterator it = sk.iterator();
                while (it.hasNext()) {
                    SelectionKey key = (SelectionKey) it.next();
                    it.remove();
                    //key.interestOps(SelectionKey.OP_READ);
                    if (key.isReadable()) {
                        System.out.println("It's readable client");
                        ByteBuffer b = ByteBuffer.allocate(4);
                        b.clear();
                        SocketAddress client = canal.receive(b);
                        b.flip();
                        int opc = b.getInt();
                        System.out.println("Opc : " + opc + "\n");
                        //createFile (canal,serverRoute);
                        //canal.close();
                    } else if (key.isWritable()) {
                        System.out.println("Is writable client: sending option");
                        if(o ==  1)//Listar archivos
                        {
                            ByteBuffer tamBuffer = ByteBuffer.allocate(4);
                            tamBuffer.asIntBuffer().put(1);
                            tamBuffer.rewind();
                            canal.send(tamBuffer,remoto);
                        } else if(o == 2)// Descargar archivo
                        {
                            ByteBuffer tamBuffer = ByteBuffer.allocate(4);
                            tamBuffer.asIntBuffer().put(2);
                            tamBuffer.rewind();
                            canal.send(tamBuffer,remoto);   
                        }
                        key.interestOps(SelectionKey.OP_READ);
                    } 
                } // while
            } // while
        } catch (Exception e) {
            System.err.println(e);
        } // catch
    }// main
    
    public static void createFile (DatagramChannel channel, String serverRoute){
        try{
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            channel.receive(buffer); // Receive the datagram with the name of the file
            System.out.println("Receiving file name");
            buffer.flip();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            String fileName = new String(bytes);
            File f = new File (serverRoute+"\\"+fileName); // Creating the file
            System.out.print(fileName+ "\n" + f + "\n");
            FileOutputStream outToFile = new FileOutputStream(f); // Creating the stream through which we write the file content
            //receiveFile(outToFile, socket); // Receiving the file
        }catch(Exception ex){
            ex.printStackTrace();
            System.exit(1);
        }   
    }
    


}// class

错误原因及修复方案

1. BindException 错误原因与修复

  • 地址绑定问题:代码中new InetSocketAddress(pto)仅指定端口,未明确绑定IP,可能导致系统尝试绑定到不可用的网络接口。需显式指定绑定到所有可用接口:
    SocketAddress dir = new InetSocketAddress("0.0.0.0", pto);
    
  • 端口占用:输入的端口可能已被其他程序占用,可更换端口或通过系统命令查看端口占用情况。

2. PortUnreachableException 错误原因与修复

  • 回传地址错误:服务器代码中canal.send(tamBuffer,dir)使用自身绑定地址作为目标,而非客户端地址。UDP是无连接协议,必须用receive方法返回的客户端地址回传数据。
  • 已连接通道的接收问题:客户端调用canal.connect(remoto)后,会过滤非指定服务器的数据包,若服务器回传地址错误,客户端无法接收,触发端口不可达错误。

关键修复代码

服务器端(保存客户端地址并正确回传)

// 全局变量保存客户端地址
private static SocketAddress clientAddr = null;

// 在可读分支中保存客户端地址
else if (key.isReadable()) {
    System.out.println("Is readable server: getting option");
    ByteBuffer b = ByteBuffer.allocate(4);
    b.clear();
    clientAddr = canal.receive(b); // 保存客户端地址
    b.flip();
    opc = b.getInt();
    System.out.println("Opc : " + opc + "\n");
    key.interestOps(SelectionKey.OP_WRITE);
}

// 在可写分支中使用客户端地址回传
if (key.isWritable()) {
    System.out.print("It's writable server\n");
    if(opc == 1 && clientAddr != null) {//Listar archivos
        ByteBuffer tamBuffer = ByteBuffer.allocate(4);
        tamBuffer.asIntBuffer().put(opc);
        tamBuffer.rewind();
        canal.send(tamBuffer, clientAddr); // 使用客户端地址回传
        System.out.print("Listar archivos\n");
    } 
    key.interestOps(SelectionKey.OP_READ);
}

客户端(简化已连接通道的读取逻辑)

客户端已连接到服务器,可直接用read方法替代receive:

if (key.isReadable()) {
    System.out.println("It's readable client");
    ByteBuffer b = ByteBuffer.allocate(4);
    b.clear();
    canal.read(b); // 已连接通道直接读取
    b.flip();
    int opc = b.getInt();
    System.out.println("Opc : " + opc + "\n");
}

内容的提问来源于stack exchange,提问作者Omar Daniel González Trejo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:15:38