乱七八糟风暴收藏Java 杂谈

TCP、UDP及服务屏蔽实现方式

2016-11-16  本文已影响437人  无敌大冰块

目录

1. TCP、UDP

2. Java Tcp

3. Java Upd

4. 降级的几种方式

5. 降级的实现方式之一

6. 故障中降级的意义

7. 总结及展望


1. TCP、UDP


(1)端口号[16bit]

网络实现的是不同主机的进程间通信。在一个操作系统中,有很多进程,当数据到来时要提交给哪个进程进行处理呢?这就需要用到端口号。在TCP头中,有源端口号(Source Port)和目标端口号(Destination Port)。源端口号标识了发送主机的进程,目标端口号标识接受方主机的进程。

(2)序号[32bit]

序号分为发送序号(Sequence Number)和确认序号(Acknowledgment Number)。

发送序号:用来标识从 TCP源端向 TCP目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP用顺序号对每个字节进行计数。序号是 32bit的无符号数,序号到达 2  32- 1后又从 0开始。当建立一个新的连接时, SYN标志变 1,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN( Initial Sequence Number)。

确认序号:包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1。只有 ACK标志为 1时确认序号字段才有效。 TCP为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。

(3)偏移[4bit]

这里的偏移实际指的是TCP首部的长度,它用来表明TCP首部中32 bit字的数目,通过它可以知道一个TCP包它的用户数据是从哪里开始的。这个字段占4bit,如4bit的值是0101,则说明TCP首部长度是5 * 4 = 20字节。 所以TCP的首部长度最大为15 * 4 = 60字节。然而没有可选字段,正常长度为20字节。

(4)Reserved [6bit]

目前没有使用,它的值都为0

(5)标志[6bit]

在TCP首部中有6个标志比特。他们中的多个可同时被置为1 。

URG         紧急指针(urgent pointer)有效
ACK          确认序号有效
PSH          指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满

RST           一般表示断开一个连接
例如:一个TCP的客户端向一个没有监听的端口的服务器端发起连接,wirshark抓包如下
image

可以看到host:192.168.63.134向host:192.168.63.132发起连接请求,但是host:192.168.63.132并没有处于监听对应端口的服务器端,这时
host : 192.168.63.132发一个RST置位的TCP包断开连接。

SYN          同步序号用来发起一个连接
FIN            发送端完成发送任务(即断开连接)

(6)窗口大小(window)[16bit]

窗口的大小,表示源方法最多能接受的字节数。。

(7)校验和[16bit]

校验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。

(8)紧急指针[16bit]

只有当URG标志置为1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。

(9)TCP选项

是可选的,在后面抓包的时候,我们在看看它


a.请求端(通常称为客户)发送一个SYN段指明客户打算连接的服务器的端口,以及初始序号(ISN,在这个例子中为1415531521)。这个SYN段为报文段1。

b.服务器发回包含服务器的初始序号的SYN报文段(报文段2)作为应答。同时,将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号

c.客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认(报文段3)

这三个报文段完成连接的建立。这个过程也称为三次握手(three-way handshake)

image

a.现在的网络通信都是基于socket实现的,当客户端将自己的socket进行关闭时,内核协议栈会向服务器自动发送一个FIN置位的包,请求断开连接。我们称首先发起断开请求的一方称为主动断开方。

b.服务器端收到请客端的FIN断开请求后,内核协议栈会立即发送一个ACK包作为应答,表示已经收到客户端的请求

c.服务器运行一段时间后,关闭了自己的socket。这个时候内核协议栈会向客户端发送一个FIN置位的包,请求断开连接

d.客户端收到服务端发来的FIN断开请求后,会发送一个ACK做出应答,表示已经收到服务端的请求

image

TCP采用一种名为“带重传功能的肯定确认(positive acknowledge with retransmission)”的技术作为提供可靠数据传输服务的基础。这项技术要求接收方收到数据之后向源站回送确认信息ACK。发送方对发出的每个分组都保存一份记录,在发送下一个分组之前等待确认信息。发送方还在送出分组的同时启动一个定时器,并在定时器的定时期满而确认信息还没有到达的情况下,重发刚才发出的分组。图3-5表示带重传功能的肯定确认协议传输数据的情况,图3-6表示分组丢失引起超时和重传。为了避免由于网络延迟引起迟到的确认和重复的确认,协议规定在确认信息中稍带一个分组的序号,使接收方能正确将分组与确认关联起来。
从图 3-5可以看出,虽然网络具有同时进行双向通信的能力,但由于在接到前一个分组的确认信息之前必须推迟下一个分组的发送,简单的肯定确认协议浪费了大量宝贵的网络带宽。为此, TCP使用滑动窗口的机制来提高网络吞吐量,同时解决端到端的流量控制。

image
滑动窗口技术是简单的带重传的肯定确认机制的一个更复杂的变形,它允许发送方在等待一个确认信息之前可以发送多个分组。如图 3-7所示,发送方要发送一个分组序列,滑动窗口协议在分组序列中放置一个固定长度的窗口,然后将窗口内的所有分组都发送出去;当发送方收到对窗口内第一个分组的确认信息时,它可以向后滑动并发送下一个分组;随着确认的不断到达,窗口也在不断的向后滑动。

image

### 1.2. UDP

image

2. TCP java实现

image
image

import java.io.*;
import java.net.*;
class TCPServer{
    public static void main(String[] args)throws IOException{
        ServerSocket listen = new ServerSocket(5050);
        
        Socket server  = listen.accept();
        InputStream in = server.getInputStream();
        OutputStream out = server.getOutputStream();
        char c = (char)in.read();
        System.out.println("收到:" + c);
        out.write('s');
        
        out.close();
        in.close();
        server.close();
        listen.close();
    }
}


import java.io.*;
import java.net.*;
class TCPClient{
    public static void main(String[] args)throws IOException{
        Socket client = new Socket("127.0.0.1" , 5050);
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
        
        out.write('c');
        char c = (char)in.read();
        System.out.println("收到:" + c);
        out.close();
        in.close();
        client.close();
    }
}

3. UDP java实现


import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPSendTest {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();

        byte[] buffer = "hello xiaobin".getBytes();
        DatagramPacket dp = new DatagramPacket(buffer,buffer.length, InetAddress.getByName("127.0.0.1"),7879);
        socket.send(dp);
        socket.close();
    }
}



import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UDPReceiveTest {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(7879);
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf,buf.length);
        socket.receive(dp);
        System.out.println("host : " + dp.getAddress().getHostAddress()+ " : " + dp.getPort() + " : " + dp.getSocketAddress());
        System.out.println(new String(dp.getData(),0,dp.getLength()));
        socket.close();
    }
}

4. 服务降级

5. 降级实现

public interface BlackListListener {

    void addAll(List<String> items);

    void removeAll(List<String> items);

    void clear();

    Set<String> list();

    boolean contains(String item);

    boolean isEmpty();
}


public class CommandServer extends Thread{

    private static final Logger LOG = LoggerFactory.getLogger(CommandServer.class);
    private DatagramSocket serverSocket;
    volatile boolean running = true;
    private BlackListListener black = BlackListenerService.getInstance();

    public void serverStart() throws SocketException {
        try {
            Config config = new Config(Dict.APP_CONFIG_PROPERTIES);
            int port = ConfigUtil.getPropertyCount(config, "WORK.COMMAND.SERVER.PORT", 9991);
            LOG.info("start a DatagramSocket with port :" + port);
            serverSocket = new DatagramSocket(port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void serverStop(){
        this.setDaemon(true);
        this.running = false;
        if (serverSocket != null){
            serverSocket.close();
        }
    }

    public void start(){
        try {
            this.serverStart();
            super.start();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    public void run(){
        while (running){
            try {
                byte[] receiveBuff = new byte[4096];
                DatagramPacket dp = new DatagramPacket(receiveBuff, receiveBuff.length);
                serverSocket.receive(dp);
                if (dp.getLength() == 0){
                    continue;
                }else{
                    String receiveStr = new String(dp.getData(), "UTF-8");
                    receiveStr = receiveStr.replace('\n',' ').replace('\r',' ').trim();
                    LOG.info(String.format("input[%s], source ip:[%s]", receiveStr, dp.getAddress()));
                    InetAddress addr = dp.getAddress();
                    int port = dp.getPort();
                    DatagramPacket dpc = output(addr, port, parser(receiveStr));
                    serverSocket.send(dpc);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String parser(String cmd){
        String backString = "Error command!";
        if (StringUtils.isEmpty(cmd)){
            return backString + "\r\n";
        }
        cmd = cmd.trim();
        if ("list".equals(cmd)) {
            backString = "["+Joiner.on(",").join(black.list())+"]";
        } else if (cmd.startsWith("add ")) {
            cmd = cmd.replaceFirst("add ", "");
            String[] cmdArray = cmd.split("\\s");
            black.addAll(Arrays.asList(cmdArray));
            backString = "["+Joiner.on(",").join(black.list())+"]";
        } else if (cmd.startsWith("remove ")) {
            cmd = cmd.replaceFirst("remove ", "");
            String[] cmdArray = cmd.split("\\s");
            black.removeAll(Arrays.asList(cmdArray));
            backString = "["+Joiner.on(",").join(black.list())+"]";
        } else if (cmd.equalsIgnoreCase("clear")) {
            black.clear();
            backString = "["+Joiner.on(",").join(black.list())+"]";
        } else if(cmd.startsWith("up ")){
            cmd = cmd.replaceFirst("up ", "");
            String[] cmdArray = cmd.split("\\s");
            if (cmdArray.length==0){
                backString = "help:ignore name name";
            }else{
                for(String name: cmdArray){
                    CrossRateManager.getCrossRateManager().ignore(name, false);
                }
                backString = CrossRateManager.getCrossRateManager().list();
            }
        }else if(cmd.startsWith("down ")) {
            cmd = cmd.replaceFirst("down ", "");
            String[] cmdArray = cmd.split("\\s");
            if (cmdArray.length == 0) {
                backString = "help:ignore name name";
            } else {
                for (String name : cmdArray) {
                    CrossRateManager.getCrossRateManager().ignore(name, true);
                }
                backString = CrossRateManager.getCrossRateManager().list();
            }
        }
        backString += "\r\n";
        return backString;
    }

    private DatagramPacket output(InetAddress addr, int port, String text){
        DatagramPacket dp = new DatagramPacket(text.getBytes(), text.getBytes().length, addr, port);
        return dp;
    }

    public static void main(String[] args) {
        CommandServer cs = new CommandServer();
        cs.start();
    }
}

6. 降级意义


参考:

TCP、UDP、IP 协议分析-草根老师

上一篇 下一篇

猜你喜欢

热点阅读