如何在另一个客户端发送到服务器时将UDP服务器推送到客户端

时间:2014-11-28 00:47:12

标签: java sockets udp server-push

我正在开发一个简单的Java聊天程序,主要是为了了解UDP以及计算机如何相互通信。

到目前为止,我已经能够设置服务器来监听客户端连接到它,我甚至能够通过服务器将消息从一个客户端重定向到另一个客户端 - 也就是说:

客户A - >服务器 - >客户B

我已经达到服务器实际发送数据包的程度(sock.send(数据包)),但问题是客户实际上并不知道"知道"听他们只知道如何发送到服务器。

我尝试为客户端设置run(),类似于我对服务器的操作,但是当我拉出两个客户端时,我的程序崩溃了,因为我试图在同一个端口上侦听。

服务器代码(请忽略所有分隔符,它只是我现在用来发送不同信息的方式):

package com.jona.chat.UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.sql.SQLException;
import java.util.TreeMap;

public class UDPServer extends Thread{

    final int PORT_NUMBER = 4447;
    private String separator = "~!~--//1337"; //IGNORE THIS
    protected DatagramSocket sock = null;
    private TreeMap<String,InetAddress> nameIPTree = new TreeMap<String,InetAddress>(); //IGNORE THIS


    public static void main(String args[]) throws IOException{

        UDPServer SERVER = new UDPServer();

        //calls the run() method
        SERVER.start();
    }

    public UDPServer() throws IOException{

        sock = new DatagramSocket(PORT_NUMBER);
    }

    public void run(){
        System.out.println("Waiting for Client");
        while(true){

            try{

                //========================================================================================================
                //Prepare the packet to receive data from client
                //========================================================================================================

                //Buffer (byte array) that will receive the client's data
                byte[] buffer = new byte[512];

                //Create a packet using the empty buffer and its length
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                //========================================================================================================
                //Receive the packet from the client, execute the statement, and get the result
                //========================================================================================================

                //Receive the packet
                sock.receive(packet);
                System.out.println("Server: Received packet from Client");

                //Extract the data
                String fromUser = new String(packet.getData(), 0, packet.getLength());
                //Parse data
                String[] instructions = fromUser.split(separator); //IGNORE THIS

                //Add UserName and IP to tree
                if(instructions[0].equals("LOGIN")){
                    System.out.println("Logged in!");
                    nameIPTree.put(instructions[1], packet.getAddress());

                    run();
                }
                //Send message to recipient and upload to DB
                else if(instructions[0].equals("MESSAGE")){

                    //Create a string composed of the sender and the message
                    String toUser = instructions[2] + separator + instructions[3];

                    //Store the string in the buffer
                    buffer = toUser.getBytes();

                    //Make a new packet with the buffer, its length, the RECEPIENT'S IP (retrieved from tree, hence receiving user HAS TO BE LOGGED IN)
                    //and the port number the server uses

                    packet = new DatagramPacket(buffer, buffer.length, nameIPTree.get(instructions[2]), PORT_NUMBER+1);

                    //Send the packet
                    sock.send(packet);
                    System.out.println("Server: Sent result to Client:  " + toUser);
                }

                }
                catch (IOException  e){
                    e.printStackTrace();
                    break;
                }
        }
        System.out.println("Closing the socket");
        sock.close();
    }
}

客户端

public class UDPClient extends Thread{

    final int PORT_NUMBER = 4447;
    private String separator = "~!~--//1337";

    public String TalkToServer(String message){

        try{
            //========================================================================================================
            //Create a datagram socket
            //========================================================================================================
            DatagramSocket sock = new DatagramSocket();

            //========================================================================================================
            //Connect & Send to server
            //========================================================================================================

            //Create a byte array called buffer that will hold the instructions to be sent to the server
            byte[] buffer =  message.getBytes("UTF-8");

            //Get the IP address to which the packet will be sent 
            InetAddress ipAddress = InetAddress.getByName("123.45.67");

            //Create a datagram packet which is composed of the buffer (message), its length, the IP address, 
            //and the port (matches with server's listening port) to send the data on
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, ipAddress, PORT_NUMBER);

//same thing in both ifs, I know, I just wanted to see what it was doing 
                if(message.substring(0, 5).equals("LOGIN")){

                    System.out.println("Client: Logging in");


                //Send the packet
                sock.send(packet);
                System.out.println("Client: Sent packet to Server\nSent: " + message);

                sock.close();
                return null;
            }
            if(message.substring(0, 7).equals("MESSAGE")){

                System.out.println("Client: Sending message to server");

                //Send the packet
                sock.send(packet);
                System.out.println("Client: Sent packet to Server\nSent: " + message);

                sock.close();
                return null;
            }
        }
        catch(IOException e){System.out.print(e);}
        return null;
    }
}

最后,这是我试图让客户端听的方式(这是我的主要课程):

public static void main(String[] args) throws SocketException{

    MainGUI listener = new MainGUI();
    listener.start();

...

public MainGUI() throws SocketException{


    sock = new DatagramSocket(PORT_NUMBER+1);
}
public void run(){


    byte[] buffer =  new byte[512];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

    try {
        sock.receive(packet);
        String fromUser = new String(packet.getData(), 0, packet.getLength());
        //Parse data
        String[] instructions = fromUser.split(separator);
        System.out.println("Received message: " + instructions[1] + " from " + instructions[0]);


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

}

这是我尝试同时运行两个主电源时出现的错误(这个错误对我来说很有意义,我只是不知道还有什么可以让客户端听): / p>

Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
    at java.net.DualStackPlainDatagramSocketImpl.socketBind(Native Method)
    at java.net.DualStackPlainDatagramSocketImpl.bind0(Unknown Source)
    at java.net.AbstractPlainDatagramSocketImpl.bind(Unknown Source)
    at java.net.DatagramSocket.bind(Unknown Source)
    at java.net.DatagramSocket.<init>(Unknown Source)
    at java.net.DatagramSocket.<init>(Unknown Source)
    at java.net.DatagramSocket.<init>(Unknown Source)
    at com.jona.chat.UDP.UDPServer.<init>(UDPServer.java:28)
    at com.jona.chat.UDP.UDPServer.main(UDPServer.java:20)

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您无法在单个计算机/网络接口上的同一端口上运行两个侦听器。我提供这个解决方案,它应该足以在同一台机器上运行服务器和多个客户端。

  1. 在您的服务器上,维护可用客户端侦听端口列表
  2. 在您的服务器上维护客户端及其侦听端口的地图
  3. 在启动客户端侦听器之前,建立与服务器的客户端连接并请求下一个可用的客户端端口
  4. 服务器更新客户端/端口映射
  5. 在客户端上启动侦听器/ serversocket,侦听从上一步从服务器收到的端口号
  6. 现在假设您有一个在唯一端口上运行的服务器,运行在唯一端口上的客户端
  7. 当客户端向另一个客户端的服务器发送消息时。服务器只是检索接收客户端端口并使客户端连接到接收器以发送消息。

答案 1 :(得分:0)

你不应该为接收器&#39;重新绑定套接字。线。删除此行sock = new DatagramSocket(PORT_NUMBER+1);。您只需要一个套接字来发送和接收数据包,因此您不应该重新绑定套接字。虽然,如果您正在等待接收数据包,这将使您的线程停止运行并且您无法发送数据包,因此如果您需要同时执行这两个数据包,则应使用两个套接字。这会解决您的问题吗?