15

如何在 Java 中实现基于线程的 UDP 服务器?

基本上我想要的是将多个客户端连接到服务器,并让每个客户端都有自己的线程。唯一的问题是,我不知道如何检查客户端是否正在尝试连接到服务器并为其生成一个新线程。

boolean listening = true;

System.out.println("Server started.");

while (listening)
    new ServerThread().start();

在这种情况下,服务器将产生新线程,直到内存不足。这是 ServerThread 的代码(我想我需要一种机制来暂停 ServerThread 的创建,直到客户端尝试连接。

public ServerThread(String name) throws IOException 
{
    super(name);
    socket = new DatagramSocket();
}

所以请Java编程之父帮忙。

4

4 回答 4

18

对此的设计在一定程度上取决于每个完整的UDP“对话”是否只需要一个请求和立即响应,是单个请求还是带有重传的响应,或者是否需要处理大量数据包每个客户。

我编写的 RADIUS 服务器具有单一请求 + 重传模型,并为每个传入数据包生成一个线程。

当每一个DatagramPacket被接收到时,它被传递给一个新线程,然后该线程负责发回响应。这是因为生成每个响应所涉及的计算和数据库访问可能需要相对较长的时间,并且产生一个线程比使用其他机制来处理在旧数据包仍在处理时到达的新数据包更容易。

public class Server implements Runnable {
    public void run() {
        while (true) {
            DatagramPacket packet = socket.receive();
            new Thread(new Responder(socket, packet)).start();
        }
    }
}

public class Responder implements Runnable {

    Socket socket = null;
    DatagramPacket packet = null;

    public Responder(Socket socket, DatagramPacket packet) {
        this.socket = socket;
        this.packet = packet;
    }

    public void run() {
        byte[] data = makeResponse(); // code not shown
        DatagramPacket response = new DatagramPacket(data, data.length,
            packet.getAddress(), packet.getPort());
        socket.send(response);
    }
}
于 2009-04-22T13:28:10.647 回答
6

既然 UDP 是无连接协议,为什么需要为每个连接生成一个新线程?当您收到 UDP 数据包时,您可能应该创建一个新线程来处理收到的消息。

UDP 连接与 TCP 连接不同。它们不会保持活动状态,这就是 UDP 的设计。

下一个代码块的 handlePacket() 方法可以对接收到的数据做任何事情。许多客户端可以将多个数据包发送到同一个 UDP 侦听器。也许它会帮助你。

public void run() {
        DatagramSocket wSocket = null;
        DatagramPacket wPacket = null;
        byte[] wBuffer = null;

        try {
            wSocket = new DatagramSocket( listenPort );
            wBuffer = new byte[ 2048 ];
            wPacket = new DatagramPacket( wBuffer, wBuffer.length );
        } catch ( SocketException e ) {
            log.fatal( "Could not open the socket: \n" + e.getMessage() );
            System.exit( 1 );
        }

        while ( isRunning ) {
            try {
                wSocket.receive( wPacket );
                handlePacket( wPacket, wBuffer );
            } catch ( Exception e ) {
                log.error( e.getMessage() );
            }
        }
    }
于 2009-04-21T15:25:09.923 回答
3

你看过Apache Mina项目吗?我相信即使是其中的一个示例也会带您了解如何使用它设置基于 UDP 的服务器。如果这是一个真正的产品,我不建议你尝试从头开始自己的实现。您将需要使用库来完成此操作,这样您就不会在每个连接中使用一个线程,而是使用线程池。

于 2009-04-21T15:29:54.933 回答
1

我真的看不出有必要。

是学校的事吧?

如果您需要跟踪客户端,您应该拥有每个客户端的本地表示(服务器上的 Client 对象)。它可以处理您需要做的任何特定于客户的事情。

在这种情况下,您需要能够找出消息是从哪个客户端发送的。(使用消息中的信息。)您可以将客户端保存在地图中。

最有效的方法可能是在主线程中进行所有处理,除非需要做的任何事情都可以“阻止”等待外部事件(或者如果某些应该发生的事情可能需要很长时间,而有些事情可能需要很短的时间。 )

public class Client {

    public void handleMessage(Message m) {
    // do stuff here.
    }

}

如果需要,客户端对象可能会在 handleMessage() 中启动一个新线程。

您不应该启动多个服务器线程。

服务器线程可以做:

while(running) {
  socket.receive(DatagramPacket p);
  client = figureOutClient(p);
  client.handleMessage(p);
}

如果没有特定于客户端的事情需要关心,只需在一个线程中读取消息并在它们到达时对其进行处理。

于 2009-04-21T15:44:22.730 回答