java - UDP 发送多个分割字符串

标签 java udp stringbuilder datagram

我遇到了一个我自己无法解决的问题。我认为,如果 (1) MSG > BUFFER 和消息速率为 1 消息/秒,那么我的拆分和添加到数组列表并最终重新组装消息片段的方法效果很好。但是当我每秒发送超过 1 条消息并且我必须拆分一条大/小消息时,就会出现问题。是的,从长远来看,这种方法可能效率低下,但这是一项任务,所以我只想让它按我想要的方式工作,我对此很满意。

我很确定问题在于它当然会发送每条消息的速率。我的控制台中的输出如下:

--------------------------------
| UDP Echo Client
| Configuration: 
| server name: localhost
| port: 4950
| buffer: 8
| rate: 5
| message size: 15
--------------------------------
Original: [HelloHe, lloHell, o]
Received: [HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!
Received: [HelloHe, HelloHe, HelloHe]
MESSAGE IS NOT EQUAL!

有人可以帮我吗?解决这个问题的最佳方法是什么?

UDP 客户端:

import java.io.IOException;
import java.net.*;
import java.util.*;

/*
UDP Echo client. Sends a echo message of a size to the server and gets it back.
It checks so that the message wasn't lost or anything has happened to it.
by jv222dp

Rate works perfectly when MSG.length <= MY_BUFFER.
When the BUFFER is smaller then the MSG it works great if rate is 1
 */
public class UDPEchoClient {

    private static final String MSG = "HelloHelloHello";

    private static int MY_PORT;
    private static int RATE;
    private static int MY_BUFFER;
    private static String HOST_NAME;
    private static byte[] buf;
    private static int packages;
    private static int chars;
    private static List<String> originalMsg;
    private static List<String> receivedString = new ArrayList<>(packages);
    private static DatagramPacket sendPacket;

    public static void main(String[] args) {

        if (!isCorrect(args)) {
            System.exit(1);
        } else {

            try {

            /* Configuration printout */
                System.out.println("--------------------------------" +
                        "\n| UDP Echo Client" +
                        "\n| Configuration: " +
                        "\n| server name: " + HOST_NAME +
                        "\n| port: " + MY_PORT +
                        "\n| buffer: " + MY_BUFFER +
                        "\n| rate: " + RATE +
                        "\n| message size: "+MSG.length()+
                        "\n--------------------------------");
                /* Sets the buffer */
                buf = new byte[MY_BUFFER];

                /* Create socket */
                DatagramSocket socket = new DatagramSocket(null);

                /* Create local endpoint using bind() */
                SocketAddress localBindPoint = new InetSocketAddress(0);
                socket.bind(localBindPoint);

                socket.setSoTimeout(2000);

                /* Create remote endpoint */
                SocketAddress remoteBindPoint = new InetSocketAddress(HOST_NAME,
                        (MY_PORT));



                /* Sends and reads the echo message */
                sendEchoPackets(socket, remoteBindPoint);


            } catch (SocketException se) {
                System.err.println("Host unreachable!" +
                        "Wrong port or host offline");
            }
        }
    }

    public static void sendEchoPackets(DatagramSocket socket, SocketAddress remoteBindPoint) {

        System.out.println("Original: "+originalMsg.toString());

        /* For each string in the List of message parts */
        for (String message : originalMsg) {

        /* Create datagram packet for sending message */
            sendPacket = new DatagramPacket(
                    message.getBytes(),
                    message.length(),
                    remoteBindPoint);

            Timer timer = new Timer();
            TimerTask rate = new TimerTask() {

                @Override
                public void run() {
                    try {

                        if (RATE == 0 || RATE == 1) {
                            for (int i = 0; i < RATE; i++) {
                                socket.send(sendPacket);
                                timer.cancel();
                            }
                        } else {
                            for (int i = 0; i < RATE; i++) {
                                socket.send(sendPacket);
                                timer.cancel();
                            }

                        }
                    } catch (IOException e) {
                        System.out.println(e.getMessage());
                    }
                }
            };

            timer.scheduleAtFixedRate(rate, 0, 1000);
            readEchoPacket(socket);
        }

    }

    public static void readEchoPacket(DatagramSocket socket){

        try {

            /* Create datagram packet for receiving echoed message */
            DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

            socket.receive(receivePacket);

            String receivedEcho = new String(
                    receivePacket.getData(),
                    receivePacket.getOffset(),
                    receivePacket.getLength());

            receivedString.add(receivedEcho);

            /* Compares if the message is the same as the one that was sent */
            compareEchoMessage(receivedString);
        }
        catch (IOException e) {
            System.out.println(e.getMessage());
        }


    }

    public static void compareEchoMessage(List<String> receivedMsg){
        StringBuilder sb = new StringBuilder();

        for (String str : receivedMsg) {
            sb.append(str);
        }
        System.out.println("Received: "+receivedMsg.toString());
        if (sb.toString().compareTo(MSG) == 0){
            System.out.printf("%s bytes sent and received!",sb.length());
        }
        else{
            System.out.println("MESSAGE IS NOT EQUAL!");
        }
    }

    /* Splits the message equally */
    private static ArrayList<String> splitMessage(String message, int chunks)    {
        /* */
        ArrayList<String> packages = new ArrayList<>(
                (message.length() + chunks) - 1 / chunks);

        for (int i = 0; i < message.length(); i += chunks){
            packages.add(message.substring(i, Math.min(message.length(),
                    i + chunks)));
        }
        return packages;
    }

    public static boolean isCorrect(String[] args) {

        /* Make sure all arguments are present */
        if (args.length != 4 && args.length == 0) {
            printUsage();
            return false;
        }
        else
            try {

                HOST_NAME = args[0];
                MY_PORT = Integer.parseInt(args[1]);
                MY_BUFFER = Integer.parseInt(args[2]);
                RATE = Integer.parseInt(args[3]);

            /* Ensures RATE is not too high with a tested limit of 3000 */
                if (RATE > 3000) {
                    System.err.println("Rate value is too large!");
                    return false;
                }

            /* Make sure the host is valid */
                if (!isValidHost(HOST_NAME)) {
                    System.err.println("Host address is not valid!" +
                            "\nRequires a valid IP address or just localhost");
                    return false;
                }

             /* Make sure the port number is in the valid range */
                if (MY_PORT <= 0 || MY_PORT >= 65536) {
                    System.err.println("Port value must be in (0 -> 65535)!");
                    return false;
                }

            /* Make sure the buffer is at least 2, not lower */
                if (MY_BUFFER < 2){
                    System.err.println("Buffer must be higher or equal to 2!");
                    return false;
                }

            /* Split the message if bigger than buffer to appropriate packages */
                if (MSG.length() > MY_BUFFER) {
                    packages = (int) Math.ceil((double) MSG.length() / MY_BUFFER);
                    chars = (MSG.length() / packages);
                    originalMsg = splitMessage(MSG, chars);
                }

            /* Else adds whole message to array list */
                else {

                    packages = (int) Math.ceil( (double)MSG.length() / MY_BUFFER);
                    chars = (MSG.length() / packages);
                    originalMsg = splitMessage(MSG, chars);
                }
            }
            catch (IndexOutOfBoundsException e) {
                printUsage();
                System.exit(1);
            }
            catch (NumberFormatException n) {
                System.err.println("Invalid arguments!");
                printUsage();
                System.exit(1);
            }

        /* Everything is valid */
        return true;
    }

    private static boolean isValidHost(String host) {

        /* Check if the string is valid */
        if (host == null || host.length() < 7 || host.length() > 15){
            return false;
        }
        else

        /* Host is valid "localhost" */
            if (host.equals("localhost")){
                return true;
            }

        /* Check the host string, should be in x.x.x.x format */
        StringTokenizer token = new StringTokenizer(host,".");
        if (token.countTokens() != 4)
            return false;

        while (token.hasMoreTokens()) {

            /* Get current token and convert to an integer value */
            String ip = token.nextToken();
            try {

                int ipVal = Integer.valueOf(ip).intValue();
                if ( ipVal < 0 || ipVal > 255)
                    return false;
            }
            catch (NumberFormatException ex) {
                return false;
            }
        }

        /* IP Address looks valid */
        return true;
    }

    private static void printUsage() {
        System.err.println("Input arguments did not match expected arguments!" +
                "\nUsage: \"<host_name> <port> <message_buffer> <message_rate>\"");
    }
}

UDP 服务器:

/*
  UDPEchoServer.java
  A simple echo server with no error handling
*/
import java.io.IOException;
import java.net.*;

public class UDPEchoServer {
    public static final int BUFSIZE = 1024;
    public static final int MYPORT = 4950;
    public static boolean running = true;

    public static void main(String[] args) {
        byte[] buf = new byte[BUFSIZE];

        try{

            /* Create socket */
            DatagramSocket socket = new DatagramSocket(null);

            /* Create local bind point */
            SocketAddress localBindPoint = new InetSocketAddress(MYPORT);
            socket.bind(localBindPoint);

            System.out.println("---------------------------------"+
                    "\n| UDP Echo Server"+
                    "\n| Configuration: "+
                    "\n| port: "+MYPORT+
                    "\n---------------------------------");

            while (running) {

                /* Create datagram packet for receiving message */
                DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

                /* Receiving message */
                socket.receive(receivePacket);

                /* Create datagram packet for sending message */
                DatagramPacket sendPacket =
                        new DatagramPacket(receivePacket.getData(),
                                receivePacket.getLength(),
                                receivePacket.getAddress(),
                                receivePacket.getPort());

                String echo = new String(receivePacket.getData(),
                        receivePacket.getOffset(), receivePacket.getLength());

                System.out.printf("UDP echo request from %s", receivePacket.getAddress().getHostAddress());
                System.out.printf(" using port %d\n", receivePacket.getPort());
                System.out.println("Received: "+echo);

                 /* Send message*/
                socket.send(sendPacket);
            }
        }
        catch (SocketException s){
            System.err.println(s.getMessage());
        }
        catch (IOException e){
            System.err.println(e.getMessage());
        }
    }
}

最佳答案

让我们看看当您的费率为 5 时会发生什么:

这是计时器主体:

                    if (RATE == 0 || RATE == 1) {
                        for (int i = 0; i < RATE; i++) {
                            socket.send(sendPacket);
                            timer.cancel();
                        }
                    } else {
                        for (int i = 0; i < RATE; i++) {
                            socket.send(sendPacket);
                            timer.cancel();
                        }

                    }

因此,if 条件为 false,因为比率既不是 0 也不是 1。我们转到 else:

                        for (int i = 0; i < RATE; i++) {
                            socket.send(sendPacket);
                            timer.cancel();
                        }

对于 RATE = 5,这就像编写:

                            socket.send(sendPacket);
                            timer.cancel();
                            socket.send(sendPacket);
                            timer.cancel();
                            socket.send(sendPacket);
                            timer.cancel();
                            socket.send(sendPacket);
                            timer.cancel();
                            socket.send(sendPacket);
                            timer.cancel();

当然,取消定时器五次没有任何效果,但是它将同一个数据包一一发送了五次。然后,它将发送下一部分 5 次,第三部分 5 次,因为您正在为这些部分创建三个单独的计时器。

我认为,如果您想以每秒 5 个数据报的速率发送(这就是速率的含义吗?),您不应该创建与部件一样多的计时器。您应该创建一个计时器,为其提供要发送的数据报列表,并将其计划周期设置为 1000L/速率(确保速率不为零!)。计时器应该从列表中弹出下一个数据报并发送它。如果列表中没有留下任何数据报,它应该自行取消。

  • 一个循环用数据报填充列表
  • 将列表分配给可从匿名类或字段中使用的最终变量。
  • 创建计时器并按照1000L/rate计划时间运行。
  • 运行第二个循环来读取和比较接收到的回波数据报。

注意两个独立的循环!

关于重新组装数据报

首先,请注意,您从服务器收到的 DatagramPacket 与您发送给服务器的 DatagramPacket 并不相同,即使内容相同!它们是两个不同的对象,并且 Object 中的 equals() 方法不会被重写,这意味着对于 DatagramPacket 类型的任意两个对象 aba.equals(b) 等价于 a == b

这意味着您唯一可以比较的是数据报内容,而不是数据报对象

由于 UDP 不保证数据包将以任何特定顺序发送,因此您必须自己处理这一点。这通常意味着您必须在数据报有效负载中包含更多信息,而不仅仅是字符串内容。一个好的起点是添加一个表示零件编号的字节。

例如,假设您要在包含“ABC”和“DEF”的两个数据包中发送消息“ABCDEF”。您现在正在做的是发送如下内容:

┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘
┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘

现在你可能会得到它作为

┌──┬──┬──┐
│68│69│70│
└──┴──┴──┘
┌──┬──┬──┐
│65│66│67│
└──┴──┴──┘

你无法知道这一点,你将重新组装它,它将是 DEFABC

但是如果您发送了另一个给出命令的字节:

┌─┬──┬──┬──┐
│0│65│66│67│
└─┴──┴──┴──┘
┌─┬──┬──┬──┐
│1│68│69│70│
└─┴──┴──┴──┘

您将获得第一个字节,将其转换为整数,并将其余字节转换为字符串。然后,您可以使用索引将其放入列表中,无论您是第一个还是第二个获得 1 数据包,它们都会按正确的顺序出现。

在现实世界中,您还将发送大小(数据包数量)和识别号(这样,如果您收到一些属于刚刚到达的旧通信的恶意数据报,它们就不会混合到您重新组装的有效负载中。

关于java - UDP 发送多个分割字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28498440/

相关文章:

java - BlueJ Java 复合条件

java - Java中的准确计时

python - msgpack 能否提供更好的性能和与 python 的 struct.pack() 相同的功能?

java - 编写函数 : short GetBits(short data, int p, int n)

java - removeFirstOccurrence 和删除之间的区别

java - java中字节数组到十进制的转换

C:套接字编程设计选项

java - 连接多个字符串的最快方法

java - 使用单个 StringBuilder - 抛出 OutofmemeoryException

java - 将对象的 ArrayList 转换为 String,并且 CVS 文件中该字符串的开头和结尾不包含引号字符 ""