Прослушивание запросов TCP и UDP на одном и том же порту
Я пишу клиент / серверный набор программ
В зависимости от операции, запрошенной клиентом, я использую запрос make TCP или UDP.
Реализация клиентской стороны проста, так как я могу легко открыть соединение с любым протоколом и отправить запрос на серверную сторону.
С другой стороны, на стороне сервера я хотел бы прослушивать соединения UDP и TCP на одном и том же порту. Более того, мне нравится, когда сервер открывает новый поток для каждого запроса на подключение.
Я принял подход, объясненный в: текст ссылки
Я расширил этот пример кода, создав новые потоки для каждого запроса TCP/UDP.
Это работает правильно, если я использую только TCP, но происходит сбой при попытке привязки UDP.
Пожалуйста, дайте мне любое предложение, как я могу исправить это.
Тпх
Вот код сервера:
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)
10x
3 ответа
Он должен работать. Кажется, одна из проблем с этим кодом заключается в том, что размер ByteBuffer установлен в 0, что означает, что датаграмма отбрасывается (как это упоминается в комментариях). Если вам нужно получить какую-либо информацию по UDP и вы находитесь в надежной сети, вы можете установить достаточно большой размер и получать большие дейтаграммы, состоящие из нескольких пакетов. В противном случае в ненадежной сети установите значение MTU. Убедитесь, что вы перевернули () ByteBuffer после получения чего-либо в нем.
Кроме того, создание новых потоков для каждого запроса - плохая идея, создавать поток "сеанса" для каждого отдельного IP-адреса, который вы получаете в HashMap или чем-то подобном, а затем создавать защищенный блок для объекта сеанса. Разбудите поток, спящий на этом объекте, когда вы получите сообщение после передачи новой информации. Код селектора, который у вас есть, предназначен для того, чтобы избежать создания потоков таким способом.
Редактировать: на основе приведенного выше кода вы создаете канал дейтаграмм и затем используете сокет для непосредственного получения дейтаграмм? Это не имеет смысла. Используйте методы канала только после привязки канала. Кроме того, не делайте этого в отдельной теме. Ваш код не является потокобезопасным и разрушится. Передайте полученную информацию отдельному потоку сеанса, как упоминалось ранее. Селектор предназначен для того, чтобы сообщать вам, с каких каналов можно читать без блокировки (хотя блокировка в любом случае отключена, поэтому он скажет вам, с каких каналов есть данные, с которых нужно считывать данные).
AFAIK, вы должны иметь возможность прослушивать как TCP-соединения, так и UDP-сообщения на одном и том же порту. Было бы полезно, если бы вы опубликовали свой UDP-код и исключение + отслеживание стека, которое вы видите.
Вы не можете использовать DatagramSocket.receive()
в неблокирующем режиме. Вы должны использовать read()
или же receive()
методы вашего DatagramChannel
непосредственно.
На самом деле, как вы используете неблокирующий режим и Selector
, совершенно невозможно понять, почему вы также используете UDPThread
совсем. Просто позвони udpserver.receive()
вместо начала потока.