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
相关产品推荐
相关产品推荐

