A Fireman's Blog

JAVA API 之网络流量优先转发控制

2021-05-24
lausaa

java.net.Socket.setTrafficClass(int tc)

TOS (type-of-service) 是IP 报文协议头的一个8位字段,用于配置此类流量的优先级。
Java API 中,java.net.Socket.setTrafficClass(int tc) 接口用于配置TOS。
由于TOS 与DSCP 的兼容性,此API 也是用于DSCP 配置。

DSCP(Differentiated Services Codepoint)是TOS 字段的高6 位,低2 位补0;

AF

保证转发(Assured Forwarding,AF)由RFC2597 对CS1~CS4 进行进一步定义。它使用第3和第4比特做丢弃优先级标志。

  • 01-低丢弃优先级;
  • 10-中丢弃优先级;
  • 11-高丢弃优先级。

这样,在同一类数据中,又根据被丢弃的可能性划分出3档。下表列出了AF服务等级及其对应的DSCP值:

                CS1      CS2      CS3      CS4   
Lowdrop         AF11     AF21     AF31    AF41  
               001010   010010   011010   100010  
Mediumdrop      AF12     AF22     AF32     AF42  
                001100   010100   011100   100100  
Highdrop        AF13     AF23     AF33      AF43  
                001110   010110   011110    100110  


实验

本实验我们配置DSCP 为AF41(0b100010 = 0x22 = 34,对应TOS:0b10001000 = 0x88 = 136)
如下两个java 文件分别作为客户端和服务端代码,编译执行:

# javac Client.java

# javac Server.java

# java -Djava.net.preferIPv4Stack=true Server 7777 136
Starting server....
Client:172.28.200.100 is accepted.
Test start.

# java -Djava.net.preferIPv4Stack=true Client 10.198.245.86 7777 136
Test start.
1000 pingpongs, Average Time(us): 2087

执行期间通过tcpdump 抓包:
# tcpdump port 7777 -nnvvS
......
16:27:01.224518 IP (tos 0x88, ttl 64, id 4821, offset 0, flags [DF], proto TCP (6), length 42)
    172.28.200.100.60918 > 10.198.245.86.7777: Flags [P.], cksum 0x74ba (incorrect -> 0x1449), seq 1936351735:1936351737, ack 1871775163, win 58, length 2
16:27:01.226615 IP (tos 0x88, ttl 57, id 62839, offset 0, flags [DF], proto TCP (6), length 42)
    10.198.245.86.7777 > 172.28.200.100.60918: Flags [P.], cksum 0x1447 (correct), seq 1871775163:1871775165, ack 1936351737, win 58, length 2
......
<-- 可以看到数据部分tos 为0x88(默认为0),说明设置生效。

ping 时延测试如下图:

测试代码

//File: Client.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {
  public static void main(String[] args) {
  String ip = args[0];
  int port = Integer.parseInt(args[1]);
  int tos = Integer.parseInt(args[2]);
    try {
      Socket s = new Socket(ip, port);
      s.setTrafficClass(tos);

      InputStream is = s.getInputStream();
      OutputStream os = s.getOutputStream();

      BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
      BufferedReader br = new BufferedReader(new InputStreamReader(is));

      bw.write("Test start.\n");
      bw.flush();
      String hiMsg = br.readLine();
      System.out.println(hiMsg);

      long now_us = 0;
      long sum_us = 0;
      long begin_us = 0;
      int num = 1000;

      for (int i = 0; i < num; i++) {
        bw.write(i);
        bw.flush();

        begin_us = System.nanoTime() / 1000;

        int msg = br.read();

        now_us = System.nanoTime() / 1000;
        sum_us += now_us - begin_us;

        //System.out.println("Server: " + msg + ", us:" + sum_us);
      }
      System.out.println(num + " pingpongs, Average Time(us): " + (sum_us / num));
    } catch (UnknownHostException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}


// File: Server.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
  public static void main(String[] args) {
    int port = Integer.parseInt(args[0]);
    int tos = Integer.parseInt(args[1]);
    while (true) {
      ServerSocket ss = null;
      try {
        ss = new ServerSocket(port);
        System.out.println("Starting server....");
        Socket s = ss.accept();
        s.setTrafficClass(tos);
        System.out.println("Client:" + s.getInetAddress().getHostAddress() + " is accepted.");

        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

        String hiMsg = br.readLine();
        System.out.println(hiMsg);
        bw.write(hiMsg + "\n");
        bw.flush();
        int num = 100000;

        for (int i = 0; i < num; i++) {
          int msg = br.read();
          //System.out.println("Client:" + msg);
          bw.write(msg);
          bw.flush();
        }
      } catch (Throwable e) {
        //e.printStackTrace();
        System.out.println(e);
        try {
          ss.close();
        } catch (IOException ioe) {
          ioe.printStackTrace();
        }
      }
    }
  }
}


Content