在同一端口上侦听 TCP 和 UDP 请求
Posted
技术标签:
【中文标题】在同一端口上侦听 TCP 和 UDP 请求【英文标题】:Listening for TCP and UDP requests on the same port 【发布时间】:2011-02-18 15:25:05 【问题描述】:我正在编写一组客户端/服务器程序
根据客户端请求的操作,我使用 make TCP 或 UDP 请求。
实现客户端很简单,因为我可以轻松打开任何协议的连接并将请求发送到服务器端。
另一方面,在服务器端,我想在同一个端口上同时监听 UDP 和 TCP 连接。此外,我喜欢服务器为每个连接请求打开新线程。
我采用了在:link text中解释的方法
我通过为每个 TCP/UDP 请求创建新线程来扩展此代码示例。
如果我只使用 TCP,这可以正常工作,但是当我尝试进行 UDP 绑定时它会失败。
请给我任何建议,我该如何纠正这个问题。
tnx
这是服务器代码:
public class Server
public static void main(String args[])
try
int port = 4444;
if (args.length > 0)
port = Integer.parseInt(args[0]);
SocketAddress localport = new InetSocketAddress(port);
// Create and bind a tcp channel to listen for connections on.
ServerSocketChannel tcpserver = ServerSocketChannel.open();
tcpserver.socket().bind(localport);
// Also create and bind a DatagramChannel to listen on.
DatagramChannel udpserver = DatagramChannel.open();
udpserver.socket().bind(localport);
// Specify non-blocking mode for both channels, since our
// Selector object will be doing the blocking for us.
tcpserver.configureBlocking(false);
udpserver.configureBlocking(false);
// The Selector object is what allows us to block while waiting
// for activity on either of the two channels.
Selector selector = Selector.open();
tcpserver.register(selector, SelectionKey.OP_ACCEPT);
udpserver.register(selector, SelectionKey.OP_READ);
System.out.println("Server Sterted on port: " + port + "!");
//Load Map
Utils.LoadMap("mapa");
System.out.println("Server map ... LOADED!");
// Now loop forever, processing client connections
while(true)
try
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
// Iterate through the Set of keys.
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext();)
SelectionKey key = i.next();
i.remove();
Channel c = key.channel();
if (key.isAcceptable() && c == tcpserver)
new TCPThread(tcpserver.accept().socket()).start();
else if (key.isReadable() && c == udpserver)
new UDPThread(udpserver.socket()).start();
catch (Exception e)
e.printStackTrace();
catch (Exception e)
e.printStackTrace();
System.err.println(e);
System.exit(1);
UDPThread 代码:
public class UDPThread extends Thread
private DatagramSocket socket = null;
public UDPThread(DatagramSocket socket)
super("UDPThread");
this.socket = socket;
@Override
public void run()
byte[] buffer = new byte[2048];
try
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String inputLine = new String(buffer);
String outputLine = Utils.processCommand(inputLine.trim());
DatagramPacket reply = new DatagramPacket(outputLine.getBytes(), outputLine.getBytes().length,
packet.getAddress(), packet.getPort());
socket.send(reply);
catch (IOException e)
e.printStackTrace();
socket.close();
我收到:
Exception in thread "UDPThread" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.DatagramSocketAdaptor.receive(Unknown Source)
at server.UDPThread.run(UDPThread.java:25)
10 倍
【问题讨论】:
失败怎么办?您是否有更多信息,例如错误消息或示例代码? 请详细说明“失败”。 【参考方案1】:您不能在非阻塞模式下使用DatagramSocket.receive()
。您必须直接使用DatagramChannel
的read()
或receive()
方法。
事实上,当您使用非阻塞模式和Selector
时,根本无法理解为什么您还使用UDPThread
。只需调用udpserver.receive()
而不是启动线程。
【讨论】:
【参考方案2】:它应该工作。这段代码的问题之一似乎是 ByteBuffer 大小设置为 0,这意味着数据报被丢弃(正如它在 cmets 中提到的那样)。如果您需要通过 UDP 接收任何信息并且您在可靠的网络上,您可以将大小设置得相当大并接收由多个数据包组成的大数据报。否则,在不可靠的网络上,将其设置为 MTU 大小。确保在收到任何内容后翻转() ByteBuffer。
此外,为每个请求创建新线程是一个坏主意,为您在 HashMap 或其他东西中收到的每个不同 IP 创建一个“会话”线程,然后在会话对象上执行 guarded block。当您在传入新信息后收到消息时,唤醒在该对象上休眠的线程。您拥有的选择器代码旨在避免以这种方式创建线程。
编辑:基于上面的代码,你是在创建一个数据报通道,然后使用套接字直接接收数据报吗?那没有意义。仅在绑定通道后使用通道方法。另外,不要在单独的线程中执行此操作。您的代码不是线程安全的,并且会自行崩溃。如前所述,将收到的信息交给单独的“会话”线程。选择器旨在告诉您可以在不阻塞的情况下读取哪些通道(尽管无论如何都禁用了阻塞,所以它会告诉您哪些通道可以读取数据)。
【讨论】:
我可以通过使用会话线程来理解您的观点,我同意您的观点,但出于我的目的,最好每个连接创建一个线程。对于 ByteBuffer,我没有使用它看起来你没有看到我上传的代码,只是链接。 tnx UDP 套接字没有连接。只有 TCP 套接字可以。该线程只是使处理远离主选择线程。无论如何,这就是 Apache MINA 的工作原理 :) Aham,如果我没听错的话,您说创建新的 UDP 线程没有意义,而是在服务器类中接收信息,然后在新线程中进一步处理它?顺便说一句,有没有更优雅的方法来做同样的事情,同时监听 tcp 和 udp 请求? 线程不是必需的,但它们增加了可扩展性(它允许选择线程做更多的工作,如果你发现你不能处理那么多数据)。至于同时监听UDP和TCP请求,有两个selector线程,一个用于TCP,一个用于UDP,可以分开逻辑。 嗯,我明白你的意思,但我不知道如何做到这一点【参考方案3】:AFAIK,您应该能够在同一端口上侦听 TCP 连接和 UDP 消息。如果您发布您的 UDP 代码以及您所看到的异常 + 堆栈跟踪,将会有所帮助。
【讨论】:
以上是关于在同一端口上侦听 TCP 和 UDP 请求的主要内容,如果未能解决你的问题,请参考以下文章