Bootstrap

Javaee的网络编程初识

网络初识:

局域网 :把若干个电脑连接到一起,通过路由器,进行组网

广域网 :把很多很多局域网进一步的相连,构成更复杂的网络体系。

IP地址:区分主机

端口号:区分主机上不同的程序

协议:

是一种约定,通信双方,对于通信规则的约定,一定是通信双方都得到认可的。

标准往往是一个认可面非常广泛的,“国家标准”“国际标准”

协议。则不一定

可以是认可面非常广,也可以是认可面不广的。

进行网络通信的时候,一定是需要通信协议的,主要是,两个用来通信的主机设备,不同的硬件,不同的操作系统,不同的应用程序。

进行网络通信的时候,通信协议是非常关键的环节。

协议分层

网络通信,是一个非常复杂的事情,这个过程中涉及到很多的细节问题。

如果使用一个协议来约定上述所有的细节,这个协议就会非常庞大,非常复杂。

然后拆分,但是拆分的协议太多了,就要对这些协议进行分类,甚至,要进行“分层”

协议分层,也就是上述类似的效果,把很多协议,按照功能分成不同的层级,每个层级都有对应的主线任务(目标/要解决的问题)

上层协议会调用下层协议的功能,下层协议会给上层协议提供服务。

协议分层的好处

好处1:封装的效果,某一层协议,不必知道其他层协议的细节,降低学习使用成本。

只要会说汉语,不需要理解电话的工作原理,就能打电话,设计电话的人,可能是外国人,不必懂汉语,也不妨碍他设计开发电话。

好处2:任意层次的协议们都是可以灵活替换的(解耦合)

当前网络的现状,就是有很多的协议,这些协议就是按照一定的分层规则,组织起来的。

OSI七层模型(不是ISO){只存在于教科书上,实际上客观世界不存在}

TCP/IP五层(或四层)模型

可以认为是OSI的简化版本,就是真是世界采取的网络分层模型,现在接触的网络大部分都是TCP/IP模型(电脑上网)

关于五层协议:

1)物理层{硬件层面上的相关约定}

2)数据链路层{关注的是通信过程中,两个相邻节点之间的通信}

3)网络层{关注的是通信中,通信路径的规划,规划出的路径,就决定了,数据要经过哪些节点“点到点的传输”}

4)传输层{关注的是通信双方的“起点”和“终点”,“端到端的传输”}

5)应用层{和具体应用程序直接相关}

协议的层和层之间,是如何配合工作的?

协议的层和层之间,上层协议调用下层协议,下层协议给上层提供服务。

比如:

1.应用层

A通过qq给B发送hello

A给B发送hello.

四个信息,为了组织成字符串,就通过分割各个部分,使用\n结束标志。

qq号/接收人qq号/时间/消息

2.传输层

应用层有了,调用系统api来进行传输。

应用层接下来把数据交给传输层,怎么交给传输层?传输层(操作系统内核)提供了api,让应用程序去调用。

调用这样的api就会把刚才的应用层数据交给输入层

传输层拿到应用层数据包之后,就会把这个数据包进一步的封装,构造成传输层数据包,在传输层,典型协议有两个,TCP/UDP,此处假设使用UDP来作为传输层协议。

报头里放的是一些,udp相关的属性(比如发件人的收件人的端口号,就在UDP中)

传输层构造好数据包之后,就会继续把数据包交给网络层

3.网络层   典型的协议IP协议

 ip报头就包含收件人的ip地址和发件人的ip地址。

又进一步的调用数据链路层api,把上述ip数据包,交给数据链路层的协议。

4.数据链路层 典型协议 以太网

此处传输网络数据,也需要介质,就把以太这个词拿过来。

上述数据已经进入到网卡驱动中了。

接下来就要真正发送出去了。

5.物理层

上述的以太网数据帧,本质上还是0101二进制数据。

硬件设备,要把上述二进制数据,转成光信号/电信号/电磁波才会真正进行反射。

上述层层包装数据,不停的加数据报头的过程,称为“封装” 

B这边进行转换

 网络编程套接字

socket=>操作系统提供的网络编程的api就称为“socket api”

TCP和UDP都是传输层协议,都是给应用层提供服务的

但是由于这两个协议,特点,差异非常大,因此我们就需要搞两套api,来分别表示

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

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

有连接就好比,打电话,打通了才能说话。无连接就好比,发短信,直接上来就发。

可靠传输vs不可靠传输

可靠是尽可能做到数据到达对方,无法100%

不可靠传输则是传输数据的时候,压根不关心,对方是否收到了,发了就完了。

面向字节流  vs  面向数据报

文件操作就是字节流的,比喻成水流一样,读写操作非常灵活,

面向数据报,就不是了,传输数据的基本单位,是一个个的udp数据报,一次读写,只能读写一个完整的udp数据报

全双工vs半双工

全双工:一条链路,能够进行双向通信(tcp,udp都是全双工)

半双工:一条链路,只能进行单向通信。

UDP的socket api重点是两个类

1.DatagramSocket

系统中,本身就有socket这样的概念,DatagramSocket就是对于操作系统的socket概念的封装。

DatagramSocket就可以视为是“操作网卡”的遥控器,针对这个对象进行读写操作,就是针对网卡进行读写操作。

socket也是一种文件,需要close

2.DatagramPacket

针对UDP数据报的一个抽象表示。

一个DatagramPacket对象,就相当于一个UDP数据报,一次发送/一次接受,就是传输了一个DatagramPacket对象。

网络程序,就会有客户端也有服务器。

Echo称为“回显”,他的意思就是请求发了个啥,响应就是啥,这个过程中,没有计算,也没有业务逻辑,最简单的客户端服务器,只是单纯的去认识socket api的用法。

客户端,都是需要通过额外的途径知道服务器ip和端口是啥的。

因此,服务器额ip和端口得是固定的,不能老变。

构造socket对象的时候,没有指定端口号,没指定不代表没有而是操作系统,自动分配了一个空闲的(不和别人冲突)的端口号过来了.这个自动分配的端口号,每次重新启动程序都可能不一样。

为什么服务器要有固定的端口号:

1)服务器要有固定的端口号,是因为,客户端需要主动给服务器发请求。

如果服务器端口号不是固定的(假设是每次都变,此时客户端就不知道请求发给谁了)

2)客户端为啥要系统自动分配,指定固定的端口号行不行?

不行,可能会和客户端所在电脑上的其他程序冲突,一旦端口冲突,程序就启动不了了。

服务器代码:

package netWork;

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

public class UdpEchoServer {
    private DatagramSocket socker=null;
    public UdpEchoServer(int port) throws SocketException {
        socker=new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //(1)读取请求并解析
            DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
            socker.receive(requestPacket);
            //为了方便在Java代码中处理(尤其是后面进行打印)可以把上述数据报中的二进制数据,拿出来,构成String
            String resquest=new String(requestPacket.getData(),0,requestPacket.getLength());
            //(2)根据请求计算响应
            String response=this.process(resquest);
            //(3)把响应写回客户端
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestPacket.getSocketAddress());
            socker.send(responsePacket);
            System.out.printf("[%s:%d] req=%s,resp=%s\n",requestPacket.getAddress(),requestPacket.getPort(),resquest,response);
        }
    }
//由于当前写的是“回显服务器”
    public String process(String resquest) {
        return resquest;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server=new UdpEchoServer(9090);
        server.start();
    }
}

客户端代码:

 

package netWork;

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

public class UdpEchoClient {

    private DatagramSocket socket=null;
    private String serverIp;
    private int serverPort;
    public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
        socket=new DatagramSocket();
        this.serverIp=serverIp;
        this.serverPort=serverPort;
    }
    public void start() throws IOException {

        Scanner scanner=new Scanner(System.in);
        //1.从控制台读取用户输入
        String request=scanner.next();
        //2.构造请求并发送
        //构造请求数据报的时候,不光要有数据,还要有“目标”
        DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),0,request.getBytes().length, InetAddress.getByName(serverIp),serverPort);
        socket.send(requestPacket);
        //3.读取响应数据
        DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
        socket.receive(responsePacket);
        //4.显示响应到控制台上。
        String response=new String(responsePacket.getData(),0,responsePacket.getLength());
        System.out.println(response);
    }
}

;