zoukankan      html  css  js  c++  java
  • 网络编程的基础[三次握手,四次挥手]

    最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。

    1.TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;

    2,TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。

    3.TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。

    4.TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。

    5.当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。

     

    为什么TCP客户端最后还要发送一次确认呢?
    
    一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
    
    如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务
    
    器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。如果采用的是三次握
    
    手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

    数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。

    1.客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

    2.服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

    3.客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

    4.服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

    5.客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗ *∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

    6.服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些

    InetAddress
    package com.demo.inet;
    
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Arrays;
    
    public class InetAddressDemo {
        public static void main(String[] args) {
            // 创建一个字节数组byte 127 ~ -128
            byte bt[] = {-64,-88,3,44};
            
            try {
                // 创建InetAddress对象
                InetAddress id = InetAddress.getByAddress(bt);
                System.out.println(id);
                // 返回原始IP地址
                byte[] bs = id.getAddress();
                System.out.println(Arrays.toString(bs));
                // 返回对象对应IP地址
                System.out.println(id.getHostAddress());
                // 返回全名计算名称
                System.out.println(id.getCanonicalHostName()+"------------------");
                
                // 返回本机计算机名及对应ip地址
                System.out.println(InetAddress.getLocalHost());
                
                // 返回host名称对应域名及ip地址
                InetAddress[] allByName = InetAddress.getAllByName("www.baidu.com");
                System.out.println(Arrays.toString(allByName));
                System.out.println(allByName[0].getHostName()); // 域名
                System.out.println(allByName[0].getHostAddress()); // ip地址
                
                // 同主机名称获取IP信息
                InetAddress addr = InetAddress.getByName("localhost");
                System.out.println(addr.getHostAddress());
                System.out.println(addr.getCanonicalHostName()); // 返回ip地址
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
    }

    server端
    package com.demo.server;
    
    import java.io.BufferedOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
    
        static int port = 12345;
    
        public static void main(String[] args) {
            // 创建服务端Socket对象
            ServerSocket serverSocket = null;
            while (true) {
                try {
                    // 创建服务端Socket对象
                    serverSocket = new ServerSocket(port);
                    System.out.println("等待客户端连接服务器.......");
                    // 阻塞服务器
                    Socket socket = serverSocket.accept();
                    System.out.println("客户端连接到服务器.......");
    
                    // 创建文件输出流
                    BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream());
                    os.write("服务器端: 我收到了客户端连接请求。".getBytes());
                    os.flush();
                    os.close();
    
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (serverSocket != null) {
                            serverSocket.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    client端
    package com.demo.client;
    
    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.net.Socket;
    
    public class Client {
        static int port = 12345;
        static String host = "111.231.93.134";
    
        public static void main(String[] args) {
            // 创建客户端对象
            Socket socket = null;
            try {
                // 创建客户端对象
                socket = new Socket(host, port);
                
                // 获取输入流对象
                BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
                // 定义缓冲字节数组
                byte[] buffer = new byte[1024];
                // 定义接收实际长度
                int len = 0;
                while ((len = bis.read(buffer)) != -1) {
                    String content = new String(buffer, 0, len);
                    System.out.println("客户端接收的内容:"+content);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 相关阅读:
    第三章 C++中的C 《C++编程思想》
    网络时间协议 网络对时程序
    不相交集类应用:迷宫生成
    第一章 对象导言 第二章 对象的创建与使用
    获取本机IP MAC地址
    详解C#制做Active控件的五个步骤
    js HTML编码转换
    Web应用Word编辑
    HTML <map>标签的使用
    Web(浏览器)打开运行WinForm应用程序
  • 原文地址:https://www.cnblogs.com/sunBinary/p/10603385.html
Copyright © 2011-2022 走看看