目录

  • TCP
  • 面向连接的TCP
  • 1.TCP概念
  • 2.TCP特点
  • 3.TCP报文段格式
  • 3.TCP连接管理
  • TCP编程
  • 模拟实现
  • UDP
  • 无连接的UDP
  • 1.UDP概念:用户数据报协议(UDP,User Datagram Protocol)是无连接、不可靠的数据报协议
  • 2.UDP特点:
  • 3.UDP报文段结构
  • UDP编程
  • 模拟实现


TCP

面向连接的TCP

1.TCP概念

  • 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议

2.TCP特点

  1. 面向连接的传输层协议,使用前必须先建立TCP连接,传输后释放TCP连接
  2. 通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点
  3. 可靠通信方式,通过TCP连接传送的数据,无差错、不丢失、不重复,并且按序到达
  4. 面向字节流(InputStream/OutputStream)
  5. 提供全双工通信,允许通信双方任何时候都能发送数据,两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据

3.TCP报文段格式

Tcp服务端 java 中文乱码 java tcpdump_网络通信


3.TCP连接管理

  1. 三次握手建立连接
  2. Tcp服务端 java 中文乱码 java tcpdump_Tcp服务端 java 中文乱码_02

  3. (1)第一次握手:客户端向服务器端发送TCP连接请求数据包,其中包含客户端的初始序列号seq(A)=x。其中报文中同步标志位SYN=1,ACK=0,表示这是一个TCP连接请求数据包文;序号seq=x,表明传输数据时的第一个数据字节的序号是x
    (2)第二次握手:服务器收到请求后,会发送连接确认数据包。其中确认报文段,标识位SYN=1,ACK=1,表示这是一个TCP连接响应数据报文,并含服务器的初始序列号seq(B)=y,以及服务器对客户端初始序列号的确认号ack(B) = seq(A) + 1 = x + 1
    (3)第三次握手:客户端收到服务器的确认报文后,还需要做出确认,即发送一个序列号seq(A) = x + 1;确认号为ack(A) = y + 1 的报文
  4. 四次挥手关闭连接
  5. Tcp服务端 java 中文乱码 java tcpdump_Tcp服务端 java 中文乱码_03

  6. (1)第一次挥手:关闭客户端到服务器的连接,首先客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认。其中,标志位FIN=1,序列号seq=u
    (2)第二次挥手:服务器收到FIN,它发给客户端一个ACK,确认号ack = u + 1
    (3)第三次挥手:关闭服务器到客户端的连接,发送一个FIN给客户端,序列号seq=w,确认号ack=u+1
    (4)第四次挥手:客户端收到FIN后,并发回一个ACK报文确认,序列号seq=u+1,确认号ack=w+1
    TCP拆除连接为什么要等待2MSL呢?
    主要目的是防止最后一个ACK包对方没有收到,那么对方在超时后将重发第三次挥手的FIN包,主动关闭端接收到重发的FIN包后可以再发一个ACK包,
  • MSL(Maximum Segment Lifetime):最大报文生存时间,它是任何报文在网络上存在的最长时间,超过这个时间将被丢弃

TCP编程

Tcp服务端 java 中文乱码 java tcpdump_Tcp服务端 java 中文乱码_04

模拟实现

  • 多个客户端和服务器端(多线程)通信
  1. 客户端
public class MyTcpClient {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader input;
        PrintWriter output = null;
        BufferedReader reader = null;
        try {
            //构造Socket
            socket = new Socket("127.0.0.1", 3333);
            System.out.println("客户端已经启动...");
            while(true){
                //发送数据到服务器端
                //获取控制台的输入流
                input = new BufferedReader(new InputStreamReader(System.in));
                String info = input.readLine();
                //获取连接对象的输出流
                output = new PrintWriter(socket.getOutputStream());
                System.out.println("debug-info: "+info);
                //输出流对象写入info数据
                output.println(info); //tcp存在缓冲区,有可能缓冲区没有刷新就导致数据无法传输
                output.flush();
                //输出服务器端返回的数据
                //获取连接对象的输入流
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String strs = reader.readLine();
                System.out.println("客户端显示-->服务器端响应的数据:"+strs);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
                reader.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 服务器端
public class MyTcpServer {
    //通信线程
    static class SocketHandler extends Thread{
        protected Socket socket;
        public SocketHandler(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            BufferedReader reader = null;
            OutputStream outputStream = null;
            BufferedReader input;
            //通信
            try {
                while (true) {
                    //获取客户端的输入信息
                    reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    String strs = reader.readLine();
                    System.out.println("服务端显示-->接收客户端" + socket.getRemoteSocketAddress() + "的数据:" + strs);
                    //给客户端发送消息
                    //获取控制台的输入流
                    input = new BufferedReader(new InputStreamReader(System.in));
                    String info = input.readLine();
                    //获取连接对象的输出流
                    outputStream = socket.getOutputStream();
                    outputStream.write(("response: " + info + "\n").getBytes());
                    //结束通信
                    if ("".equals(strs) || "exit".equals(strs)) {
                        System.out.println("通信结束");
                        break;
                    }
                }
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                try {
                    reader.close();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }

    }

    //主线程
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            //构造ServerSocket对象  监听port:3333
            serverSocket = new ServerSocket(3333);
            System.out.println("服务器端已经启动...");
            while(true){
                //进行监听,等待多个客户端连接
                Socket accept = serverSocket.accept();
                System.out.println("与客户端:"+accept.getRemoteSocketAddress()+ "连接建立成功...");
                //将accpet实例交给子线程处理客户端请求
                new SocketHandler(accept).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

UDP

无连接的UDP

1.UDP概念:用户数据报协议(UDP,User Datagram Protocol)是无连接、不可靠的数据报协议

2.UDP特点:

  1. UDP无连接,减少了开销和发送数据时的时延
  2. UDP尽最大努力交付,不保证可靠交付,不需要维持复杂的连接状态表
  3. UDP面向报文,对应用程序交付下来的报文,再添加首部后就向下交付网络层
  4. UDP没有拥塞控制,网络拥塞不会使源主机的发送速率降低,这对实时应用很重要
  5. UDP支持一对一、一对多、多对一和多对多的交互通信
  6. UDP的首部开销小,只有8个字节

3.UDP报文段结构

Tcp服务端 java 中文乱码 java tcpdump_网络协议_05

UDP编程

通信过程和网上购物相似

Tcp服务端 java 中文乱码 java tcpdump_java_06

Tcp服务端 java 中文乱码 java tcpdump_网络协议_07

模拟实现

  • 客户端向服务器端发送消息
  1. 客户端
public class MyUDPClient {
    public static void main(String[] args) {
        //从控制台获取数据
        String info = (new Scanner(System.in)).next();
        try {
            //构造发送数据的对象
            DatagramSocket socket = new DatagramSocket();
            //将数据打包
            DatagramPacket datagramPacket = new DatagramPacket(info.getBytes(),info.length()
                    , InetAddress.getByName("localhost"),6666);
            //发送
            socket.send(datagramPacket);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 服务器端
public class MyUDPServer {
    public static void main(String[] args) {
        try {
            //构造接收数据的对象
            DatagramSocket socket = new DatagramSocket(6666);
            //接收数据空间
            byte[] buf = new byte[1024];
            //将接收数据空间打包
            DatagramPacket dp = new DatagramPacket(buf, 1024);
            //数据接收到打包的数据空间中
            socket.receive(dp);
            socket.send(dp);
            //拆包
            String info = new String(dp.getData(), 0, dp.getLength());
            System.out.println("接收到数据为: "+info);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}