Lxjiesun 2015-09-20 07:02 采纳率: 0%
浏览 2094

java 关于NIO实现UDP数据传输问题 ,急谢谢,C币不足请不要介意

各位大侠好,小弟想问一下问题,搞了一两天没有搞明白的。因为要实现一个UDP传输服务端,于是在网上找了很多资料然后就写了一个。但是写好之后发现有两个很严重的问题,希望各位大哥给点意见或者思路去解决。
问题一:启动服务端,同时也启动客户端,客户端传输数据服务器正常接收,但是断开客户端后,再启动客户端,服务器就收不到任何客户端发送的消息,好像是服务器关闭了UDP一样,但是重启服务器后(重新打开UDP)客户端既可以发送信息过来。
问题二:多个客户端。第一个客户端连接上后,第二个客户端怎么也链接不上了。即使关闭了第一个客户端也一样。
如下是代码:
package com.gateway.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;

public class UDPEchoServerSelector extends Thread {

private static final Logger log = Logger.getLogger(ServerSocket.class);

//
private InetSocketAddress inetSocketAddress;

// socket处理类
private UDPSocketHandler handler = new UDPSocketHandler();

// 注册的接受服务
private SocketReceiver receiver = null;

/**
 * 初始化socket
 * 
 * @param receiver
 * @param hostname
 * @param port
 * @throws IOException
 * @throws UnknownHostException
 */
public UDPEchoServerSelector(SocketReceiver receiver, String hostname,
        int port) {
    if (hostname.isEmpty()) {
        inetSocketAddress = new InetSocketAddress(port);
    } else {
        inetSocketAddress = new InetSocketAddress(hostname, port);
    }

    this.receiver = receiver;
}

@Override
public void run() {
    try {

        Selector selector = Selector.open(); // 创建选择器,可以处理多路通道。

        DatagramChannel serverSocketChannel = DatagramChannel.open(); // 打开通道

        serverSocketChannel.configureBlocking(false); // 非阻塞

        serverSocketChannel.socket().bind(inetSocketAddress);
        /*
         * 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_READ事件,注册该事件后,
         * 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
         */
        serverSocketChannel.register(selector, SelectionKey.OP_READ,
                new ClientData());

        log.info("Server: socket server started.");
        /*
         * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
         */
        while (true) { // 轮询

            // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
            int nKeys = selector.select();

            if (nKeys == 0) {
                continue;
            }

            // 得到选择键列表
            Set Keys = selector.selectedKeys();
            Iterator it = Keys.iterator();

            while (it.hasNext()) {

                SelectionKey key = null;

                key = (SelectionKey) it.next(); // 键为位掩码
                it.remove();
                // 客户端请求连接事件
                if (key.isValid() && key.isWritable()) {
                    log.info("Server: SelectionKey is acceptable.");
                    handler.handleWrite(key);
                }
                if (key.isReadable()) {// 获得了可读的事件
                    log.info("Server: SelectionKey is readable.");
                    handler.receiveMsg(key, receiver);

                }
            }

            Keys.clear();
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static class ClientData {
    public SocketAddress clientAddress;
    public ByteBuffer buffer = ByteBuffer.allocate(255);
}

}

package com.gateway.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import org.apache.log4j.Logger;
import com.gateway.common.DeviceDataTools;
import com.gateway.common.data.HexUtils;
import com.gateway.socket.UDPEchoServerSelector.ClientData;

/**

  • 处理socket类
  • @author Andy
  • */
    public class UDPSocketHandler {

    private static Logger log = Logger.getLogger(UDPSocketHandler.class);

    /**

    • 链接请求
    • @throws IOException
      */
      public void handleWrite(SelectionKey key) {
      try {
      DatagramChannel channel = (DatagramChannel) key.channel();
      ClientData clntDat = (ClientData) key.attachment();
      clntDat.buffer.flip(); // 从起始位置开始发送
      int bytesSent;

      bytesSent = channel.send(clntDat.buffer, clntDat.clientAddress);
      if (bytesSent != 0) {
          key.interestOps(SelectionKey.OP_READ); // 关注客户端发送数据
      }
      

      } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }

    }

    /**

    • 读请求
    • @throws IOException
      */
      public void receiveMsg(SelectionKey key, SocketReceiver receiver) {

      ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
      byteBuffer.clear();
      DatagramChannel socketChannel = (DatagramChannel) key.channel();

      //非阻塞
      try {
      socketChannel.configureBlocking(false);
      } catch (IOException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
      }

      System.out.println("channel code:" + socketChannel.hashCode());

      try {
      while (true) {

          InetSocketAddress client = (InetSocketAddress) socketChannel
                  .receive(byteBuffer);
      
          byteBuffer.flip();
      
          // byteBuffer中传过来的是10进制的bytep[]
          byte[] dst = new byte[byteBuffer.limit()];
      
          byteBuffer.get(dst);
      
          // 将10进制的byte[]转化成16进制字符串
          String data = HexUtils.converBytesToHex(dst);
      
          System.out.println(data);
      
          log.info("Server: data1 = " + data);
      
          byteBuffer.clear();
      
          receiver.udpreceive(socketChannel, data, client);
      
          break;
      }
      

      } catch (java.io.IOException e) {
      //e.printStackTrace();
      //this.closeChannel(key, socketChannel);
      } catch (Exception e) {
      //e.printStackTrace();
      //this.closeChannel(key, socketChannel);
      }

    }

    /**

    • @param socketChannel */ private void closeChannel(SelectionKey key, DatagramChannel socketChannel) { try { while (socketChannel.isOpen()) { key.cancel(); socketChannel.close(); } } catch (IOException e1) { e1.printStackTrace(); } }

    /**

    • 根据socketKey从内存中获取channel,通过channel向client端发送消息
    • @param socketKey
    • 内存在channel对应的key
    • @param data
    • 发送的数据
    • @return
    • @throws IOException
      */
      public static boolean send(String socketKey, String data)
      throws IOException {

      DatagramChannel socketChannel = SocketChannelMapper
      .getUDPChannel(socketKey);

      if (socketChannel == null || !socketChannel.isOpen()) {
      return false;
      }
      InetSocketAddress client = SocketChannelMapper
      .getUDPInetSocketAddress(socketKey + "address");
      boolean f = socketChannel.isConnected();
      ByteBuffer byteBuffer = ByteBuffer.wrap(DeviceDataTools.hex2Byte(data));
      if (f) {
      socketChannel.write(byteBuffer);
      } else {
      socketChannel.connect(new InetSocketAddress(client.getAddress(),
      client.getPort()));
      socketChannel.send(byteBuffer, client);
      }
      return true;
      }

    /**

    • 根据socketKey从内存中获取channel,通过channel向client端发送消息
    • @param socketKey
    • 内存在channel对应的key
    • @param data
    • 发送的数据
    • @return
    • @throws IOException
      */
      public static boolean send(DatagramChannel socketChannel, String data,
      InetSocketAddress client) throws IOException {

      if (socketChannel == null) {
      return false;
      }
      System.out.println("#########################ADDRESS"
      + client.getAddress());
      System.out.println("#########################PORT" + client.getPort());

      boolean f = socketChannel.isConnected();

      ByteBuffer byteBuffer = ByteBuffer.wrap(DeviceDataTools.hexStr2ByteArray(data));

      if (f) {
      socketChannel.write(byteBuffer);
      } else {
      socketChannel.connect(new InetSocketAddress(client.getAddress(),
      client.getPort()));
      socketChannel.send(byteBuffer, client);
      }

      return true;
      }
      }

  • 写回答

2条回答 默认 最新

  • 「已注销」 2015-09-20 07:54
    关注

    建议楼主建一个服务器,多个客户端测试一下

    评论

报告相同问题?

悬赏问题

  • ¥15 MCNP里如何定义多个源?
  • ¥20 双层网络上信息-疾病传播
  • ¥50 paddlepaddle pinn
  • ¥20 idea运行测试代码报错问题
  • ¥15 网络监控:网络故障告警通知
  • ¥15 django项目运行报编码错误
  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏