本文作者:乐字节-坑王老薛

这个问题,对于咱们学习技术的开发者来说,有很多技术,就目前来说可能感觉都很简单且功能强大,然而其实所有的这些技术都是由前人从最根底的性能缓缓致力与日俱增才成就的,咱们是站在了很多伟人的肩膀上前行,明天咱们就来看看服务器是怎么缓缓的倒退过去的吧。

通信的形式

  • 简略通信
  • 不同申请
  • 简单申请

案例实操

简略通信

回顾 Socket 编程给咱们最大的感触,是能够在多台电脑之间进行数据的传输,这就是网络编程的开始和根底,通过理解客户端和服务端之间的通信来更直观地理解 Web 的倒退历程。

Client

/** * 客户端:向服务器发送申请,并发送简略的音讯 * @author 坑王老薛 * */public class Client {        public static void main(String[] args) throws UnknownHostException, IOException {        // 创立客户端 必须指定服务器+端口        Socket client = new Socket("localhost", 8888);        // 发送音讯 申请资源        // 获取输入流        OutputStream os = client.getOutputStream();        // 应用缓冲字符输入流        BufferedWriter br = new BufferedWriter(new OutputStreamWriter(os));        // 写出音讯,发送内容        String msg = "Hello, I am Client, I need some resources";        br.write(msg);        br.close();    }    }

Server

/** * 服务端,接管客户端申请并给出简略的响应 * @author 坑王老薛 * */public class Server {        public static void main(String[] args) throws IOException{        // 创立服务器,指定端口ServerSocket(int port)        ServerSocket socket = new ServerSocket(8888);        // 接管客户端连贯        Socket client = socket.accept();        System.out.println("******************");        // 获取数据的输出流        InputStream is = client.getInputStream();        // 应用缓冲字符输出流        BufferedReader br = new BufferedReader(new InputStreamReader(is));        String msg = "";        while ((msg = br.readLine()) != null) {            System.out.println(msg);        }        br.close();    }    }

服务端控制台:

从下面的例子总结通信条件如下:

  1. 须要有服务器端(server):期待被申请,须要裸露 ip 和 port
  2. 须要有客户端(client):被动发动申请,通晓服务端的 ip 和 port
  3. 通信规定(协定):TCP/IP 协定

ip 用于定位计算机;端口号(定位程序),用于标识过程的逻辑地址,不同过程的标记;无效端口:0~65535,其中 0~1023 由零碎应用或者保留端口,开发中倡议应用 1024 以上的端口。

不同申请

Client

/** * 客户端:向服务器发送申请,发送不同的申请 * @author 坑王老薛 * */public class Client {        public static void main(String[] args) throws IOException {        // 通过零碎默认类型的 SocketImpl 创立未连贯套接字        Socket socket = new Socket();        // 此类实现 IP 套接字地址(IP 地址 + 端口号)。它还能够是一个对(主机名 + 端口号),在此状况下,将尝试解析主机名        SocketAddress address = new InetSocketAddress("localhost", 8898);        // 将此套接字连贯到服务器,并指定一个超时值。 或者不指定超时工夫        socket.connect(address, 1000);        OutputStream os = socket.getOutputStream();        os.write("time".getBytes());        os.flush();        socket.close();    }    }

Server

/** * 服务端  * public class ServerSocketextends Object:此类实现服务器套接字。 * 服务器套接字期待申请通过网络传入。 * 它基于该申请执行某些操作,而后可能向请求者返回后果。 *  * @author 坑王老薛 * */public class Server {    public static void main(String[] args) throws IOException {        // 创立绑定到特定端口的服务器套接字。        ServerSocket server = new ServerSocket(8898);        // Socket accept() 侦听并承受到此套接字的连贯。        Socket client = server.accept();        System.out.println("接管到连贯");        InputStream is = client.getInputStream();        BufferedInputStream bis = new BufferedInputStream(is);        byte[] req = new byte[1024];        // 接管客户端申请        int len = bis.read(req);        String reqStr = new String(req, 0, len);        System.out.println(reqStr);        if (reqStr.equals("money")) {            System.out.println("here's the money");        } else if (reqStr.equals("time")) {            System.out.println("you have so much time");        }        client.close();        server.close();    }}

服务端控制台:

简单申请

Client

/** * 客户端 *  * @author 坑王老薛 * */public class Client {        public static void main(String[] args) throws IOException {        // 通过零碎默认类型的 SocketImpl 创立未连贯套接字        Socket socket = new Socket();        // 此类实现 IP 套接字地址(IP 地址 + 端口号)。它还能够是一个对(主机名 + 端口号),在此状况下,将尝试解析主机名        SocketAddress address = new InetSocketAddress("localhost", 8898);        // 将此套接字连贯到服务器,并指定一个超时值。 或者不指定超时工夫        socket.connect(address, 1000);        OutputStream os = socket.getOutputStream();        os.write("money".getBytes());        os.flush();        // 接管响应, 显示后果        InputStream is = socket.getInputStream();        byte[] result = new byte[1024];        int len = is.read(result);        String resultStr = new String(result, 0, len);        System.out.println(resultStr);        socket.close();    }    }

Server

/** * 服务端 * @author 坑王老薛 * */public class Server2 {        public static void main(String[] args) throws IOException {        // 创立绑定到特定端口的服务器套接字。        ServerSocket server = new ServerSocket(8898);        // Socket accept() 侦听并承受到此套接字的连贯。        Socket client = server.accept();        System.out.println("接管到连贯");        InputStream is = client.getInputStream();        BufferedInputStream bis = new BufferedInputStream(is);        byte[] req = new byte[1024];        // 接管客户端申请        int len = bis.read(req);        String reqStr = new String(req, 0, len);        System.out.println(reqStr);        // 将接管到的申请封装成对象, 传送给申请的类        MyRequest request = new MyRequest();        MyResponse response = new MyResponse();        OutputStream os = client.getOutputStream();        if (reqStr.equals("money")) {            // 依据申请的信息, 结构解决的类            MyServlet s1 = new ServletMoney();            s1.service(request, response);            // 通过client的响应, 将后果响应回客户端            os.write("here's the money".getBytes());            os.flush();        } else if (reqStr.equals("time")) {            // 依据申请的信息, 结构解决的类            MyServlet s2 = new ServletTime();            s2.service(request, response);            // 通过client的响应, 将后果响应回客户端            os.write("you have somuch time".getBytes());            os.flush();        }        client.close();        server.close();    }    }/* * 我是一个有要求的人,你申请的这个资源必须是满足我要求格局的类,作用:避免凌乱,不便调用 这是我的规范 */interface MyServlet {    void service(MyRequest req, MyResponse resp);}class ServletMoney implements MyServlet {    /*     * @see com.mage.server.MyServlet#service(com.mage.server.MyRequest, com.mage.server.MyResponse)     */    @Override    public void service(MyRequest req, MyResponse resp) {        // 做出力不从心的解决    }}class ServletTime implements MyServlet {    /*     * @see com.mage.server.MyServlet#service(com.mage.server.MyRequest, com.mage.server.MyResponse)     */    @Override    public void service(MyRequest req, MyResponse resp) {        // 做出力不从心的解决    }}/* * 申请信息都按法则封装在该对象 */class MyRequest {}class MyResponse {}

服务端控制台: 客户端控制台:

随着客户需要越来越简单,须要的性能越来越多,咱们的服务器端须要解决的申请越来越多,须要辨别不同的申请,还须要依照不同申请进行申请数据的提取以及资源的调配和运算还有逻辑的解决,最初还须要响应给客户端,这就使得服务器端代码越来越简单,实现越来越艰难

依据以往的教训,单方进行通信只须要遵循肯定的规定就能够很明确地晓得各局部数据的含意,于是呈现了网络更下层的利用协定(HTTP 协定),规定服务器端和客户端通信的规定。

客户端申请服务器端和服务器端响应客户端,都依照固定的规定,那么接管申请和响应数据这部分操作就能够固定下来,交给特定的一段代码来执行,从而缩小服务器端的代码量,于是缓缓演变出了咱们当初应用的服务器

扩大~TCP/IP协定簇

概念

TCP/IP 协定簇(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指可能在多个不同网络间实现信息传输的协定簇。

TCP/IP 协定不仅仅指的是 TCP 和 IP 两个协定,而是指一个由FTP、SMTP、TCP、UDP、IP等协定形成的协定簇,只是因为在 TCP/IP 协定中 TCP 协定和 IP 协定最具代表性,所以被称为TCP/IP协定。

TCP/IP传输协定是严格来说是一个四层的体系结构,应用层、传输层、网络层和数据链路层都蕴含其中。

应用层

应用层是间接为利用过程提供服务的,对于不同品种的应用程序会依据本人的须要来应用应用层的不同协定。比方邮件传输利用应用了 SMTP 协定、万维网利用应用了 HTTP 协定、文件传输利用应用了 FTP 协定、近程登录服务利用应用了有 TELNET 协定等等。

传输层

传输层对下层应用层,提供处于网络连接中的两台计算机之间的数据传输。

在传输层有两个性质不同的协定:

  • TCP(Transmission ControlProtocol,传输控制协议)
  • UDP(User Data Protocol,用户数据报协定)。

网络层

网络层用来解决在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎么的门路(所谓的传输路线)达到对方计算机,并把数据包传送给对方。网络层能够进行网络连接的建设和终止以及IP地址的寻找等性能。

链路层

链路层用来解决连贯网络的硬件局部。包含管制操作系统、硬件的设施驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见局部(还包含连接器等所有传输媒介)。硬件上的领域均在链路层的作用范畴之内。

传输示意图

依据以上概念以及 TCP/IP 四个层的介绍,咱们能够来看看网络传输中 TCP/IP 协定到底是怎么一步一步地将数据进行传输的。