JavaEE-网络编程-客户端服务器

协议是非常重要的芝士

协议分层,->解耦

TCP/IP五层协议

  1. 物理层 基础设施
  2. 数据链路层 两个相邻节点之间的数据传输
  3. 网络层 两个节点之间的路径规划
  4. 传输层 通信中的起点和重点
  5. 应用层 传过去的数据咋用

下层协议给上层提供服务,上层协议调用下层协议

封装和分用->发送和接受数据


网络编程(突破一台主机的限制)

一些网络编程中的基础概念

  1. 网络编程:两个/多个 进程,通过网络,来进行相互通信(写代码来实现)

进程具有隔离性(每个进程有自己独立的虚拟地址空间)

进程间通信->借助一个每个进程都能够访问到的公共区域,完成数据交换

网络编程也就是一种进程间通信的方式.

借助的公共区域就是网卡.是当下最主流的方式

既能够让同一个主机的多个进程间通信

也可以让不同主机的多个进程间通信

高并发,分布式,大数据

  1. 客户端(client)/服务器(server)

客户端:主动发送网络数据的一方

服务器:被动接受网络数据的一方

因为服务器无法知道客户端啥时候发来数据

因此就只能长时间运行,甚至7*24小时运行!

  1. 请求(request)/响应(response)

请求:客户端给服务器发送的数据

响应:服务器给客户端返回的数据.

  1. 客户端和服务器之间的交互方式

1)一问一答(最常见的方式)

客户端给服务器发送个请求

服务器给客户端返回个请求

2)多问一答(更少见一些,比如上传文件)

客户端发送多个请求

服务器返回一个响应

3)一问多答(出现的还行,比如播放视频,你点击一下,会返回弹幕,视频内容)

客户端发送一个请求

服务器返回多个相应

4)多问多答(远程控制,游戏串流)

客户端发送多个请求

服务器返回多个响应


进行网络编程,需要使用操作系统提供的网络编程API

JavaEE-网络编程-客户端服务器_第1张图片

 

传输层提供了两个非常重要的协议,截然不同

TCP

UDP

这两个协议对应的socket API也是截然不同的

如果谈到操作系统,最爱靠的就是多线程

那么谈到网络,最常考的就是TCP

简单概括

TCP:有连接,可靠传输,面向字节流,全双工

UDP:无连接,不可靠传输,面向数据包,全双工

有连接:打电话.先建立连接,然后再通信

无连接:发微信.不必建立连接,直接通信即可

(网络通信是无法保证百分之百到达的)

可靠传输:数据对方收没收到,发送方能够有感知

不可靠传输:数据对方收没收到也不管, 不知道~

比如打电话,就是可靠传输

比如发微信,也就是不可靠传输

面向字节流:这里的字节流和文件那里的字节流是一样的(不光概念上是一样的,连代码编写都是一样的)

面向数据报:以数据报为传输的基本单位

全双工:双向通信,一个管道,能A->B,B->A 同时进行

半双工:单向通信,一个管道,同一时刻,要么A->B,要么B->A 不能同时进行

为啥一个管道能够双向通信,你想想公路就知道了.

JavaEE-网络编程-客户端服务器_第2张图片

 

网线,标准的以太网线,里面其实是八根铜线

有没有做过水晶头

JavaEE-网络编程-客户端服务器_第3张图片

 

打开之后就可以传输数据了

send():发送数据

receive():接收数据

close():关闭文件

DatagramPacket :

表示一个UDP数据报

UDP是面向数据报的协议

传输数据,就是以DatagramPacket为基本单位

InetSocketAddress IP地址+端口号

写一个UDP版本的回显服务器-客户端(echo server)

客户端发啥,服务器就返回啥~~

JavaEE-网络编程-客户端服务器_第4张图片

绑定一个端口,把这个进程和一个端口号关联起来

一个操作系统上面,有很多端口号.0-65535

程序如果需要进行网络通信,就需要获取到一个端口号

端口号相当于用来再网络上区分进程的身份标识符

(操作系统收到网卡的数据,就可以根据网络数据报中的端口号,来确定要把这个数据交给哪个进程)

分配端口号的过程

  1. 程序员手动指定

  2. 系统自动分配 new DatagramSocket();()系统会自动分配一个空闲的端口

 

一个端口,在通常情况下,是不能被同一个主机上的多个进程同时绑定的

一个进程是可以绑定多个端口的.

如果端口已经被占用的话

读取客户端发来的请求,尝试读取,不是说调用了一定能读到

 

如果客户端没有发来请求,receive就会阻塞等待

知道真的有客户端请求过来了,receive才会返回

 

这个方法是通过参数来放置读取到的数据的,而不是通过返回值.

JavaEE-网络编程-客户端服务器_第5张图片

 

 

通过后面的new,就构造了一个空的DatagramPacket

输入输出的时候很容易发生异常.

 

一个服务器最容易坏的就是硬盘,特别是机械硬盘

 

这是最新的

String的构造方法,String(byte[], int)这个版本的构造方法,是被舍弃的

Java版本一直在更新,出新的东西的同时,也在舍弃旧的东西

旧的东西不能立即就删,得给程序员留下缓冲的时间.

先标记为被舍弃,也就是不建议程序员继续使用,可能会在未来的版本中删掉)

这个注解就是被舍弃的意思

 

JavaEE-网络编程-客户端服务器_第6张图片

 

这个也是一种构造DatagramPacket的方式

先是拿字符串俩面的字节数组中,来构造Packet的内容

还要把请求中的客户端的地址拿过来,也填写到包中.

JavaEE-网络编程-客户端服务器_第7张图片

 

把数据发出去

 

JavaEE-网络编程-客户端服务器_第8张图片

 

必须使用下面的哪个length,下面的表示字节数,而上面的表示字符数

//如果是服务器的端口号一般是指定的,客户端的端口号一般是自动分配的
//如果服务器自动分配,客户端就不知道服务器的端口是啥了.
//因此,服务器有固定的端口,客户端才方便访问
//客户端程序是安装在用户的电脑上的,用户电脑当前运行哪些程序是不可控的
//如果要是手动指定端口,说不好这个端口就和其他程序的端口冲突了
//就导致我们的代码无法运行了.

JavaEE-网络编程-客户端服务器_第9张图片

这是另外一种DatagramPacket的构造方式

getByName此处的127.0.0.1是环回IP.就表示当前主机.

端口号就填写服务器的端口号

因为这个包裹是要从客户端发送给服务器的

所以就要知道发送的内容以及发送给的目的地是在哪里(收件人地址+端口)

目前我们已经见到了三个版本的DatagramPacket的构造

  1. 只太填写缓冲区,用来接收数据的,是一个空的PACKET
  2. 填写缓冲区,并且填写把包发给谁,InetAddress对象来表示的
  3. 填写缓冲区,并且填写把包发给谁,InetAddress+port

先理清楚,客户端和服务器的工作流程

服务器:

  1. 读取请求并解析

JavaEE-网络编程-客户端服务器_第10张图片

  1. 根据请求计算响应

 

  1. 构造响应数据,并返回给客户端.

JavaEE-网络编程-客户端服务器_第11张图片

客户端的核心流程

  1. 根据用户输入构造请求

JavaEE-网络编程-客户端服务器_第12张图片

 

JavaEE-网络编程-客户端服务器_第13张图片

 

  1. 把请求发送给服务器

  1. 读取服务器返回的响应

JavaEE-网络编程-客户端服务器_第14张图片

  1. 解析响应,并显示给用户

JavaEE-网络编程-客户端服务器_第15张图片

JavaEE-网络编程-客户端服务器_第16张图片

 

客户端和服务器的核心流程是紧密相连的.

上述流程,不仅仅是回显服务器客户端如此,大部分的客户端服务器都是如此

这都是一台基本套路

我们学习网络编程

  1. 学习网络编程的基本套路(核心流程)
  2. 学习socket api的使用

一个服务器是可以同时给多个客户端提供服务的

JavaEE-网络编程-客户端服务器_第17张图片

如果我现在不是想写一个回显服务器了,而是一个带有业务逻辑的服务器,怎么实现呢?

什么叫业务逻辑呢?

我们当前的回显服务器,是没有业务逻辑的

请求和回想都一样

正经的服务器,应该是请求和响应都不一样的,这样才有意义

JavaEE-网络编程-客户端服务器_第18张图片

主要就是在这个方法里面进行修改

我们新写一个类,继承这个服务器

重写一下process

package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

/**
 * 字典服务器/翻译服务器
 * 希望实现一个英译汉的效果
 * 请求是一个英文单词,响应是对应的中文翻译
 */
public class UDPDictServer extends UDPEchoServer{
    private Map dict = new HashMap<>();

    public UDPDictServer(int port) throws SocketException {
        super(port);

        /**
         * 这里可以无限地构造下去
         * 即使是有道词典这种,也是按照类似地方法实现(打表)
         */
        dict.put("cat","小猫");
        dict.put("dog","小狗");
    }

    @Override
    public String process(String req) {
        return dict.getOrDefault(req,"这个词俺也不会");
    }

    public static void main(String[] args) throws IOException {
        UDPDictServer server = new UDPDictServer(8000);//8000这个端口相当于被我们占用了
        server.start();//启动服务器
    }
}

JavaEE-网络编程-客户端服务器_第19张图片

 

一个服务器地灵魂所在

一个服务器要完成地工作,都是通过"根据请求计算响应"来体现地

不管是啥样地服务器,读取请求并解析,构造响应并返回,这两个步骤,大同小异

以下是本篇博客涉及到的两个服务器和一个客户端代码

package network;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UDPEchoClient {
    private DatagramSocket socket = null;

    public UDPEchoClient()throws SocketException {
        //客户端的端口号,一般都是由操作系统自动分配的,虽然手动指定也行,但还是自动分配比较好
        //如果是服务器的端口号一般是指定的,客户端的端口号一般是自动分配的
        //如果服务器自动分配,客户端就不知道服务器的端口是啥了.
        //因此,服务器有固定的端口,客户端才方便访问
        //客户端程序是安装在用户的电脑上的,用户电脑当前运行哪些程序是不可控的
        //如果要是手动指定端口,说不好这个端口就和其他程序的端口冲突了
        //就导致我们的代码无法运行了.
        socket = new DatagramSocket();
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true){
            //1.让客户端从控制台读取一个请求数据
            System.out.print("> ");
            String request = scanner.next();
            //2.把这个字符串请求发送给服务器,构造DatagramPacket
            //  我们构造的Packet既要包含要传输的数据,又要包含把数据发到哪里
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length,
                    InetAddress.getByName("127.0.0.1"),8000);
            //3.把数据包发给服务器
            socket.send(requestPacket);
            //4.我们要从服务器中读取响应数据
            //  因为服务器收到数据后会发出响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            //5.我们再把响应的数据获取出来,转成字符串
            String response = new String(responsePacket
                    .getData(),0,responsePacket.getLength());

            System.out.printf("req: %s;resp: %s\n",request,response);
        }
    }

    public static void main(String[] args) throws IOException {
        UDPEchoClient client = new UDPEchoClient();
        client.start();
    }
}
package network;

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

public class UDPEchoServer {
    //要想创建UDP服务器,首先要先打开一个socket文件
    private DatagramSocket socket = null;

    public UDPEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    //启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true){
            //1.读取客户端发来的请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //2.对请求进行解析,把DatagramPacket转成一个String
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //3.根据请求,处理响应.虽然咱们这个是个回显服务器,但是还是可以单独搞个方法来做这个事情
            String response = process(request);
            //4.把响应构造成DatagramPacket对象
            //  构造响应对象,要搞清楚,对象要发给谁,谁给咱们发的请求,我们就把响应发给谁.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,
                    requestPacket.getSocketAddress());
            //5.把这个DatagramPacket对象返回给客户端
            socket.send(responsePacket);
            System.out.printf("[%s:%d] req=%s;resp=%s\n",responsePacket.getAddress().toString(),responsePacket.getPort(),
                    request,response);//IP和端口,请求和响应
        }
    }
    //根据这个方法,实现根据请求计算响应 这个过程
    //由于是回显服务器,所以不涉及到其他裸机
    //但是如果是其他服务器,就可以在process里面,来加上一些其他逻辑的处理
    public String process(String req){
        return req;
    }

    public static void main(String[] args) throws IOException {
        //真正启动服务器,这个端口号说是随便写,但是也是有范围的.0-65535
        //但是一般来说1024以下的端口,都是系统保留
        //因此咱们自己写代码,端口尽量还是选择1024以上,65535以下的.
        UDPEchoServer server = new UDPEchoServer(8000);//8000这个端口相当于被我们占用了
        server.start();//启动服务器
    }
}

package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

/**
 * 字典服务器/翻译服务器
 * 希望实现一个英译汉的效果
 * 请求是一个英文单词,响应是对应的中文翻译
 */
public class UDPDictServer extends UDPEchoServer{
    private Map dict = new HashMap<>();

    public UDPDictServer(int port) throws SocketException {
        super(port);

        /**
         * 这里可以无限地构造下去
         * 即使是有道词典这种,也是按照类似地方法实现(打表)
         */
        dict.put("cat","小猫");
        dict.put("dog","小狗");
    }

    @Override
    public String process(String req) {
        return dict.getOrDefault(req,"这个词俺也不会");
    }

    public static void main(String[] args) throws IOException {
        UDPDictServer server = new UDPDictServer(8000);//8000这个端口相当于被我们占用了
        server.start();//启动服务器
    }
}

JavaEE-网络编程-客户端服务器_第20张图片

 

你可能感兴趣的:(JavaEE冲冲冲,网络,服务器,java,java-ee)